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 oftext
,integer
,boolean
,date
,decimal
,time
$required
Whether this input is required, or whether it can return an empty value. Default0
$showtype
Whether to show the user what data type is expected, in parentheses. Default1
$showdefault
Whether to show the user what the default value is set to, in square brackets. Default1
$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 oftext
,integer
,boolean
,date
,decimal
,time
. Defaults totext
.
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 to1
.
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 to1
.
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 to0
.$log
Write an event to the database log. Defaults to1
.
Returns
Primary key of inserted row
notimplemented
Print a warning that this command/subcommand is not yet implemented
Usage
¬implemented
Arguments
None
Returns
Nothing
nocommand
Print list of available top-level commands
Usage
&nocommand(\%handlers);
Arguments
$handlers
reference to hash of handlers fromhandlers.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 fromhandlers.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 totext
$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 returnundef
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 to0
$char
character to use to signal that you want to enter a new row, ifinserthandler
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 asNow 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 to0
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 to18
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
or10/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 fromminfocal
,maxfocal
,zoom
oraperture
.
Returns
If
$param
is specified, returns the value of this parameter as a stringIf
$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
, or3"
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. CurrentlyADD
orEDIT
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
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 providedesc
, a description of the action,handler
, a function reference to a suitable handler, andid
, 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'