diff --git a/README.md b/README.md
index 2af3cf9042dd46dddf3dd9f96f1015e43a3a432f..3d2fb92394b83c465e461b60fd90b9e37b51e3e6 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,7 @@ - week
- hour
- filtering of data by
- time range of photos shot
+ - values in fields in "CONTAINS" and "NOT CONTAINS" mode
- custom sorting of output by arbitrary fields
- output results in a table view to your terminal
@@ -102,13 +103,17 @@ only media files which match (case-insensitive!) the given extensions are added to the database
This option is saved to the user conf
stats querying:
- -g : group by time range, defaults to total (which means no grouping by time range)
+ -g <period> : group by a time period, defaults to total (which means no grouping by period)
allowed values: year, month, week, hour
-s <fields> : specify the information you want to select, defaults to none (just show number of images)
allowed values: maker, model, lensmake, lens, aperture, exposuretime, iso, focallength, focallength35mm
multiple fields should be listed comma-separated
-t <range> : only take images into account which have been taken in the given timerange
<range> must be specified as 'YYYYMMDD-YYYYMMDD', you can omit one value
+ -f <expr> : filter images based on the given expression, multiple expression can be given comma-separated
+ this works as a case-insensitive CONTAINS search, multiple expressions need to match all (AND logic)
+ use "field=value" for CONTAINS and "field!=value" for NOT CONTAINS, value must not include whitespaces
+ allowed fields: file, maker, model, lensmake, lens, aperture, exposuretime, iso, focallength, focallength35mm
-n <number> : limit the resultset to <number> of lines
-o <fields> : sort your output by the given fields (sequence matters!) in descending order
allowed values: any comma separated combination of the values of -t and -s param and 'count'
@@ -119,10 +124,13 @@ phosta -E jpg,jpeg,tiff -D ~/Documents/stats.db -p ~/Pictures
load EXIF data of files with the extensions jpg, jpeg and tiff in folder ~/Pictures to the database located in ~/Documents/stats.db
phosta -s model -g month -t 20190101-20121231 -o month,count
- show number of pictures taken with a specific camera body in 2019 grouped by month, sorted by newest month first
+ show number of pictures taken in with a specific camera body in 2019 grouped by month, sorted by newest month first
phosta -n 10 -s lens -o count
- show top 10 lenses used the most over all pictures taken
+ show top 10 lenses used the most
+
+ phosta -s model -f maker!=ricoh
+ show camera models where the maker field does not contain the term "ricoh"
```
## FAQ
diff --git a/phosta.pl b/phosta.pl
index 16fed2a629d86cead5a702a87c686e2aa634fae1..769ca38153e9d3fce13097413b1f64f413af9a1d 100755
--- a/phosta.pl
+++ b/phosta.pl
@@ -17,7 +17,7 @@ use File::HomeDir;
$Getopt::Std::STANDARD_HELP_VERSION = 'true';
-my $VERSION = '0.8';
+my $VERSION = '0.9';
my $PROGRAM = 'Photo Stats';
my $configfile = catfile(File::HomeDir->my_home, '.phosta.conf');
@@ -34,10 +34,11 @@ our $opt_v=0;
our $opt_n=undef;
our $opt_o='count';
our $opt_r=0;
+our $opt_f=undef;
getconfig($configfile);
-getopts('vcrp:n:g:s:t:D:E:o:') or die "Invalid parameters provided! See 'phosta --help' for more details.";
+getopts('vcrp:n:g:s:t:D:E:o:f:') or die "Invalid parameters provided! See 'phosta --help' for more details.";
validate() or die "Invalid parameters provided! See 'phosta --help' for more details.";
my $dsn = "DBI:SQLite:dbname=$opt_D";
@@ -83,6 +84,7 @@ !defined($opt_t) || $opt_t =~ /^([0-9]{8}){0,1}\-([0-9]{8}){0,1}$/ or return 0;
$opt_E =~ /^([a-z]{2,4}){1,}(\,[a-z]{2,4}){0,}$/ or return 0;
+ !defined($opt_f) || $opt_f =~ /^[\w]{2,}(!){0,1}=[\w]{1,}(,[\w]{2,}(!){0,1}=[\w]{1,}){0,}$/ or return 0;
return 1;
}
@@ -139,7 +141,7 @@ if ( $lens ~~ @forbidden_content) { $lens = '-'; }
$apert = ($apert ne '-') ? sprintf("%.1f", $apert) : $apert;
$fl = ($fl ne '-') ? sprintf("%.1f", $fl) : $fl;
$fl35 = ($fl35 ne '-') ? POSIX::lround($fl35) : $fl35;
- $exposuretime = (looks_like_number($exposuretime) && $exposuretime < 1) ? "1/". POSIX::lround(1/$exposuretime) : $exposuretime;
+ $exposuretime = (looks_like_number($exposuretime) && $exposuretime < 1 && $exposuretime != 0) ? "1/". POSIX::lround(1/$exposuretime) : $exposuretime;
if ($datetimeoriginal eq '0000:00:00 00:00:00') { $datetimeoriginal = '-'; }
my $stmt = "INSERT OR REPLACE INTO photos (file, maker, model, lensmake, lens, focallength, focallength35mm, aperture, exposuretime, iso, flash, datetimeoriginal)
@@ -154,40 +156,40 @@ }
sub get_sql
{
- my ($selected, $grouping, $order, $timerange) = @_;
+ my ($selected, $grouping, $order, $timerange, $filter) = @_;
my $fieldlist = '';
- my $grouplist = '';
+ my @grouparray;
my $orderlist = '';
- my $wherelist = '';
+ my @wherearray;
given ($grouping)
{
when ('month')
{
$fieldlist = "IFNULL(strftime('%Y/%m', datetimeoriginal), '-') as month,";
- $grouplist = "strftime('%Y/%m', datetimeoriginal)";
+ push @grouparray, "strftime('%Y/%m', datetimeoriginal)";
}
when ('week')
{
$fieldlist = "IFNULL(strftime('%Y/%W', datetimeoriginal), '-') as week,";
- $grouplist = "strftime('%Y/%W', datetimeoriginal)";
+ push @grouparray, "strftime('%Y/%W', datetimeoriginal)";
}
when ('year')
{
$fieldlist = "IFNULL(strftime('%Y', datetimeoriginal), '-') as year,";
- $grouplist = "strftime('%Y', datetimeoriginal)";
+ push @grouparray, "strftime('%Y', datetimeoriginal)";
}
when ('hour')
{
$fieldlist = "IFNULL(strftime('%H', datetimeoriginal), '-') as hour,";
- $grouplist = "strftime('%H', datetimeoriginal)";
+ push @grouparray, "strftime('%H', datetimeoriginal)";
}
}
if (defined($selected))
{
$fieldlist = $fieldlist . $selected . ', ';
- $grouplist .= ($grouplist ne '' ? ',' : '') . $selected;
+ push @grouparray, $selected;
}
if (defined($order))
@@ -202,16 +204,28 @@ {
my ($from, $to) = split(/\-/, $timerange);
if ($from ne '')
{
- $wherelist = 'datetimeoriginal >= \''. substr($from, 0, 4) .'-'. substr($from, 4, 2) .'-'. substr($from, 6, 2) .'\'';
+ push @wherearray, 'datetimeoriginal >= \''. substr($from, 0, 4) .'-'. substr($from, 4, 2) .'-'. substr($from, 6, 2) .'\'';
}
if ($to ne '')
{
- $wherelist .= ($wherelist ne '' ? ' AND ' : '') . 'datetimeoriginal <= \''. substr($to, 0, 4) .'-'. substr($to, 4, 2) .'-'. substr($to, 6, 2) .'\'';
+ push @wherearray, 'datetimeoriginal <= \''. substr($to, 0, 4) .'-'. substr($to, 4, 2) .'-'. substr($to, 6, 2) .'\'';
+ }
+ }
+
+ if (defined($filter))
+ {
+ my (@filters) = split /\,/, $filter;
+ foreach (@filters)
+ {
+ my ( $sub1, $sub2, $sub3 ) = split /(!){0,1}=/, $_;
+ push @wherearray, $sub1 . ($_ =~ /!/ ? ' NOT ' : '') . " LIKE '%$sub3%'";
}
}
- if ($grouplist ne '') { $grouplist = 'GROUP BY '. $grouplist; }
- if ($wherelist ne '') { $wherelist = 'WHERE ' . $wherelist; }
+ my $wherelist = '';
+ my $grouplist = '';
+ if (scalar @grouparray) { $grouplist = 'GROUP BY '. join(', ', @grouparray); }
+ if (scalar @wherearray) { $wherelist = 'WHERE ' . join(' AND ', @wherearray); }
return "SELECT $fieldlist count(file) as count FROM photos $wherelist $grouplist $orderlist";
}
@@ -225,9 +239,9 @@
say "Querying database $opt_D with $total_count entries...";
say '';
$total_count > 0 or return;
-
- if ($opt_v) { say '### SQL Statement: '. get_sql($opt_s, $opt_g, $opt_o, $opt_t); }
- my $stmt = $dbh->prepare(get_sql($opt_s, $opt_g, $opt_o, $opt_t));
+ my $sql = get_sql($opt_s, $opt_g, $opt_o, $opt_t, $opt_f);
+ if ($opt_v) { say '### SQL Statement: '. $sql; }
+ my $stmt = $dbh->prepare($sql);
$stmt->execute();
my $rows = $stmt->fetchall_arrayref;
@@ -317,6 +331,10 @@ say ' allowed values: maker, model, lensmake, lens, aperture, exposuretime, iso, focallength, focallength35mm';
say ' multiple fields should be listed comma-separated';
say ' -t <range> : only take images into account which have been taken in the given timerange';
say ' <range> must be specified as \'YYYYMMDD-YYYYMMDD\', you can omit one value';
+ say ' -f <expr> : filter images based on the given expression, multiple expression can be given comma-separated';
+ say ' this works as a case-insensitive CONTAINS search, multiple expressions need to match all (AND logic)';
+ say ' use "field=value" for CONTAINS and "field!=value" for NOT CONTAINS, value must not include whitespaces';
+ say ' allowed fields: file, maker, model, lensmake, lens, aperture, exposuretime, iso, focallength, focallength35mm';
say ' -n <number> : limit the resultset to <number> of lines';
say ' -o <fields> : sort your output by the given fields (sequence matters!) in descending order';
say ' allowed values: any comma separated combination of the values of -t and -s param and \'count\'';
@@ -331,4 +349,7 @@ say ' show number of pictures taken in with a specific camera body in 2019 grouped by month, sorted by newest month first';
say '';
say ' phosta -n 10 -s lens -o count';
say ' show top 10 lenses used the most';
+ say '';
+ say ' phosta -s model -f maker!=ricoh';
+ say ' show camera models where the maker field does not contain the term "ricoh"';
}