1
0
forked from HPR/hpr-tools

Moved project directories and files to an empty local repo

This commit is contained in:
Dave Morriss
2024-06-04 16:35:44 +01:00
parent 2d2b937a9b
commit 38abbcdd39
271 changed files with 55348 additions and 0 deletions

View File

@@ -0,0 +1,912 @@
#!/usr/bin/env perl
#===============================================================================
#
# FILE: add_hosts_to_show
#
# USAGE: ./add_hosts_to_show -show=N -host=H1 [-host=H2 ... -host=Hn]
#
# DESCRIPTION: Adds one or more hosts to an existing show in the 'HPR2'
# database.
#
# OPTIONS: ---
# REQUIREMENTS: ---
# BUGS: ---
# NOTES: ---
# AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
# VERSION: 0.0.5
# CREATED: 2017-10-28 18:56:21
# REVISION: 2019-07-08 22:53:35
#
#===============================================================================
use 5.010;
use strict;
use warnings;
use utf8;
use Getopt::Long;
use Pod::Usage;
use Config::General;
use List::Util qw( uniqstr );
use IO::Prompter;
use DBI;
use Data::Dumper;
#
# Version number (manually incremented)
#
our $VERSION = '0.0.5';
#
# 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_pg2.cfg";
my ( $dbh1, $sth1, $h1, $rv1 );
my ( $show_type, $show_number );
my %details;
my $pfmt = "%-16s: %s\n";
#
# Enable Unicode mode
#
binmode STDOUT, ":encoding(UTF-8)";
binmode STDERR, ":encoding(UTF-8)";
#
# Load database configuration data
#
my $conf = Config::General->new(
-ConfigFile => $configfile,
-InterPolateVars => 1,
-ExtendedAccess => 1
);
my %config = $conf->getall();
#-------------------------------------------------------------------------------
# Options and arguments
#-------------------------------------------------------------------------------
my $DEF_DEBUG = 0;
#
# Process options
#
my %options;
Options( \%options );
#
# Default help
#
pod2usage( -msg => "$PROG version $VERSION\n", -exitval => 1, -verbose => 0 )
if ( $options{'help'} );
#
# The -documentation or -man option shows the full POD documentation through
# a pager for convenience
#
pod2usage( -msg => "$PROG version $VERSION\n", -exitval => 1, -verbose => 2 )
if ( $options{'documentation'} );
#
# Collect options
#
my $DEBUG = ( defined( $options{debug} ) ? $options{debug} : $DEF_DEBUG );
my $verbose = ( defined( $options{verbose} ) ? $options{verbose} : 0 );
my $prompting = ( defined( $options{prompt} ) ? $options{prompt} : 1 );
my $show = $options{show};
my @hosts = ( defined( $options{host} ) ? @{ $options{host} } : () );
#-------------------------------------------------------------------------------
# Basic sanity checks
#-------------------------------------------------------------------------------
pod2usage( -msg => "$PROG version $VERSION\n", -exitval => 1, -verbose => 0 )
unless ( $show and @hosts );
#
# Check the show spec conforms
#
unless ( ( $show_type, $show_number ) = ( $show =~ /^(hpr|twat)?(\d+)$/ ) ) {
warn "Invalid show specification: $show\n\n";
pod2usage( -msg => "$PROG version $VERSION\n", -exitval => 1,
-verbose => 0 );
}
#
# Ensure the default show type is 'hpr' and the show number contains
# leading zeroes
#
$show_type = 'hpr' unless $show_type;
$show = sprintf( "%s%04d", $show_type, $show_number );
#
# Remove host duplicates and empty strings
#
@hosts = uniqstr @hosts;
@hosts = grep { !/^\s*$/ } @hosts;
unless ( scalar(@hosts) > 0 ) {
warn "No valid hosts provided\n\n";
pod2usage( -msg => "$PROG version $VERSION\n", -exitval => 1, -verbose => 0 );
}
#
# Debug the options and what we did with them
#
if ( $DEBUG > 2 ) {
print "D> Show type: $show_type\n";
print "D> Show number: $show_number\n";
print "D> Show key: $show\n";
print "D> Hosts: ", join( ", ", @hosts ), "\n";
}
#-------------------------------------------------------------------------------
# Connect to the PostgreSQL database
#-------------------------------------------------------------------------------
my $dbtype = $config{database}->{type} // 'Pg';
my $dbhost = $config{database}->{host} // '127.0.0.1';
my $dbport = $config{database}->{port} // 5432;
my $dbname = $config{database}->{name};
my $dbuser = $config{database}->{user};
my $dbpwd = $config{database}->{password};
$dbh1
= DBI->connect( "dbi:$dbtype:host=$dbhost;database=$dbname;port=$dbport",
$dbuser, $dbpwd, { PrintError => 0, AutoCommit => 1 } )
or die $DBI::errstr;
#
# Enable client-side UTF8
#
$dbh1->{pg_enable_utf8} = 1;
#-------------------------------------------------------------------------------
# Collect the details for the nominated show
#-------------------------------------------------------------------------------
#
# Initialise the hash where we'll keep show and host details
#
$details{$show} = {};
#
# Query to check the details of the nominated show. If the show has multiple
# hosts already we'll get back multiple rows.
#
$sth1 = $dbh1->prepare(
q{
SELECT
e.episode_id AS eid,
e.episode_key,
e.release_date,
e.title,
h.host_id AS hid,
h.host
FROM episodes e
JOIN episodes_hosts_xref eh ON (e.episode_id = eh.episode_id)
JOIN hosts h ON (h.host_id = eh.host_id)
WHERE e.episode_key = ?
}
) or die $DBI::errstr;
if ( $dbh1->err ) {
warn $dbh1->errstr;
}
#
# Run the query
#
$rv1 = $sth1->execute($show);
if ( $dbh1->err ) {
die $dbh1->errstr;
}
#
# Check the result of the query
#
$rv1 = 0 if ( $rv1 eq '0E0' );
die
"Problem finding show $show and associated hosts. Database inconsistency?\n"
unless $rv1;
#
# Accumulate details in a hash using 'save_details' to remove duplicates
#
while ( $h1 = $sth1->fetchrow_hashref ) {
save_details( $details{$show}, 'id', $h1->{eid} );
save_details( $details{$show}, 'release_date', $h1->{release_date} );
save_details( $details{$show}, 'title', $h1->{title} );
save_details( $details{$show}, 'hosts', {} );
save_details( $details{$show}->{hosts}, $h1->{host}, $h1->{hid} );
}
#
# Report what we collected
#
print_details( \%details, $show, $pfmt );
print '-' x 80, "\n";
#-------------------------------------------------------------------------------
# Find details for the host(s) we are to add
#-------------------------------------------------------------------------------
#
# Remove hosts who are already linked to the show. This relies on the host
# names being an exact match with the host details we got back for the show
# itself.
#
@hosts = clean_host_options( \%details, $show, \@hosts );
#
# Find details of hosts to be added and save them
#
save_details( $details{$show}, 'new_hosts', find_hosts( $dbh1, \@hosts ) );
#
# Remove hosts from the 'new_hosts' sub-hash if they match the hosts already
# linked to this show
#
clean_details( \%details, $show );
_debug($DEBUG > 2, Dumper(\%details));
#
# Abort if we didn't find all the hosts we were given
#
die "Cannot continue - some hosts cannot be added\n"
unless keys( %{ $details{$show}->{new_hosts} } );
#
# Display what we're going to do
#
print "Host(s) to be added\n";
for my $host ( keys( %{ $details{$show}->{new_hosts} } ) ) {
my $ptr = $details{$show}->{new_hosts};
printf $pfmt, 'Host', $host;
printf $pfmt, 'Id', $ptr->{$host}->{id};
printf $pfmt, 'Email', $ptr->{$host}->{email};
print '-' x 10, "\n";
}
#-------------------------------------------------------------------------------
# Confirm that we are to add the requested hosts(s) if 'prompting' is on, and
# do it!
#-------------------------------------------------------------------------------
if ($prompting) {
if (prompt(
-style => 'bold',
-prompt => 'Do you wish to continue? [Y/n] ',
-default => 'Y',
-yes
)
)
{
link_hosts( $dbh1, \%details, $show, $verbose );
}
else {
print "No hosts added\n" if ( $verbose > 0 );
}
}
else {
link_hosts( $dbh1, \%details, $show, $verbose );
}
exit;
#=== FUNCTION ================================================================
# NAME: clean_details
# PURPOSE: Check the hosts in the %details hash. If any of the hosts who
# are already linked to the show are in the list of new hosts to
# add, remove them.
# PARAMETERS: $details Hashref containing details
# $show Show key
# RETURNS: Nothing
# DESCRIPTION: Walks through the hosts that we were asked to add. They have
# been expanded by looking them up in the database and stored in
# $details->{$show}->{new_hosts}. If we find them in the list of
# hosts linked to this show we remove them from the new hosts
# list. This may leave no new hosts of course!
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub clean_details {
my ( $details, $show ) = @_;
#
# Walk through the hosts that we were asked to add. They have been
# expanded by looking them up in the database. If we find them in the list
# of hosts linked to this show we remove them from the new hosts list.
#
foreach my $new ( keys( %{ $details->{$show}->{new_hosts} } ) ) {
if ( exists( $details->{$show}->{hosts}->{$new} ) ) {
delete( $details->{$show}->{new_hosts}->{$new} );
}
}
}
#=== FUNCTION ================================================================
# NAME: link_hosts
# PURPOSE: Link the requested host(s) to the nominated show
# PARAMETERS: $dbh Database handle
# $details Hashref containing details
# $show Show key
# $verbose Verbosity level
# RETURNS: Nothing
# DESCRIPTION: Adds new hosts to the nominated show. These are in the
# sub-hash $details->{$show}->{new_hosts} as keys, so we simply
# need to loop through them. All hosts have their id numbers, as
# does the show itself so we have all that is needed to create
# the xref row.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub link_hosts {
my ( $dbh, $details, $show, $verbose ) = @_;
my ( $sth, $rv, $show_id );
$verbose = 0 unless $verbose;
#
# Query to make a link entry in the xref table
#
$sth = $dbh->prepare(
q{
INSERT INTO episodes_hosts_xref VALUES (?,?)
}
);
$show_id = $details->{$show}->{episode_id};
#
# Loop through all the hosts to be added and add them
#
foreach my $host ( keys( %{ $details->{$show}->{new_hosts} } ) ) {
print "Adding $host to $show\n" if ( $verbose > 0 );
$rv = $sth->execute( $show_id,
$details->{$show}->{new_hosts}->{$host}->{host_id} );
if ( $dbh->err ) {
die $dbh->errstr;
}
$rv = 0 if ( $rv eq '0E0' );
warn "Failed to link $host to $show\n"
unless ( defined($rv) );
}
}
#=== FUNCTION ================================================================
# NAME: find_hosts
# PURPOSE: Find the requested hosts by a variety of means
# PARAMETERS: $dbh Database handle
# $hosts Arrayref containing the hosts we want to find
# RETURNS: A hash containing the host names and details
# DESCRIPTION: Loops through the host names in @$hosts and looks for each one
# in the 'hosts' table. If found the the local hash %hosts_found
# is populated with the host details. A count of matches is
# maintained during this phase. If at the end of it the count is
# less than the number of elements of @$hosts then we need to
# try again. A local array @missing is then populated with the
# missing hosts and used in another loop using the names as regular
# expressions. The counter is incremented further in this loop,
# and if a host is found it is added to %hosts_found. If the
# count of hosts equals the number in @$hosts the function exits
# with a reference to %hosts_found, otherwise an empty hash is
# returned. Failure to find any host is intended to be used by
# the caller as a reason to abort the script (though maybe
# further searches could be added in the future).
# The regex query takes care that it also counts the number of
# matches, so that if a wildcard host name is used which
# matches multiple hosts we ignore the result. That way
# "-host='A*'" doesn't match all the hosts with an 'a' in their
# name. The regex matching is case insensitive.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub find_hosts {
my ( $dbh, $hosts ) = @_;
my ( $sth, $h, $hcount, @found, @missing, %hosts_found );
#
# Query to find host by name using an exact match
#
$sth = $dbh->prepare(
q{
SELECT * FROM hosts where host = ?
}
) or die $DBI::errstr;
if ( $dbh->err ) {
warn $dbh->errstr;
}
#
# Loop through the host names matching exactly
#
$hcount = 0;
for my $host (@$hosts) {
$sth->execute($host);
if ( $dbh->err ) {
die $dbh->errstr;
}
if ( $h = $sth->fetchrow_hashref ) {
$hcount++;
$hosts_found{$host} = {
id => $h->{host_id},
email => $h->{email},
};
}
}
#
# If we didn't find any hosts have another go
#
if ( $hcount < scalar(@$hosts) ) {
#
# Isolate the hosts we didn't find (find the items in @hosts that are
# not in @found)
#
@found = keys(%hosts_found);
@missing
= grep { my $item = $_; !grep( $_ eq $item, @found ) } @$hosts;
#print "D> ",join(", ",@missing),"\n";
#
# Query to find host by name using a regex match. This one also
# returns the count of hosts since if it's not 1 we don't want to
# continue because the regex is too vague. TODO: Does this make sense?
#
$sth = $dbh->prepare(
q{
SELECT
(SELECT
count(*) AS count
FROM hosts
WHERE host ~* ?),
host_id,
host,
email
FROM hosts
WHERE host ~* ?
}
) or die $DBI::errstr;
if ( $dbh->err ) {
warn $dbh->errstr;
}
#
# Try the regex search on all hosts we didn't find with an exact match
#
for my $host (@missing) {
$sth->execute( $host, $host );
if ( $dbh->err ) {
die $dbh->errstr;
}
if ( $h = $sth->fetchrow_hashref ) {
if ( $h->{count} == 1 ) {
$hcount++;
$hosts_found{ $h->{host} } = {
id => $h->{host_id},
email => $h->{email},
};
}
}
}
}
#
# We didn't find everyone, so better report this
#
if ( $hcount < scalar(@$hosts) ) {
warn "Did not find all of the nominated hosts\n";
return {};
}
return \%hosts_found;
}
#=== FUNCTION ================================================================
# NAME: clean_host_options
# PURPOSE: Check the requested hosts (as specified in the options)
# against the actual hosts linked to the show
# PARAMETERS: $details Hashref containing details
# $show Show key
# $hosts Arrayref of requested hosts
# RETURNS: A list of the hosts that aren't already linked
# DESCRIPTION: Places all the hosts associated with the show into an array
# then compares it with the @$hosts array such that what is left
# is all the hosts that do not match hosts linked to the show.
# Of cxourse, if any of the hosts given as options are not exact
# matches with the names from the database (they are regexes
# perhaps) they'll be regarded as different. We have to do
# further processing of what's returned from querying the
# database for new hosts elsewhere, but this function should
# help to save some unnecessary database queries.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub clean_host_options {
my ( $details, $show, $hosts ) = @_;
#
# Collect the names of the host(s) already linked to this show
#
my @show_hosts = keys( %{ $details->{$show}->{hosts} } );
#
# Return a list of the hosts that aren't in @show_hosts
#
return grep { my $item = $_; !grep( $_ eq $item, @show_hosts ) } @$hosts;
}
#=== FUNCTION ================================================================
# NAME: print_details
# PURPOSE: Print accumulated show details
# PARAMETERS: $details Hashref containing details
# $show Show key
# $pfmt Format string for printing
# RETURNS: Nothing
# DESCRIPTION: Prints the show and linked host information from the %details
# hash. To be used after these details have been collected and
# before the new hosts have been analysed.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub print_details {
my ( $details, $show, $pfmt ) = @_;
my $count = 0;
print "Analysis of show and associated host details\n";
printf $pfmt, 'Show key', $show;
my $ptr = $details->{$show};
printf $pfmt, 'Episode id', $ptr->{episode_id};
printf $pfmt, 'Release date', $ptr->{release_date};
printf $pfmt, 'Title', $ptr->{title};
foreach my $host ( keys( %{ $ptr->{hosts} } ) ) {
$count++;
my $ptr2 = $ptr->{hosts};
printf $pfmt, "Host $count", $host;
printf $pfmt, "Host id $count", $ptr2->{$host};
}
}
#=== FUNCTION ================================================================
# NAME: save_details
# PURPOSE: Save details of the show and related hosts in a hash
# PARAMETERS: $ptr location to store into
# $key key into the hash (under $ptr)
# $value value to store
# RETURNS: Nothing
# DESCRIPTION: Simplifes the process of saving information in the %details
# hash, skipping the saving process if there is already data
# stored. This is needed because the query used to find out
# about the show and its host(s) returns more than one row. We
# want to make it easy to store one set of show information and
# all host information.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub save_details {
my ( $ptr, $key, $value ) = @_;
unless ( exists( $ptr->{$key} ) ) {
$ptr->{$key} = $value;
}
}
#=== FUNCTION ================================================================
# NAME: coalesce
# PURPOSE: To find the first defined argument and return it
# PARAMETERS: Arbitrary number of arguments
# RETURNS: The first defined argument or undef if there are none
# DESCRIPTION: Modelled on the SQL function of the same name. It takes a list
# of arguments, scans it for the first one that is not undefined
# and returns it. If an argument is defined and it's an arrayref
# then the referenced array is returned comma-delimited. This
# allows calls such as "coalesce($var,'undef')" which returns
# the value of $var if it's defined, and 'undef' if not and
# doesn't break anything along the way.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub coalesce {
foreach (@_) {
if ( defined($_) ) {
if ( ref($_) eq 'ARRAY' ) {
return join( ',', @{$_} );
}
else {
return $_;
}
}
}
return; # implicit undef
}
#=== FUNCTION ================================================================
# NAME: _debug
# PURPOSE: Prints debug reports
# PARAMETERS: $active Boolean: 1 for print, 0 for no print
# $message Message to print
# RETURNS: Nothing
# DESCRIPTION: Outputs a message if $active is true. It removes any trailing
# newline and then adds one in the 'print' to the caller doesn't
# have to bother. Prepends the message with 'D> ' to show it's
# a debug message.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub _debug {
my ( $active, $message ) = @_;
chomp($message);
print "D> $message\n" if $active;
}
#=== 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", "documentation|man", "debug=i", "verbose+",
"show=s", "host=s@", "prompt!",
);
if ( !GetOptions( $optref, @options ) ) {
pod2usage(
-msg => "$PROG version $VERSION\n",
-exitval => 1,
-verbose => 0
);
}
return;
}
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# Application Documentation
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#{{{
=head1 NAME
add_hosts_to_show - add hosts to a pre-existing show
=head1 VERSION
This documentation refers to add_hosts_to_show version 0.0.5
=head1 USAGE
add_hosts_to_show -show=SHOW -host=HOST1 [-host=HOST2 ... -host=HOSTn]
[-[no]prompt] [-debug=N] [-verbose] [-help] [-documentation]
Examples:
./add_hosts_to_show -show=hpr2297 -host='Andrew Conway'
./add_hosts_to_show -show=2297 -host='Andrew Conway'
./add_hosts_to_show -show=2297 -host='Andrew Conway' -noprompt
=head1 OPTIONS
=over 8
=item B<-show=SHOW>
This mandatory option defines the show which is to be adjusted. The argument
is a show designation which may consist of one of the following:
=over 4
=item B<hprNNNN>
The characters 'B<hpr>' must be followed by a show number, and this will select
the I<Hacker Public Radio> show with that number.
=item B<twatNNNN>
The characters 'B<twat>' must be followed by a show number, and this will
select the I<Today with a Techie> show with that number.
=item B<NNNN>
A plain number is interpreted as a I<Hacker Public Radio> show as if
B<hprNNNN> had been specified.
=back
=item B<-host=HOST>
This mandatory option defines a host to be added to the list of show hosts.
The B<HOST> part must consist of the name of the host. If the name contains
a space then it must be enclosed in quotes (or escaped).
The B<-host=HOST> option may be repeated as many times as needed.
The B<HOST> part may be shortened so long as it is unambiguous. The script
attempts an exact match on the name, but if that fails it will use the name as
a case insensitive regular expression match, which it will use so long as it
finds only one match.
=item B<-[no]prompt>
Normally the script prompts to give an opportunity to cancel the process if an
error has been made. Specifying B<i-noprompt> turns off this feature.
=item B<-verbose>
This option, which can be repeated, increases the level of verbosity for each
use. The levels of verbosity are:
=over 4
0 - minimal reporting
1 - simple messages shown
=back
=item B<-help>
Prints a brief help message describing the usage of the program, and then exits.
=item B<-documentation> or B<-man>
Reports full information about how to use the script and exits.
Since the formatting used by the POD parser used here seems faulty, an
alternative way to view the documentation on-screen is:
pod2man add_hosts_to_show | man -l -
Alternatively, to generate a PDF version use the I<pod2pdf> tool from
I<http://search.cpan.org/~jonallen/pod2pdf-0.42/bin/pod2pdf>. This can be
installed with the cpan tool as App::pod2pdf.
=item B<-debug=N>
Selects a level of debugging. Debug information consists of a line or series
of lines prefixed with the characters 'D>':
=over 4
=item B<0>
No debug output is generated: this is the default
=item B<3>
The results of analysing the options are displayed.
The B<%details> hash is dumped after capturing show and linked host details
and new host details. The hash has also been cleaned of host duplicates.
=back
=item B<-verbose>
Makes the script verbose resulting in the production of more information about
what it is doing.
The script always reports the show details, the linked host details and the
details of the new hosts that have been requested. It needs to do this in
order to produce a meaningful prompt.
The option may be repeated to increase the level of verbosity. The levels are:
=over 4
=item B<0>
No output is generated (apart from errors and warnings if appropriate). This
is the default level.
=item B<1>
Once the prompt has been given the script will either report on the addition
of the new host(s) as it adds them iif the prompt was confirmed, or will
report that no host has been added if the answer to the prompt was "No".
=back
=back
=head1 DESCRIPTION
A full description of the application and its features.
May include numerous subsections (i.e. =head2, =head3, etc.)
=head1 DIAGNOSTICS
A list of every error and warning message that the application can generate
(even the ones that will "never happen"), with a full explanation of each
problem, one or more likely causes, and any suggested remedies. If the
application generates exit status codes (e.g. under Unix) then list the exit
status associated with each error.
=head1 CONFIGURATION AND ENVIRONMENT
A full explanation of any configuration system(s) used by the application,
including the names and locations of any configuration files, and the
meaning of any environment variables or properties that can be set. These
descriptions must also include details of any configuration language used
=head1 DEPENDENCIES
A list of all the other modules that this module relies upon, including any
restrictions on versions, and an indication whether these required modules are
part of the standard Perl distribution, part of the module's distribution,
or must be installed separately.
=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) 2017 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
-- View: eh_view
DROP VIEW IF EXISTS eh_view;
CREATE OR REPLACE VIEW eh_view AS
SELECT ep.episode_key,
ep.release_date,
ep.title,
ep.summary,
( SELECT string_agg(h2.host::text, '; '::text ORDER BY h2.host::text) AS string_agg
FROM hosts h2,
episodes_hosts_xref eh2
WHERE eh2.hosts_id = h2.id
GROUP BY eh2.episodes_id
HAVING eh2.episodes_id = ep.id) AS hosts
FROM episodes ep
GROUP BY ep.id
ORDER BY ep.id;
ALTER TABLE eh_view
OWNER TO hpradmin;

