Koha-clms/Koha/Notice/Util.pm

149 lines
4.7 KiB
Perl

package Koha::Notice::Util;
# Copyright Rijksmuseum 2023
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha 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. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <https://www.gnu.org/licenses>.
use Modern::Perl;
use Data::Dumper qw/Dumper/;
use C4::Context;
use Koha::DateUtils qw/dt_from_string/;
use Koha::Notice::Messages;
=head1 NAME
Koha::Notice::Util - Utility class related to Koha notice messages
=head1 CLASS METHODS
=head2 load_domain_limits
my $domain_limits = Koha::Notice::Util->load_domain_limits;
=cut
sub load_domain_limits {
my ($class) = @_;
my $domain_limits;
my $entry = C4::Context->config('message_domain_limits');
if ( ref($entry) eq 'HASH' ) {
if ( exists $entry->{domain} ) {
# Turn single hash entry into array
$domain_limits =
ref( $entry->{domain} ) eq 'HASH'
? [ $entry->{domain} ]
: $entry->{domain};
# Convert to hash structure by domain name
$domain_limits = { map { _init_domain_entry($_); } @$domain_limits };
}
}
return $domain_limits;
}
sub _init_domain_entry {
my ($config_entry) = @_;
# Return either a hash like ( name => { limit => , unit =>, count => } ) for regular entries
# or return a hash like ( name => { belongs_to => } ) for a domain that is part of a group
return if ref($config_entry) ne 'HASH' || !exists $config_entry->{name};
my $elements;
if ( $config_entry->{belongs_to} ) {
$elements = { belongs_to => lc $config_entry->{belongs_to} };
} else {
$elements = { limit => $config_entry->{limit}, unit => $config_entry->{unit}, count => undef };
}
return ( lc $config_entry->{name}, $elements );
}
=head2 exceeds_limit
my $boolean = Koha::Notice::Util->exceeds_limit({ to => $to_address, limits => $domain_limits, incr => 1|0 });
=cut
sub exceeds_limit {
my ( $class, $params ) = @_;
my $domain_limits = $params->{limits} or return 0; # no limits at all
my $to_address = $params->{to} or return 0; # no address, no limit exceeded
my $incr = $params->{incr} // 1; # by default we increment
my $domain = q{};
$domain = lc $1 if $to_address && $to_address =~ /@(\H+)/;
return 0 if !$domain || !exists $domain_limits->{$domain};
# Keep in mind that domain may be part of group count
my $group = $domain_limits->{$domain}->{belongs_to} // $domain;
_get_domain_count( $domain, $group, $domain_limits ) if !defined $domain_limits->{$group}->{count};
return 1 if $domain_limits->{$group}->{count} >= $domain_limits->{$group}->{limit};
if ($incr) {
$domain_limits->{$group}->{count}++;
warn "Sending messages: domain $group reached limit of "
. $domain_limits->{$group}->{limit} . '/'
. $domain_limits->{$group}->{unit}
if $domain_limits->{$group}->{count} == $domain_limits->{$group}->{limit};
}
return 0;
}
=head1 PRIVATE METHODS
=cut
sub _get_domain_count {
my ( $domain, $group, $limits ) = @_;
# Check if there are group members too
my @domains;
push @domains, $domain if $domain eq $group;
push @domains, map {
my $belongs = $limits->{$_}->{belongs_to} // q{};
$belongs eq $group ? $_ : ();
} keys %$limits;
my $sum = 0;
my $start_dt = _convert_unit( undef, $limits->{$group}->{unit} );
foreach my $domain (@domains) {
$sum += Koha::Notice::Messages->search(
{
message_transport_type => 'email',
status => 'sent',
to_address => { 'LIKE', '%' . $domain },
}
)->filter_by_last_update( { timestamp_column_name => 'updated_on', from => $start_dt } )->count;
}
$limits->{$group}->{count} = $sum;
}
sub _convert_unit { # unit should be like \d+(m|h|d)
my ( $dt, $unit ) = @_;
$dt //= dt_from_string();
if ( $unit && $unit =~ /(\d+)([mhd])/ ) {
my $abbrev = { m => 'minutes', h => 'hours', d => 'days' };
foreach my $h ( 0, 1 ) { # try hour before too when subtract fails (like: change to summertime)
eval { $dt->subtract( hours => $h )->subtract( $abbrev->{$2} => $1 ) } and last;
}
}
return $dt;
}
1;