package LIMS::Validate::Roles::ConstraintMethods; # contains constraint_method helper methods for LIMS::Validate use Moose::Role; use namespace::clean -except => 'meta'; use Data::Dumper; use LIMS::Local::Utils; use FormValidator::Simple qw(+LIMS::Validate::DatePlugin); use Data::FormValidator::Constraints qw(:regexp_common FV_max_length FV_length_between valid_email); use Data::FormValidator::Constraints::DateTime qw(:all); # using: ymd_before_today() use Data::FormValidator::Constraints::MethodsFactory qw(:bool); # FV_and #------------------------------------------------------------------------------- sub check_nhsno_is_valid { my $self = shift; # warn 'here'; warn Dumper $self; return sub { my $dfv = shift; # warn 'here'; warn Dumper $dfv; my $nhs_number = shift || return 0; # warn 'NHSNo:'.$nhs_number; $dfv->name_this('is_valid'); # check_nhsno() return true if valid: my $is_valid = LIMS::Local::Utils::check_nhsno( $nhs_number ); # warn $is_valid; return $is_valid; } } #------------------------------------------------------------------------------- sub check_nhsno_is_unique { my $self = shift; return sub { my $dfv = shift; # warn 'here'; warn Dumper $dfv; my $nhs_number = shift || return 0; # warn 'NHSNo:'.$nhs_number; $dfv->name_this('is_unique'); my $validation_model = $self->models->{validate_nhs_number_unique}; my $not_unique = $validation_model->($nhs_number); return $not_unique ? 0 : 1; } } #------------------------------------------------------------------------------- sub check_email_is_valid { my $self = shift; return sub { my ($dfv, $email) = @_; # warn $email; return valid_email($email); # Data::FormValidator::Constraints method } } #------------------------------------------------------------------------------- sub check_post_code_is_valid { my $self = shift; return sub { my ($dfv, $zip) = @_; # warn $zip; return LIMS::Local::Utils::format_postcode($zip); } } #------------------------------------------------------------------------------- sub check_email_domain { my $self = shift; my $settings = $self->settings; my @valid_domains = split /\,\s?|\s+/, $settings->{valid_recipient_domains}; return sub { my ($dfv, $email) = @_; # warn $email; return grep $email =~ /$_\Z/, @valid_domains; } } #------------------------------------------------------------------------------- sub check_no_spaces { my $self = shift; return sub { my $dfv = shift; # warn 'here'; $dfv->name_this('no_spaces'); my $val = $dfv->get_current_constraint_value(); # warn $val; my ($is_valid) = $val =~ /^[a-zA-Z0-9_]+$/; # warn $is_valid; return $is_valid; } } #------------------------------------------------------------------------------- # used by new_request() & Ajax::_validate_params() sub get_request_number_constraints { my $self = shift; my $is_unique = sub { my $dfv = shift; # warn 'here'; $dfv->name_this('is_unique'); my $val = $dfv->get_current_constraint_value(); # warn $val; my $uniqueness = $self->check_request_number($val); return $uniqueness; }; return [ # TODO: join_these() doesn't work here $is_unique, # check it's unqiue FV_num_int, # must be integer; Regexp::Common, s/RE/FV/ $self->check_param_length( [1,5] ), # don't expect >99_999/yr # FV_clamp(1, 1, 5), # MethodsFactory method - no configurable msgs output ? ]; } #------------------------------------------------------------------------------- # used by new_request() & Ajax::_validate_params() sub get_request_number_messages { my $self = shift; return { # messages need to be set to constraint_names, not field_names: constraints => { is_unique => $self->messages->{not_unique}, FV_num_int => $self->messages->{need_integer}, length_between => $self->messages->{too_long}, }, }; } #------------------------------------------------------------------------------- # can have non-unique test_names, but not with same lab_section_id: =begin ? not in use sub check_lab_test_is_valid { my $self = shift; return sub { my $dfv = shift; $dfv->name_this('is_unique'); my $data = $dfv->get_filtered_data; # warn Dumper $data; my $validation_model = $self->models->{validate_lab_test}; my $is_valid = $validation_model->($data); return $is_valid; } } =cut #------------------------------------------------------------------------------- # used by new_request() & Ajax::_validate_params() sub get_specimen_constraints { my $self = shift; return sub { my $dfv = shift; # warn 'here'; $dfv->name_this('check_specimen'); my $val = $dfv->get_current_constraint_value(); # warn $val; my $is_valid = $self->check_specimen($val); # warn $is_valid; return $is_valid; } } #------------------------------------------------------------------------------- # checks entry(s) in specimen field exists in specimens table: # (not a DFV method so doesn't receive dfv object) sub check_specimen { # warn 'HERE'; warn Dumper @_; warn 'HERE'; my $self = shift; my $specimens = shift || return 0; # warn 'specimens:'.$specimens; # split input on comma and/or space(s): my @specimens = split /\,\s?|\s+/, $specimens; # $self->debug(\@specimens); my $validation_model = $self->models->{validate_specimen}; foreach my $specimen (@specimens) { my $valid = $validation_model->($specimen); # warn "$specimen:$valid"; return 0 if ! $valid; } # OK, specimen(s) valid: return 1; } #------------------------------------------------------------------------------- sub check_referrer_code { my $self = shift; return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('referrer_code'); my $val = $dfv->get_current_constraint_value(); # warn $val; # C, D, E or CS followed by 5-7 numbers: return ($val =~ /\A([CDE]|CS)\d{5,7}\Z/) ? 1 : 0; } } #------------------------------------------------------------------------------- sub check_request_number { my $self = shift; my $request_number = shift || return 0; # warn 'request_number:'.$request_number; my $validation_model = $self->models->{validate_request_number}; my $not_unique = $validation_model->($request_number); return $not_unique ? 0 : 1; } #------------------------------------------------------------------------------- sub get_date_constraints { my $self = shift; my $prefix = shift; # optional - to support forms with multiple dates eg dob, request date, etc my $constraint = FV_and( $self->check_four_digit_year($prefix), $self->check_date_is_valid($prefix), $self->check_date_not_future($prefix), ); return $constraint; } #------------------------------------------------------------------------------- sub get_change_password_profile { my $self = shift; return [ # FormValidator::Simple method new_password => [ 'NOT_BLANK', ['LENGTH', 5, 255 ] ], old_password => [ 'NOT_BLANK', ['LENGTH', 5, 255 ] ], new_password_confirm => [ 'NOT_BLANK', ['LENGTH', 5, 255 ] ], { pwds => ['new_password', 'new_password_confirm'] } => [ 'DUPLICATION' ], ]; } #------------------------------------------------------------------------------- # uses FV_and() to return constraint_method, which executes list in order & # exits on 1st failure, instead of executing all and potentially generating errs sub join_these { my $self = shift; return FV_and(@_); } #------------------------------------------------------------------------------- sub check_four_digit_year { my $self = shift; my $prefix = shift; # optional, used if form has >1 date type (eg request date, dob) my $year_field_name = $prefix ? $prefix.'_year' # eg request_year, dob_year : 'year'; return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('yyyy_format'); my $data = $dfv->get_filtered_data; # warn Dumper $data; return ( length $data->{$year_field_name} == 4 ); } } #------------------------------------------------------------------------------- sub check_numeric { my $self = shift; return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('numeric_format'); my $val = $dfv->get_current_constraint_value(); # warn $val; # digits only: my ($is_valid) = $val =~ /^[0-9]+$/; # warn $is_valid; return $is_valid; } } #------------------------------------------------------------------------------- # override MethodsFactory method until can find out how to target msgs at it sub _FV_clamp { my $self = shift; my ($result, $low, $high) = @_; return sub { my $dfv = shift; $dfv->name_this('FV_clamp'); my $val = $dfv->get_current_constraint_value(); return (($val < $low) or ($val > $high)) ? !$result : $result; } } #------------------------------------------------------------------------------- # or could use 'day => ymd_before_today( qw/year month day/ )' sub check_date_not_future { my $self = shift; my $prefix = shift; # optional, used if form has >1 date type (eg request date, dob) my $param_names = $self->_get_date_param_names($prefix); #arrayref return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('future_date'); my $data = $dfv->get_filtered_data; # warn Dumper $data; # returns error if date not less than DateTime->now: my @cmp = ( 'DATE_LESS_THAN', DateTime->now ); my $result = FormValidator::Simple->check( $data => [ { grouped => $param_names } => [ \@cmp ], ] ); # warn Dumper $result->has_error; return $result->has_error ? 0 : 1; } } #------------------------------------------------------------------------------- # if hidden flag '_diagnosis_id' passed, also need either 'revision' or # 'final_diagnosis' param: sub check_report_params { my $self = shift; return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('diagnosis_resubmission'); my $data = $dfv->get_filtered_data; # warn Dumper $data; my $original_diagnosis_id = $dfv->get_current_constraint_value(); # warn 'HERE'; warn $original_diagnosis_id; # eg authorisation, without change of diagnosis_id: return 1 if $original_diagnosis_id == $data->{diagnosis_id}; # require either 'final_diagnosis' or 'revision' params: return grep $data->{$_}, qw(final_diagnosis revision); } } #------------------------------------------------------------------------------- # # check end_date NOT before start_date - returns true (1) UNLESS $second_date # BEFORE $first_date: sub check_date_order { my $self = shift; return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('date_order'); my $data = $dfv->get_filtered_data; # warn Dumper $data; # LIMS::Local::Utils::check_chronological_order() requires DateTime objects: my $start_date = LIMS::Local::Utils::to_datetime_using_datecalc($data->{start_date}); my $end_date = LIMS::Local::Utils::to_datetime_using_datecalc($data->{end_date}); my $expr # returns '-1' if dates in chronological order (0 if equal, otherwise 1): = LIMS::Local::Utils::check_chronological_order($end_date, $start_date); # warn Dumper $expr; return $expr == -1 ? 0 : 1; # return false if $expr eq -1 ie in date order } } #------------------------------------------------------------------------------- sub check_date_is_valid { my $self = shift; my $prefix = shift; # optional, used if form has >1 date type (eg request date, dob) my $param_names = $self->_get_date_param_names($prefix); #arrayref return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('valid_date'); my $data = $dfv->get_filtered_data; # warn Dumper $data; my $result = FormValidator::Simple->check( $data => [ { date => $param_names } => [ 'DATE' ] ] ); # warn Dumper $result->has_error; return $result->has_error ? 0 : 1; }; } #------------------------------------------------------------------------------- sub check_EU_date_is_valid { my $self = shift; return sub { my $dfv = shift; # warn Dumper $dfv; $dfv->name_this('valid_date'); my $val = $dfv->get_current_constraint_value(); # warn $val; my $result = LIMS::Local::Utils::check_date($val); return $result; }; } #------------------------------------------------------------------------------- sub check_param_length { my $self = shift; my $arg = shift; # warn Dumper $arg; # if $arg is arrayref, assume length_between, otherwise max_length: return ref $arg eq 'ARRAY' ? FV_length_between(@$arg) : FV_max_length($arg); } #------------------------------------------------------------------------------- # checks param is either unique, or belongs to submitted record id: sub check_param_ownership { my $self = shift; my $tbl_info = shift; # arryref of 'col_name' & 'class' return sub { my $dfv = shift; # warn Dumper $dfv; my $data = $dfv->get_filtered_data; # warn Dumper $data; $dfv->name_this('ownership'); my %args = ( col => $tbl_info->[0], # can be arrayref of col_names class => $tbl_info->[1], data => $data, ); # warn Dumper \%args; # returns 'true' if param unique, or already belongs to record id: my $unique_or_belongs_to_record = $self->models->{validate_param_ownership}->(\%args); # so just return value of expression: return $unique_or_belongs_to_record; }; } #------------------------------------------------------------------------------- # allows for form to submit several date params, eg request_year, dob_year, etc sub _get_date_param_names { my $self = shift; my $prefix = shift; # optional, used if form has >1 date type (eg request date, dob) my @param_names = qw(year month day); if ($prefix) { # eg validating request_year, request_day, etc map { $_ = $prefix . '_' . $_ } @param_names; # eg year => dob_year } # warn Dumper \@param_names; return \@param_names; } 1;