View File

@@ -0,0 +1,22 @@
DROP VIEW eht_view;
CREATE OR REPLACE VIEW eht_view AS
SELECT
e.*,
h.host,
t.tag,
(select string_agg(tag, ', ')
from tags t2, episodes_tags_xref et2
where et2.tags_id = t2.id
group by et2.episodes_id
having et2.episodes_id = e.id) as tags
FROM episodes e, hosts h, episodes_hosts_xref eh, episodes_tags_xref et, tags t
where e.id = eh.episodes_id
and h.id = eh.hosts_id
and e.id = et.episodes_id
and et.tags_id = t.id
group by e.id,h.host,t.tag
order by e.id;
ALTER TABLE eht_view
OWNER TO hpradmin;

View File

@@ -0,0 +1,22 @@
--
-- An attempt to produce a query that summarises multiple hosts and multiple
-- tags for an episode. Not very successful, but kept for reference.
--
SELECT
ep.id,
ep.release_date,
ep.title,
string_agg(distinct h.host, ', ') AS hosts,
string_agg(t.tag, ', ') AS taglist
FROM episodes ep
JOIN episodes_hosts_xref eh ON (eh.episodes_id = ep.id)
JOIN hosts h ON (h.id = eh.hosts_id)
LEFT JOIN episodes_tags_xref et ON (et.episodes_id = ep.id)
LEFT JOIN tags t ON (t.id = et.tags_id)
WHERE ep.id BETWEEN 2200 AND 2400
GROUP BY ep.id, h.host
ORDER BY ep.id;
-- Footer ---------------------------------------------------------------------
-- vim: syntax=pgsql:ts=8:sw=4:ai:tw=78:et:fo=tcrqn21

