Functions

This package provides reusable functions to be consumed by the rest of the PhotoDB application.

Note that some of these functions take traditional argument lists which must be in order, while the more complex functions take a hashref of arguments which can be passed in any order. Examples of each function are given.

prompt

Prompt the user for an arbitrary value. Has various options for data validation and customisation of the prompt. If the provided input fails validation, or if a blank string is given when required=1 then the prompt is repeated.

Usage

my $camera = &prompt({prompt=>'What model is the camera?', required=>1, default=>$$defaults{model}, type=>'text'});

would give a prompt like

What model is the camera? (text) []:

Arguments

  • $default Default value that will be used if no input from user. Default empty string.

  • $prompt Prompt message for the user

  • $type Data type that this input expects, out of text, integer, boolean, date, decimal, time

  • $required Whether this input is required, or whether it can return an empty value. Default 0

  • $showtype Whether to show the user what data type is expected, in parentheses. Default 1

  • $showdefault Whether to show the user what the default value is set to, in square brackets. Default 1

  • $char Character to print at the end of the prompt. Defaults to :

Returns

The value the user provided

term

Set up a terminal object for use by PhotoDB

Usage

my $term = &term;

Arguments

None

Returns

Terminal object

validate

Validate that a value is a certain data type

Usage

my $result = &validate({val => 'hello', type => 'text'});

Arguments

  • $val The value to be validated

  • $type Data type to validate as, out of text, integer, boolean, date, decimal, time. Defaults to text.

Returns

Returns 1 if the value passes validation as the requested type, and 0 if it doesn't.

ini

Find PhotoDB config ini file

Usage

my $ini = &ini;

Arguments

None

Returns

File path to the config ini file

db

Connect to the database, run migrations and return database handle

Usage

my $db = &db;

Arguments

None

Returns

Variable representing the database handle

runmigrations

Run database migrations

Usage

&runmigrations($db);

Arguments

  • $db DB handle

Returns

Nothing

updaterecord

Update an existing record in any table

Usage

my $rows = &updaterecord({db=>$db, data=>\%data, table=>'FILM', where=>{film_id=>$film_id}});

Arguments

  • $db DB handle

  • $data Hash of new values to update

  • $table Name of table to update

  • $where Where clause, formatted for SQL::Abstract

  • $silent Suppress output

  • $log Write an event to the database log. Defaults to 1.

Returns

The number of rows updated

deleterecord

Delete an existing record from any table

Usage

my $rows = &deleterecord({db=>$db, table=>'FILM', where=>{film_id=>$film_id}});

Arguments

  • $db DB handle

  • $table Name of table to delete from

  • $where Where clause, formatted for SQL::Abstract

  • $silent Suppress output

  • $log Write an event to the database log. Defaults to 1.

Returns

The number of rows deleted

newrecord

Insert a record into any table

Usage

my $id = &newrecord({db=>$db, data=>\%data, table=>'FILM'});

Arguments

  • $db DB handle

  • $data reference to hash of new values to insert

  • $table Name of table to insert into

  • $silent Suppress user output and don't ask for confirmation. Defaults to 0.

  • $log Write an event to the database log. Defaults to 1.

Returns

Primary key of inserted row

notimplemented

Print a warning that this command/subcommand is not yet implemented

Usage

&notimplemented

Arguments

None

Returns

Nothing

nocommand

Print list of available top-level commands

Usage

&nocommand(\%handlers);

Arguments

  • $handlers reference to hash of handlers from handlers.pm

Returns

Nothing

nosubcommand

Print list of available subcommands for a given command

Usage

&nosubcommand(\%{$handlers{$command}}, $command);

Arguments

  • $command name of command whose subcommands you want

  • $handlers reference to hash slice of handlers from handlers.pm

Returns

Nothing

listchoices

List arbitrary choices from the DB and return ID of the selected one

Usage

my $id = &listchoices({db=>$db, table=>$table, where=>$where});