View File

@@ -0,0 +1,30 @@
--
-- An alternative (better) way of showing multiple hosts with an episode. It
-- doesn't do much of a job with tags though.
--
-- (Actually doesn't work just now)
SELECT
ep.id,
ep.episode_key,
ep.release_date,
ep.title,
(SELECT string_agg(host, ', ' ORDER BY host)
FROM hosts h2,
episodes_hosts_xref eh2
WHERE eh2.hosts_id = h2.id
GROUP BY eh2.episodes_id
HAVING eh2.episodes_id = ep.id) AS hosts,
string_agg(distinct t.tag, ', ') AS taglist
FROM episodes ep
-- JOIN episodes_hosts_xref eh ON (eh.episodes_id = ep.id)
-- JOIN hosts h ON (h.id = eh.hosts_id)
LEFT JOIN episodes_tags_xref et ON (et.episodes_id = ep.id)
LEFT JOIN tags t ON (t.id = et.tags_id)
WHERE ep.episode_key in ('hpr1777','hpr2297','hpr2400')
GROUP BY ep.id
ORDER BY ep.id;
-- Footer ---------------------------------------------------------------------
-- vim: syntax=pgsql:ts=8:sw=4:ai:tw=78:et:fo=tcrqn21

View File

@@ -0,0 +1,6 @@
select e.id, e.release_date,e.title, s.name as series_name
from episodes e
join episodes_series_xref es on (es.episodes_id = e.id)
join series s on (es.series_id = s.id)
where e.id between 2300 and 2400
order by e.id;

View File

@@ -0,0 +1,426 @@
#!/usr/bin/env perl
#===============================================================================
#
# FILE: find_double_hosts
#
# USAGE: ./find_double_hosts
#
# DESCRIPTION: Find HPR shows with two hosts (host is "A and B"), find the
# hosts if possible and flag updates to the database to
# represent the dual nature.
#
# OPTIONS: ---
# REQUIREMENTS: ---
# BUGS: ---
# NOTES: NOTE: Don't run this. Its functionality is now part of
# copy_mysql_pg_2 and is being applied in the 'epilogue' phase.
# AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
# VERSION: 0.0.2
# CREATED: 2017-10-13 19:17:51
# REVISION: 2017-10-13 19:19:43
#
#===============================================================================
use 5.010;
use strict;
use warnings;
use utf8;
use Config::General;
use DBI;
use Data::Dumper;
#
# Version number (manually incremented)
#
our $VERSION = '0.0.2';
#
# 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 $configfile1 = "$basedir/.hpr_db.cfg";
my $configfile2 = "$basedir/.hpr_pg.cfg";
my $email_template = 'host_%s@hackerpublicradio.org';
my $default_licence = 'CC-BY-SA';
my ( $dbh1, $dbh2, $sth1, $h1, $rv1, $sth2, $h2, $rv2, $sth3, $h3, $rv3, $sth4, $h4, $rv4 );
my ( %doubles, @h, %hosts, $unknown, $default_email );
#
# Enable Unicode mode
#
binmode STDOUT, ":encoding(UTF-8)";
binmode STDERR, ":encoding(UTF-8)";
#
# Load database configuration data
#
my $conf1 = Config::General->new(
-ConfigFile => $configfile1,
-InterPolateVars => 1,
-ExtendedAccess => 1
);
my %config1 = $conf1->getall();
my $conf2 = Config::General->new(
-ConfigFile => $configfile2,
-InterPolateVars => 1,
-ExtendedAccess => 1
);
my %config2 = $conf2->getall();
#-------------------------------------------------------------------------------
# Connect to the MariaDB database
#-------------------------------------------------------------------------------
my $dbtype1 = $config1{database}->{type} // 'mysql';
my $dbhost1 = $config1{database}->{host} // '127.0.0.1';
my $dbport1 = $config1{database}->{port} // 3306;
my $dbname1 = $config1{database}->{name};
my $dbuser1 = $config1{database}->{user};
my $dbpwd1 = $config1{database}->{password};
$dbh1
= DBI->connect( "dbi:$dbtype1:host=$dbhost1;port=$dbport1;database=$dbname1",
$dbuser1, $dbpwd1, { AutoCommit => 1 } )
or die $DBI::errstr;
#
# Enable client-side UTF8
#
$dbh1->{mysql_enable_utf8} = 1;
#-------------------------------------------------------------------------------
# Connect to the PostgreSQL database
#-------------------------------------------------------------------------------
my $dbtype2 = $config2{database}->{type} // 'Pg';
my $dbhost2 = $config2{database}->{host} // '127.0.0.1';
my $dbport2 = $config2{database}->{port} // 5432;
my $dbname2 = $config2{database}->{name};
my $dbuser2 = $config2{database}->{user};
my $dbpwd2 = $config2{database}->{password};
$dbh2 = DBI->connect( "dbi:$dbtype2:host=$dbhost2;database=$dbname2;port=$dbport2",
$dbuser2, $dbpwd2, { PrintError => 0, AutoCommit => 1 } )
or die $DBI::errstr;
#
# Enable client-side UTF8
#
$dbh2->{pg_enable_utf8} = 1;
#-------------------------------------------------------------------------------
# Query preparation
#-------------------------------------------------------------------------------
#
# MariaDB query to find double hosts
#
my $sql1 = q{
SELECT hostid, host FROM hosts
WHERE host regexp '[[:<:]]and[[:>:]]'
ORDER BY hostid
};
$sth1 = $dbh1->prepare($sql1) or die $DBI::errstr;
if ( $dbh1->err ) {
warn $dbh1->errstr;
}
#
# MariaDB query to find the host by name
#
$sth2 = $dbh1->prepare(q{SELECT hostid FROM hosts WHERE host REGEXP ?})
or die $DBI::errstr;
if ( $dbh1->err ) {
warn $dbh1->errstr;
}
#
# PostgreSQL query to register an unknown host
#
$sth3
= $dbh2->prepare(q{INSERT INTO hosts (host,email,license) VALUES (?,?,?)})
or die $DBI::errstr;
if ( $dbh2->err ) {
warn $dbh2->errstr;
}
#
# PostgreSQL query to find shows with particular host ids
#
$sth4 = $dbh2->prepare(
q{
SELECT e.id AS eps_id
FROM episodes e
JOIN episodes_hosts_xref eh ON (e.id = eh.episodes_id)
JOIN hosts h ON (h.id = eh.hosts_id)
WHERE h.id = ?
}
) or die $DBI::errstr;
if ( $dbh2->err ) {
warn $dbh2->errstr;
}
#-------------------------------------------------------------------------------
# Find all the "double hosts"
#-------------------------------------------------------------------------------
#
# Query MariaDB for the target hosts
#
$sth1->execute;
if ( $dbh1->err ) {
die $dbh1->errstr;
}
#
# Loop through the list of double hostnames and parse them out. Save the
# originals in the %doubles hash and the parsed names in the %hosts hash.
#
while ( $h1 = $sth1->fetchrow_hashref ) {
#
# Each hash value is a hash containing the original id, and, in a sub-hash
# the replacement ids
#
$doubles{$h1->{host}} = {
double => $h1->{hostid},
singles => {},
};
#
# Parse the double host string
#
@h = ( $h1->{host} =~ /^(.+)\s+and\s+(.+)$/ );
printf "%-4d %s", $h1->{hostid}, $h1->{host};
print " [", join( ",", @h ), "]\n";
#
# Initialise the entries for %doubles and %hosts
#
for my $host (@h) {
$doubles{$h1->{host}}->{singles}->{$host} = undef;
unless ( exists( $hosts{$host} ) ) {
$hosts{$host} = 0;
}
}
}
print '-' x 80,"\n";
#-------------------------------------------------------------------------------
# Find the single hosts in the 'hosts' table
#-------------------------------------------------------------------------------
#
# Scan the list of individual hosts and find them in the 'hosts' table
#
$unknown = 0;
foreach my $host ( sort(keys(%hosts)) ) {
$rv2 = $sth2->execute("^$host\$");
if ( $dbh1->err ) {
die $dbh1->errstr;
}
$rv2 = 0 if ( $rv2 eq '0E0' );
if ($rv2) {
$h2 = $sth2->fetchrow_hashref;
print "Found id for $host: ", $h2->{hostid}, "\n";
$hosts{$host} = $h2->{hostid};
save_hostid(\%doubles,$host,$h2->{hostid});
}
else {
print "Can't find $host\n";
$unknown++;
}
}
#print Dumper(\%hosts),"\n";
print '-' x 80,"\n";
#-------------------------------------------------------------------------------
# Allocate all unknown hosts a host id in the PostgreSQL database, and give an
# unique email address.
#-------------------------------------------------------------------------------
if ( $unknown > 0 ) {
print "Registering $unknown hosts\n";
foreach my $host ( sort( keys(%hosts) ) ) {
if ( $hosts{$host} == 0 ) {
$rv3 = $sth3->execute( $host, undef, $default_licence );
if ( $dbh2->err ) {
die $dbh2->errstr;
}
#
# Write a row to the 'hosts' table and save the id number
# generated
#
my $newid = $dbh2->last_insert_id( undef, undef, undef, undef,
{ sequence => 'host_seq' } );
$hosts{$host} = $newid;
save_hostid(\%doubles,$host,$newid);
print "Host $host added with id $newid\n";
#
# Give the new host entry a default email address
#
$default_email = sprintf($email_template,$newid);
$rv3 = $dbh2->do( 'UPDATE hosts SET email = ? WHERE id = ?',
undef, $default_email, $newid );
if ( $dbh2->err ) {
warn $dbh2->errstr;
}
$rv3 = 0 if ( $rv3 eq '0E0' );
warn "Failed to set email address $default_email for $host\n"
unless ( defined($rv3) );
}
}
}
print '-' x 80,"\n";
#-------------------------------------------------------------------------------
# Now %doubles contains all the original names and host ids and %hosts
# contains the parsed out names and their ids. We can look for shows
# attributed to the first set and re-attribute them to the second set.
#-------------------------------------------------------------------------------
print "Changing host associations for shows with two hosts\n";
foreach my $double ( sort( keys(%doubles) ) ) {
print "Processing $double\n";
my ( $doubleid, @newids ) = (
$doubles{$double}->{double},
values( %{ $doubles{$double}->{singles} } )
);
print " Original id: $doubleid\n";
print " Replacements: ", join( ", ", @newids ), "\n";
#
# Find shows marked as belonging to this double-host
#
$sth4->execute($doubleid);
if ( $dbh2->err ) {
die $dbh2->errstr;
}
#
# Process all the shows
#
while ( $h4 = $sth4->fetchrow_hashref ) {
my $eps_id = $h4->{eps_id};
print " Show $eps_id is ascribed to host $doubleid\n";
$dbh2->begin_work();
#
# Delete the xref link for the double host
#
$rv4
= $dbh2->do(
'DELETE FROM episodes_hosts_xref WHERE episodes_id = ?',
undef, $eps_id );
if ( $dbh2->err ) {
warn $dbh2->errstr;
}
$rv4 = 0 if ( $rv4 eq '0E0' );
if ( defined($rv4) ) {
print " Deleted entry from episodes_hosts_xref for $eps_id\n";
}
else {
warn "Problem deleting from episodes_hosts_xref for $eps_id\n";
}
#
# Add links for the single hosts
#
foreach my $hid (@newids) {
$rv4 = $dbh2->do( 'INSERT INTO episodes_hosts_xref VALUES (?,?)',
undef, $eps_id, $hid );
if ( $dbh2->err ) {
warn $dbh2->errstr;
}
$rv4 = 0 if ( $rv4 eq '0E0' );
if ( defined($rv4) ) {
print " Added entry to episodes_hosts_xref values ",
"$eps_id,$hid\n";
}
else {
warn "Problem adding to episodes_hosts_xref values "
. "$eps_id,$hid\n";
}
}
#
# Commit the delete/inserts above
#
$dbh2->commit();
}
print '~' x 80, "\n";
#
# Delete the double host (NOTE: This will fail due to referential
# integrity if the DELETE above failed, so there is scope for debris to be
# left around)
#
$rv4 = $dbh2->do( 'DELETE FROM hosts WHERE id = ?', undef, $doubleid );
if ( $dbh2->err ) {
warn $dbh2->errstr;
}
$rv4 = 0 if ( $rv4 eq '0E0' );
if ( defined($rv4) ) {
print " Deleted entry from hosts for id $doubleid ($double)\n";
}
else {
warn "Problem deleting from hosts for id $doubleid ($double)\n";
}
}
print '-' x 80,"\n";
exit;
#=== FUNCTION ================================================================
# NAME: save_hostid
# PURPOSE: Saves the host id after searching for the key in the %doubles
# hash
# PARAMETERS: $doubles hashref to %doubles
# $host host key
# $hostid host id number
# RETURNS: Nothing
# DESCRIPTION: Searches the %doubles hash for particular keys in the
# 'singles' sub-hash. If found saves the corresponding host id
# there.
# THROWS: No exceptions
# COMMENTS: None
# SEE ALSO: N/A
#===============================================================================
sub save_hostid {
my ( $doubles, $host, $hostid ) = @_;
foreach my $key ( keys(%$doubles) ) {
if ( exists( $doubles->{$key}->{singles}->{$host} ) ) {
$doubles->{$key}->{singles}->{$host} = $hostid;
}
}
}
# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker

View File

@@ -0,0 +1,480 @@
/* =============================================================================
* PostgreSQL Schema V2 - designs for a new HPR database
*
* File: hpr_schema_2.pgsql
* Created: 2017-10-22
* Updated: 2019-04-15
* =============================================================================
*/
/* ------------------------------------------------------------------------------
* Design decisions made:
*
* - Plural names for tables ('episodes' not 'episode')
* - Table names all lower case with underscores
* - Field names all lower case with underscores
* - Primary keys named "[singularOfTableName]ID", so 'episodeID' in the
* 'episodes' table TODO: make it 'episode_ID'??
* - Foreign keys named consistently in all tables
* - Explicit sequences called "[singularOfTableName]_seq"
* - Cross reference tables shouldn't call their fields
* "[pluralTableName]_id" but "[singularOfTableName]_id" because
* 'episodes_id' seems stupid.
*
* ------------------------------------------------------------------------------
*/
/* ------------------------------------------------------------------------------
* Drop everything to start with. The order is important because of the
* relations between tables. Also, some items are dependent and go with the
* tables.
* ------------------------------------------------------------------------------
*/
-- {{{
DROP TABLE IF EXISTS comments CASCADE;
DROP TABLE IF EXISTS episodes CASCADE;
DROP TABLE IF EXISTS episodes_hosts_xref CASCADE;
DROP TABLE IF EXISTS episodes_series_xref CASCADE;
DROP TABLE IF EXISTS episodes_tags_xref CASCADE;
DROP TABLE IF EXISTS hosts CASCADE;
DROP TABLE IF EXISTS licenses CASCADE;
DROP TABLE IF EXISTS series CASCADE;
DROP TABLE IF EXISTS tags CASCADE;
DROP TABLE IF EXISTS assets CASCADE;
-- DROP INDEX IF EXISTS episode_release_date_key;
DROP SEQUENCE IF EXISTS comment_seq;
DROP SEQUENCE IF EXISTS episode_seq;
DROP SEQUENCE IF EXISTS host_seq;
DROP SEQUENCE IF EXISTS license_seq;
DROP SEQUENCE IF EXISTS series_seq;
DROP SEQUENCE IF EXISTS tag_seq;
DROP SEQUENCE IF EXISTS assets_seq;
-- DROP VIEW IF EXISTS eht_view;
DROP FUNCTION IF EXISTS id_in_licenses(sname varchar);
DROP FUNCTION IF EXISTS id_in_episodes(ekey varchar);
DROP TRIGGER IF EXISTS comment_changed ON comments CASCADE;
DROP FUNCTION IF EXISTS comment_changed();
-- }}}
-- \/\/ licenses \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'licenses' - licenses relating to episodes (needed because 'episodes'
* and hosts' reference it)
*
* license_id primary key, the licence number
* short_name brief name of CC licence
* long_name longer name of CC licence
* url link to details of licence at creativecommons.org
*
* ------------------------------------------------------------------------------
*/
CREATE SEQUENCE license_seq;
ALTER TABLE license_seq
OWNER TO hpradmin;
CREATE TABLE licenses (
license_id integer default nextval('license_seq') PRIMARY KEY,
short_name varchar(11) NOT NULL UNIQUE,
long_name varchar(40) NOT NULL,
url varchar(80) NOT NULL
);
ALTER TABLE licenses
OWNER TO hpradmin;
/*
* Load the table since it's quite short. Don't set the license_id to ensure
* the sequence is updated properly.
*/
INSERT INTO licenses (short_name, long_name, url) VALUES
('CC-0', 'Public Domain Dedication', 'http://creativecommons.org/publicdomain/zero/1.0/'),
('CC-BY', 'Attribution', 'http://creativecommons.org/licenses/by/4.0'),
('CC-BY-SA', 'Attribution-ShareAlike', 'http://creativecommons.org/licenses/by-sa/3.0'),
('CC-BY-ND', 'Attribution-NoDerivs', 'http://creativecommons.org/licenses/by-nd/4.0'),
('CC-BY-NC', 'Attribution-NonCommercial', 'http://creativecommons.org/licenses/by-nc/4.0'),
('CC-BY-NC-SA', 'Attribution-NonCommercial-ShareAlike', 'http://creativecommons.org/licenses/by-nc-sa/4.0'),
('CC-BY-NC-ND', 'Attribution-NonCommercial-NoDerivs', 'http://creativecommons.org/licenses/by-nc-nd/4.0');
/*
* Define a simple function to return the license_id number corresponding to
* a short name in the licenses table. This simplifies setting a default value
* in a foreign key definition in other tables. It's not resilient to errors
* as it stands.
*/
CREATE OR REPLACE FUNCTION id_in_licenses(sname varchar) RETURNS integer AS
$$
SELECT license_id FROM licenses WHERE short_name = sname;
$$
LANGUAGE SQL STABLE;
ALTER FUNCTION id_in_licenses(sname varchar)
OWNER TO hpradmin;
-- }}}
-- \/\/ episodes \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'episodes' - TWAT and HPR shows
*
* episode_id primary key (NOT the episode number)
* episode_key episode designator
* release_date date the episode was released
* title title of the episode
* summary summary of the episode content
* notes the show notes (as an HTML fragment)
* explicit a Boolean; true for explicit, false for otherwise
* license the licence which the show is under (US spelling)
* duration the duration (time) of the audio
* downloads number of downloads
* archived a Boolean; true if the episode has been uploaded to the IA
* archive_date date the episode was archived
* IA_URL URL to the episode on archive.org
* journal a journal of actions performed on this episode
*
* TODO: Do we need a function to determine the next 'episode_key'?
* NOTE: The 'valid' column has been removed.
*
* ------------------------------------------------------------------------------ */
CREATE SEQUENCE episode_seq;
ALTER TABLE episode_seq
OWNER TO hpradmin;
CREATE TYPE episode_status AS ENUM ('reserved', 'processing', 'posted');
CREATE TABLE episodes (
episode_id integer default nextval('episode_seq') PRIMARY KEY,
episode_key varchar(40) NOT NULL,
release_date date NOT NULL,
title varchar(128) NOT NULL,
summary varchar(128),
notes text NOT NULL,
explicit boolean NOT NULL DEFAULT TRUE,
license integer NOT NULL DEFAULT id_in_licenses('CC-BY-SA')
REFERENCES licenses (license_id),
duration interval NOT NULL DEFAULT '00:00:00',
downloads integer NOT NULL DEFAULT 0,
archived boolean NOT NULL DEFAULT FALSE,
archive_date date,
IA_URL text,
status episode_status,
journal text
);
ALTER TABLE episodes
OWNER TO hpradmin;
CREATE INDEX episode_key_idx
ON episodes
USING btree
(episode_key);
CREATE INDEX episode_release_date_idx
ON episodes
USING btree
(release_date);
/*
* Define a simple function to return the episode_id number corresponding to
* an episode key in the 'episodes' table.
*/
CREATE OR REPLACE FUNCTION id_in_episodes(ekey varchar) RETURNS integer AS
$$
SELECT episode_id FROM episodes WHERE episode_key = ekey;
$$
LANGUAGE SQL STABLE;
ALTER FUNCTION id_in_episodes(ekey varchar)
OWNER TO hpradmin;
-- }}}
-- \/\/ hosts /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'hosts' - hosts contributing shows
*
* host_id primary key, host number
* host host name or handle
* email host email address (cannot be unique because some hosts need
* to have their mail directed to admin@hpr)
* profile text describing the host, HTML preferred
* license the default licence chosen by the host (US spelling)
* local_image true if there's a host-supplied image for the host
* gpg the host's GPG key
* espeak_name the host name written so that 'espeak' speaks it properly
* when_added the date the host was added to the database
*
* TODO: Do we need the 'valid' field? Removed for the moment.
* NOTE: Is there any point in the added date? Most will start as the date the
* table was created in this database, and effort will be needed to set
* approximate dates in the past (from the date of their first shows).
*
* ------------------------------------------------------------------------------ */
CREATE SEQUENCE host_seq;
ALTER TABLE host_seq
OWNER TO hpradmin;
CREATE TABLE hosts (
host_id integer default nextval('host_seq') PRIMARY KEY,
host varchar(1024) UNIQUE NOT NULL,
email varchar(1024) UNIQUE NOT NULL,
profile text,
license integer NOT NULL DEFAULT id_in_licenses('CC-BY-SA')
REFERENCES licenses (license_id),
local_image boolean NOT NULL DEFAULT FALSE,
gpg text,
-- valid boolean NOT NULL DEFAULT TRUE,
espeak_name text,
when_added date NOT NULL DEFAULT now()
);
ALTER TABLE hosts
OWNER TO hpradmin;
/* ------------------------------------------------------------------------------
* Table 'episodes_hosts_xref' - joining table between 'episodes' and 'hosts'
*
* episode_id primary key of the episodes table
* host_id primary key of the hosts table
*
* The two fields jointly make the primary key of this table
*
* TODO: Check the deletion actions
*
* ------------------------------------------------------------------------------ */
CREATE TABLE episodes_hosts_xref (
episode_id integer REFERENCES episodes(episode_id)
ON DELETE RESTRICT,
host_id integer REFERENCES hosts(host_id)
ON DELETE CASCADE,
PRIMARY KEY (episode_id, host_id)
);
ALTER TABLE episodes_hosts_xref
OWNER TO hpradmin;
-- }}}
-- \/\/ tags \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'tags' - tags relating to episodes
*
* tag_id primary key, tag number
* tag a tag
*
* TODO: Tags are case-sensitive. Should they be?
* TODO: Should tags have a unique key on them?
*
* ------------------------------------------------------------------------------ */
CREATE SEQUENCE tag_seq;
ALTER TABLE tag_seq
OWNER TO hpradmin;
CREATE TABLE tags (
tag_id integer default nextval('tag_seq') PRIMARY KEY,
tag varchar(1024) NOT NULL
);
ALTER TABLE tags
OWNER TO hpradmin;
/* ------------------------------------------------------------------------------
* Table 'episodes_tags_xref' - joining table between 'episodes' and 'tags'
*
* episode_id primary key of the episodes table
* tag_id primary key of the tags table
*
* The two fields jointly make the primary key of this table
*
* TODO: Check the deletion actions
*
* ------------------------------------------------------------------------------ */
CREATE TABLE episodes_tags_xref (
episode_id integer REFERENCES episodes(episode_id)
ON DELETE RESTRICT,
tag_id integer REFERENCES tags(tag_id)
ON DELETE CASCADE,
PRIMARY KEY (episode_id, tag_id)
);
ALTER TABLE episodes_tags_xref
OWNER TO hpradmin;
-- }}}
-- \/\/ series \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'series' - series grouping for episodes
*
* series_id primary key, series number
* name name of series
* description description of series (HTML preferred)
* private whether others may contribute to the series
* image ??
*
* NOTE: Removed 'valid'
*
* ------------------------------------------------------------------------------ */
CREATE SEQUENCE series_seq;
ALTER TABLE series_seq
OWNER TO hpradmin;
CREATE TABLE series (
series_id integer default nextval('series_seq') PRIMARY KEY,
name varchar(100) NOT NULL,
description text NOT NULL,
private boolean NOT NULL DEFAULT FALSE,
image text
-- valid boolean NOT NULL DEFAULT TRUE
);
ALTER TABLE series
OWNER TO hpradmin;
/* ------------------------------------------------------------------------------
* Table 'episodes_series_xref' - joining table between 'episodes' and 'series'
*
* episodes_id primary key of the episodes table
* series_id primary key of the series table
*
* The two fields jointly make the primary key of this table
*
* TODO: Check the deletion actions
*
* ------------------------------------------------------------------------------ */
CREATE TABLE episodes_series_xref (
episode_id integer REFERENCES episodes(episode_id)
ON DELETE RESTRICT,
series_id integer REFERENCES series(series_id)
ON DELETE CASCADE,
PRIMARY KEY (episode_id, series_id)
);
ALTER TABLE episodes_series_xref
OWNER TO hpradmin;
-- }}}
-- \/\/ comments \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'comments' - comments relating to episodes
*
* comment_id primary key, comment number
* episode_id primary key of the episodes table
* comment_timestamp UTC timestamp when comment was submitted
* comment_author_name name given by the comment author
* comment_title title given by the comment author
* comment_text text of the comment (NO HTML)
* last_changed UTC timestamp of the last change to the row
*
* TODO: Check the deletion FK actions
*
* ------------------------------------------------------------------------------ */
CREATE SEQUENCE comment_seq;
ALTER TABLE comment_seq
OWNER TO hpradmin;
CREATE TABLE comments (
comment_id integer default nextval('comment_seq') PRIMARY KEY,
episode_id integer REFERENCES episodes(episode_id)
ON DELETE RESTRICT,
comment_timestamp timestamp without time zone NOT NULL,
comment_author_name varchar(1024),
comment_title varchar(1024),
comment_text text,
last_changed timestamp without time zone NOT NULL
DEFAULT timezone('UTC'::text, now())
);
ALTER TABLE comments
OWNER TO hpradmin;
/* ------------------------------------------------------------------------------
* Trigger function 'comment_changed' and trigger on the 'comments' table
*
* ------------------------------------------------------------------------------ */
CREATE FUNCTION comment_changed() RETURNS trigger AS $comment_changed$
BEGIN
-- Remember when the comment row was changed
NEW.last_changed := current_timestamp;
RETURN NEW;
END;
$comment_changed$ LANGUAGE plpgsql;
ALTER FUNCTION comment_changed()
OWNER TO hpradmin;
CREATE TRIGGER comment_changed BEFORE UPDATE ON comments
FOR EACH ROW EXECUTE PROCEDURE comment_changed();
-- }}}
-- \/\/ assets \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
-- {{{
/* ------------------------------------------------------------------------------
* Table 'assets' - assets relating to episodes
*
* asset_id primary key
* episode_id link to episodes.episode_id (show the link was found in)
* URL URL of the asset (on the HPR site)
* filename filename (or path) component (after percent decoding)
* uploaded Boolean showing if an asset has been uploaded to the IA
*
* TODO: should we consider recording the IA URL of an asset?
*
* ------------------------------------------------------------------------------ */
CREATE SEQUENCE asset_seq;
ALTER TABLE asset_seq
OWNER TO hpradmin;
CREATE TABLE assets (
asset_id integer default nextval('asset_seq') PRIMARY KEY,
episode_id integer REFERENCES episodes(episode_id)
ON DELETE RESTRICT,
URL text NOT NULL,
filename text NOT NULL,
uploaded boolean NOT NULL DEFAULT FALSE
);
ALTER TABLE assets
OWNER TO hpradmin;
-- }}}
-- Footer ---------------------------------------------------------------------
-- vim: syntax=pgsql:ts=8:sw=4:ai:tw=78:et:fo=tcrqn21:fdm=marker:nu:rnu