Arguments

  • $db DB handle

  • $query (legacy) the SQL to generate the list of choices

  • $type Data type of choice to be made. Defaults to text

  • $inserthandler function ref to handler that can be used to insert a new row if necessary

  • $default ID of default choice

  • $autodefault if default not set, count number of allowed options and if there's just 1, make it the default

  • $skipok whether it is ok to return undef if there are no options to choose from

  • $table table to run query against. Part of the SQL::Abstract tuple

  • $cols columns to select for the ID and the description. Defaults to ('id', 'opt). Part of the SQL::Abstract tuple

  • $where where clause passed in as a hash, e.g. {'field'='value'}>. Part of the SQL::Abstract tuple

  • $keyword keyword to describe the thing being chosen, e.g. camera. Defaults to attempting to figure it out with &keyword

  • $required whether this is a required choice, or whether we allow the user to enter an empty input. Defaults to 0

  • $char character to use to signal that you want to enter a new row, if inserthandler is set. Defaults to +

Returns

ID of the selected option

multiplechoice

Choose from a number of options expressed as an array, and return the index of the chosen option

Usage

my @choices = [
    { desc => 'Do nothing' },
    { desc => 'Also do nothing' },
];

my $action = &multiplechoice({choices => \@choices});

Arguments

  • $choices array of hashes of options

Returns

Integer of the chosen option

printlist

Print arbitrary rows from the database as an easy way of displaying data

Usage

&printlist({db=>$db, msg=>"prints from negative $neg_id", table=>'info_print', where=>{`Negative ID`=>$neg_id}});

Arguments

  • $db DB handle

  • $msg Message to display to user to describe what is being displayed. Shows up as Now showing $msg\n

  • $table Table to select from. Part of the SQL::Abstract tuple

  • $cols Columns to display. Defaults to (id, opt). Part of the SQL::Abstract tuple

  • $where Where clause for the query. Part of the SQL::Abstract tuple

  • $order Order by clause for the query. Part of the SQL::Abstract tuple

Returns

Integer representing the number of rows printed

lookupcol

Return values from an arbitrary column from database as an arrayref

Usage

my $existing = &lookupcol({db=>$db, table=>'CAMERA', where=>{camera_id=>$camera_id}});

Arguments

  • $db DB handle

  • $query (legacy) bare SQL query to run

  • $table table to run query against. Part of the SQL::Abstract tuple

  • $cols columns to select for the ID and the description. Defaults to *. Part of the SQL::Abstract tuple

  • $where where clause passed in as a hash, e.g. {'field'='value'}>. Part of the SQL::Abstract tuple

Returns

An arrayref containing a hashref of columns and values

thin

Thin out keys with empty values from a sparse hash

Usage

$data = &thin($data);

Arguments

  • $data Hashref containing data to be thinned

Returns

Hashref containing thinned data

lookupval

Return arbitrary single value from database

Usage

my $info = &lookupval({db=>$db, col=>'notes', table=>'FILM', where=>{film_id=>$film_id}});

Arguments

  • $db DB handle

  • $query (legacy) bare SQL query to run

  • $table table to run query against. Part of the SQL::Abstract tuple

  • $col column to select. Part of the SQL::Abstract tuple

  • $where where clause passed in as a hash, e.g. {'field'='value'}>. Part of the SQL::Abstract tuple

Returns

Single value from the database

call

Call a stored procedure from the database

Usage

&call({db=>$db, procedure=>'print_unarchive', args=>['123']});

Arguments

  • $db DB handle

  • $procedure name of the database stored procedure to call

  • $args arrayref of arguments to pass to the stored procedure

Returns

Number of affected rows

lookuplist

Return multiple values from a single database column as an arrayref

Usage

my $values = &lookuplist({db=>$db, col=>$column, table=>$table, where{key=>value}});

Arguments

  • $db DB handle

  • $table table to run query against. Part of the SQL::Abstract tuple

  • $col column to select. Part of the SQL::Abstract tuple

  • $where where clause passed in as a hash, e.g. {'field'='value'}>. Part of the SQL::Abstract tuple

Returns

An arreyref containing a list of values

today

Return today's date according to the DB

Usage

my $todaysdate = &today;

Arguments

  • $db DB handle

Returns

Today's date, formatted YYYY-MM-DD

now

Return an SQL-formatted timestamp for the current time

Usage

my $time = &now;

Arguments

  • $db Database handle

Returns

String containing the current time, formatted YYYY-MM-DD HH:MM:SS

friendlybool

Translate "friendly" bools to integers so we can accept human input and map it to binary boolean values. y/yes/true/1 map to 1 and n/no/false/0 map to 0. See also &printbool.

Usage

my $binarybool = &friendlybool($friendlybool);

Arguments

  • $friendlybool string representation of a boolean, e.g. yes, y, true, 1, no, n, false, 0, etc

Returns

1 if $bool represents a true value and 0 if it represents a false value

printbool

Translate numeric bools to strings for friendly printing of user messages. See also &friendlybool.

Usage

my $string = &printbool($bool);

Arguments

  • $bool boolean value to rewrite

Returns

Returns yes if $bool is true and no if $bool is false.

writeconfig

Write out an initial config file by prompting the user interactively.

Usage

&writeconfig($path);

Arguments

  • $path path to the config file that should be written

Returns

Nothing

round

Round a number to any precision

Usage

my $rounded = &round($num, 3);

Arguments

  • $num Number to round

  • $pow10 Number of decimal places to round to. Defaults to 0 i.e. round to an integer

Returns

Rounded number

pad

Pad a string with spaces up to a fixed length, to make it easier to print fixed-width tables

Usage

my $paddedstring = &pad('Hello', 8);

Arguments

  • $string Text to pad

  • $totallength Total number of characters to pad to, defaults to 18

Returns

Padded string

resolvenegid

Get a negative ID either from the neg ID or the film/frame ID

Usage

my $negID = &resolvenegid({db=>$db, string=>'10/4'});

Arguments

  • $db DB handle

  • $string String to represent a negative ID, either as an integer or in film/frame format, e.g. 834 or 10/4

Returns

Integer negative ID

chooseneg

Select a negative by drilling down

Usage

my $id = &chooseneg({db=>$db, oktoreturnundef=>$oktoreturnundef});

Arguments

  • $db variable containing database handle as returned by &db

  • $oktoreturnundef optional boolean to specify whether it is OK to fail to find a negative

Returns

Integer representing the negative ID

annotatefilm

Write out a text file in the film scans directory

Usage

&annotatefilm({db=>$db, film_id=>$film_id});

Arguments

  • $db variable containing database handle as returned by &db

  • $film_id integer variable containing ID of the film to be annotated

Returns

Nothing

keyword

Figure out the human-readable keyword of an SQL statement, e.g. statements that select from CAMERA or choose_camera would return camera. Selecting from CAMERA_MOUNT or choose_camera_mount would return camera mount. This can be helpful when automating user messages.

Usage

my $keyword = &keyword($query);

Arguments

  • $query an SQL statement, e.g. SELECT * FROM CAMERA;

Returns

A human-readable keyword representing the "subject" of the SQL query

parselensmodel

Parse lens model name to guess some data about the lens. Either specify which parameter you want to be returned as a string, or expect a hashref of all params to be returned. Currently supports guessing minfocal (minimum focal length), maxfocal (maximum focal length), zoom (whether this is a zoom lens) and aperture (maximum aperture of lens).

Usage

my $aperture = &parselensmodel($model, 'aperture');
my $lensparams = &parselensmodel($model);

Arguments

  • $model Model name of the lens

  • $param The name of the desired parameter. Optional, choose from minfocal, maxfocal, zoom or aperture.

Returns

  • If $param is specified, returns the value of this parameter as a string

  • If $param is undefined, returns a hashref of all parameters

unsetdisplaylens

Unassociate a display lens from a camera by passing in either the camera ID or the lens ID. It is not harmful to pass in both, but it is pointless.

Usage

&unsetdisplaylens({db=>$db, camera_id=>$camera_id});
&unsetdisplaylens({db=>$db, lens_id=>$lens_id});

Arguments

  • $db DB handle

  • $camera_id ID of camera whose display lens you want to unassociate

  • $lens_id ID of lens you want to unassociate

Returns

Result of SQL update

welcome

Print a friendly welcome message

Usage

&welcome;

Arguments

None

Returns

Nothing

duration

Calculate duration of a shutter speed from its string representation

Usage

my $duration = &duration($shutter_speed);

Arguments

  • $shutter_speed string containing a representation of a shutter speed, e.g. 1/125, 0.7, 3, or 3"

Returns

Numeric representation of the duration of the shutter speed, e.g. 0.05

tag

This func reads data from PhotoDB and writes EXIF tags to the JPGs that have been scanned from negatives

Usage

&tag({db=>$db, where=>$where});
&tag({db=>$db, where=>{film_id=1}});
&tag({db=>$db, where=>{negative_id=100}});

Arguments

  • $db DB handle

  • $where hash to specify which scans should be tagged. Tags all scans if not set!

Returns

Nothing

hashdiff

Compare new and old data to find changed keys.

Usage

my $diff = &hashdiff(\%old, \%new);
my $diff = &hashdiff($old, $new);

Arguments

  • $old hashref of old values

  • $new hashref of new values

Returns

Hashref containing values that are new or different.

logger

Record a database event in the log

Usage

&logger({db=>$db, type=>$type, message=>$message});

Arguments

  • $db DB handle

  • $type Type of log message. Currently ADD or EDIT to reflect database changes.

  • $message Message to write to the log file

Returns

ID of the log message

choosescan

Select a scan by specifying a filename. Allows user to pick if there are multiple matching filenames.

Usage

my $id = &choosescan({db=>$db});

Arguments

  • $db variable containing database handle as returned by &db

Returns

Integer representing the scan ID

basepath

Returns filesystem basepath which contains scans

Usage

my $basepath = &basepath;

Arguments

None

Returns

Path to directory which contains scans

untaint

Untaint a tainted value

Usage

my $untainted = &untaint($tainted);

Arguments

  • $tainted Tainted value to untaint

Returns

Returns the untained string

fsfiles

List all scan files on the filesystem

Usage

my @scansondisk = &fsfiles;

Arguments

None

Returns

Array of file paths of scans found on the filesystem

dbfiles

List all scan files in the database

Usage

my @scansindb = &dbfiles;

Arguments

  • $db database handle

Returns Array of file paths of scans recorded in the database

unsci

DBD returns integer zero in scientific format as 0E0. This rewrites it.

Usage

$int = &unsci($int);

Arguments

  • $int an integer returned by DBD

Returns

The same integer as passed in, except with string 0E0 rewritten as integer 0

Search for objects in the database

Usage

my $id = &search({
    db         => $db,
    table      => 'choose_camera',
    searchterm => $searchterm,
    choices    => [
        { desc => 'Do nothing' },
        { desc => 'Get camera info', handler => \&camera_info, id=>'camera_id', },
        { desc => 'Load a film', handler => \&film_load, id=>'camera_id', },
        { desc => 'Sell this camera', handler => \&camera_sell, id=>'camera_id', }
    ],
});

Arguments

  • $db database handle

  • $table name of table or view to search in

  • $keyword keyword to describe the thing being chosen, e.g. camera. Defaults to attempting to figure it out with &keyword

  • $searchterm string to search for in the database

  • $cols = pair of columns where the first will be returned as the matched ID and the second is the column to be searched in. Defaults to ['id', 'opt']

  • $where where clause for the search. Defaults to "opt like '%$searchterm%' collate utf8mb4_general_ci"

  • $choices arrayref to an array of hashes which represent actions to be taken on a located item. You must provide desc, a description of the action, handler, a function reference to a suitable handler, and id, the name of the parameter to use to pass in the ID located object.

Returns

ID of located object

tabulate

Display multi-column SQL views as tabulated data.

Usage

&tabulate({db=>$db, view=>$view});

Arguments

  • $db database handle

  • $view name of SQL view to print

  • $cols columns of view to return. Defaults to *

  • $where optional WHERE clause

Returns

Number of rows displayed

canondatecode

Decode Canon datecodes to discover the year of manufacture. Datecodes are sometimes ambiguous so by passing in the dates that the model was introduced and discontinued, the year of manufacture can be pinned down.

Usage

my $manufactured = &canondatecode({datecode=>$datecode, introduced=>$introduced, discontinued=>$discontinued});

Arguments

  • $datecode the datecode to decode

  • $introduced year that the model was introduced. Assumes 1800 if not defined.

  • $discontinued year that the model was discontinued. Assumes 2100 if not defined.

Returns

Year of manufacture if the decoding was successful, otherwise undef

choose_shutterspeed

While entering a negative into a film, prompt the user to select an available shutter speed for the camera in use. If they choose B or T, prompt them for the duration in seconds, and return that instead. Also add it to the SHUTTER_SPEED_AVAILABLE table, marked as a "bulb" speed if necessary.

Usage

my $shutter_speed = &choose_shutterspeed({db=>$db, film_id=>$film_id});

Arguments

  • $db DB handle

  • $film_id Film ID that we are inserting into, so the camera can be found

Returns

String representation of a shutter speed, which is both a valid EXIF representation, and also a valid data object.

84 POD Errors

The following errors were encountered while parsing the POD:

Around line 47:

'=item' outside of any '=over'

Around line 61:

You forgot a '=back' before '=head4'

Around line 149:

'=item' outside of any '=over'

Around line 153:

You forgot a '=back' before '=head4'

Around line 326:

'=item' outside of any '=over'

Around line 328:

You forgot a '=back' before '=head4'

Around line 362:

'=item' outside of any '=over'

Around line 374:

You forgot a '=back' before '=head4'

Around line 449:

'=item' outside of any '=over'

Around line 459:

You forgot a '=back' before '=head4'

Around line 520:

'=item' outside of any '=over'

Around line 530:

You forgot a '=back' before '=head4'

Around line 617:

'=item' outside of any '=over'

Around line 619:

You forgot a '=back' before '=head4'

Around line 645:

'=item' outside of any '=over'

Around line 649:

You forgot a '=back' before '=head4'

Around line 674:

'=item' outside of any '=over'

Around line 700:

You forgot a '=back' before '=head4'

Around line 817:

'=item' outside of any '=over'

Around line 819:

You forgot a '=back' before '=head4'

Around line 856:

'=item' outside of any '=over'

Around line 868:

You forgot a '=back' before '=head4'

Around line 918:

'=item' outside of any '=over'

Around line 928:

You forgot a '=back' before '=head4'

Around line 979:

'=item' outside of any '=over'

Around line 981:

You forgot a '=back' before '=head4'

Around line 1005:

'=item' outside of any '=over'

Around line 1015:

You forgot a '=back' before '=head4'

Around line 1063:

'=item' outside of any '=over'

Around line 1069:

You forgot a '=back' before '=head4'

Around line 1104:

'=item' outside of any '=over'

Around line 1112:

You forgot a '=back' before '=head4'

Around line 1158:

'=item' outside of any '=over'

Around line 1160:

You forgot a '=back' before '=head4'

Around line 1180:

'=item' outside of any '=over'

Around line 1182:

You forgot a '=back' before '=head4'

Around line 1207:

'=item' outside of any '=over'

Around line 1209:

You forgot a '=back' before '=head4'

Around line 1237:

'=item' outside of any '=over'

Around line 1239:

You forgot a '=back' before '=head4'

Around line 1266:

'=item' outside of any '=over'

Around line 1268:

You forgot a '=back' before '=head4'

Around line 1311:

'=item' outside of any '=over'

Around line 1315:

You forgot a '=back' before '=head4'

Around line 1338:

'=item' outside of any '=over'

Around line 1342:

You forgot a '=back' before '=head4'

Around line 1377:

'=item' outside of any '=over'

Around line 1381:

You forgot a '=back' before '=head4'

Around line 1416:

'=item' outside of any '=over'

Around line 1420:

You forgot a '=back' before '=head4'

Around line 1456:

'=item' outside of any '=over'

Around line 1460:

You forgot a '=back' before '=head4'

Around line 1544:

'=item' outside of any '=over'

Around line 1546:

You forgot a '=back' before '=head4'

Around line 1581:

'=item' outside of any '=over'

Around line 1585:

You forgot a '=back' before '=head4'

Around line 1587:

'=item' outside of any '=over'

Around line 1622:

You forgot a '=back' before '=head2'

Around line 1634:

'=item' outside of any '=over'

Around line 1640:

You forgot a '=back' before '=head4'

Around line 1704:

'=item' outside of any '=over'

Around line 1706:

You forgot a '=back' before '=head4'

Around line 1738:

'=item' outside of any '=over'

Around line 1742:

You forgot a '=back' before '=head4'

Around line 1879:

'=item' outside of any '=over'

Around line 1883:

You forgot a '=back' before '=head4'

Around line 1919:

'=item' outside of any '=over'

Around line 1925:

You forgot a '=back' before '=head4'

Around line 1950:

'=item' outside of any '=over'

Around line 1952:

You forgot a '=back' before '=head4'

Around line 2011:

'=item' outside of any '=over'

Around line 2013:

You forgot a '=back' before '=head4'

Around line 2068:

'=item' outside of any '=over'

Around line 2070:

You forgot a '=back' before '=head4'

Around line 2100:

'=item' outside of any '=over'

Around line 2102:

You forgot a '=back' before '=head4'

Around line 2136:

'=item' outside of any '=over'

Around line 2150:

You forgot a '=back' before '=head4'

Around line 2208:

'=item' outside of any '=over'

Around line 2216:

You forgot a '=back' before '=head4'

Around line 2260:

'=item' outside of any '=over'

Around line 2266:

You forgot a '=back' before '=head4'

Around line 2366:

'=item' outside of any '=over'

Around line 2370:

You forgot a '=back' before '=head4'