View File

@@ -0,0 +1,28 @@
#!/bin/bash
#
# Script to re-initialise the HPR2 database then rebuild it from the local
# MySQL copy (and ia.db for the IA parts). Also updates shows with multiple
# hosts by adding the missing hosts.
#
#
# Directories
#
BASEDIR="$HOME/HPR/PostgreSQL_Database"
cd "$BASEDIR" || { echo "Failed to cd to $BASEDIR"; exit 1; }
#
# Drops all tables, etc and re-creates them
#
psql -U hpradmin HPR2 < hpr_schema_2.pgsql
#
# Does all phases in order by default
#
./copy_mysql_pg_2 -verb
#
# Adds in all the double hosts we know about at the moment
#
./update_multi-host_shows.sh

View File

@@ -0,0 +1,19 @@
#!/bin/bash
#
# This script keeps a record of the shows that actually have multiple hosts
# but can't indicate as much. The shows that are hosted by 'host1 and host2'
# should have already been processed during the copying of the MySQL database
# to PostgreSQL (though it might not be 100% reliable!).
#
./add_hosts_to_show -noprompt -show=1777 -host='Andrew Conway'
./add_hosts_to_show -noprompt -show=1788 -host='Dave Morriss'
./add_hosts_to_show -noprompt -show=1868 -host=kevie -host='Andrew Conway'
./add_hosts_to_show -noprompt -show=2036 -host=kevie
./add_hosts_to_show -noprompt -show=2116 -host=inscius # -host=chalkahlom
./add_hosts_to_show -noprompt -show=2297 -host='Andrew Conway'
./add_hosts_to_show -noprompt -show=2852 -host='b-yeezi'
./add_hosts_to_show -noprompt -show=2905 -host='MrX'
# vim: syntax=sh:ts=8:sw=4:ai:et:tw=78:fo=tcrqn21

File diff suppressed because it is too large Load Diff