#!/usr/bin/perl use strict; #################### main pod documentation begin ################### =head1 NAME Zymonic - Test script to generate field output, for performance testing =head1 SYNOPSIS =head1 DESCRIPTION This script will take in a list of fields and generate output for each. =head1 SUPPORT As in the license, Zymonic is provided without warranty or support unless purchased separately, however... If you email zymonic-support@zednax.com your issue will be noted and may receive a response. For security issues, please contact zymonic-security@zednax.com and someone will respond within 8 working hours. =head1 AUTHOR Alex Masidlover et al. CPAN ID: MODAUTHOR Zednax Limited alex.masidlover@zednax.com http://www.zednax.com =head1 COPYRIGHT This program is free software licensed under the... Zymonic Public License 1.0 The full text of the license can be found in the LICENSE file included with this module. Other licenses may be acceptable if including parts of Zymonic in larger projects, please contact Zednax for details. =head1 SEE ALSO perl(1). =cut #################### main pod documentation end ################### # modules use Data::Dumper; use List::Util qw(shuffle sum); use Time::HiRes qw(time); use Zymonic; use Zymonic::Auth; use Zymonic::Config; use Zymonic::Session; use Zymonic::Utils qw(death_handler random_string); # use all field subclasses to avoid module loading times use Zymonic::Field::Choice; use Zymonic::Field::DateTime; #use Zymonic::Field::DecryptorCrypt; #use Zymonic::Field::DecryptorEncrypted; use Zymonic::Field::Email; use Zymonic::Field::File; use Zymonic::Field::FileChooser; use Zymonic::Field::Glob; use Zymonic::Field::LinkedField; use Zymonic::Field::MultipleChoiceLinkedField; #use Zymonic::Field::PAN; use Zymonic::Field::PasswordCapture; use Zymonic::Field::Stylesheet; use Zymonic::Field::SubForm; use Zymonic::Field::SubFilter; # death handler $main::SIG{__DIE__} = \&death_handler; # setup the field factory $Zymonic::field_factory = Zymonic::FieldFactory->new(); # enable cache for table includes and relationship permissions $Zymonic::object_cache = { 'Zymonic::TableInclude' => {}, 'Zymonic::RelationshipPermissions' => {}, }; # Create a session (CGI only), give ident so can be parent for fields $Zymonic::session = Zymonic::Session->new( ident => 'field_output_script' ); print "# Loaded Session\n"; # force no debugs $Zymonic::Utils::debugs_written = 1; # set system $Zymonic::system = $ARGV[0]; usage() unless $Zymonic::system; # create config $Zymonic::ZCONFIG{$Zymonic::system} = Zymonic::Config->new( system_name => $Zymonic::system, config_dir => "/etc/zymonic", ); print "# Loaded Config\n"; # set session db $Zymonic::session->{DB} = $Zymonic::ZCONFIG{$Zymonic::system}->{DB}; # create auth, make it use manual auth so don't need permissions $Zymonic::ZCONFIG{$Zymonic::system}->{authtype} = 'Manual'; $Zymonic::ZCONFIG{$Zymonic::system}->{building_documentation} = 'Y'; my $auth = Zymonic::Auth->new( config => $Zymonic::ZCONFIG{$Zymonic::system}, session => $Zymonic::session, DB => $Zymonic::ZCONFIG{$Zymonic::system}->{DB}, user => '', credentials => '', logged_in => '', ); # set session config/auth so can use as field parent $Zymonic::session->{config} = $Zymonic::ZCONFIG{$Zymonic::system}; $Zymonic::session->{auth} = $auth; main(); exit(0); #################### subroutine header begin #################### =head2 main Usage : main(); Purpose : main script functionality Returns : nothing Argument : nothing Throws : nothing Comment : See Also : =cut #################### subroutine header end ################### sub main { my $count = $ARGV[1] || 1; my @fields = @ARGV[ 2 .. $#ARGV ]; usage() unless @fields; if ( $fields[0] =~ /^\d+$/ ) { @fields = random_fields( $fields[0] ); print "# To run this set of fields again:\nfield_output.pl $Zymonic::system $count " . join( ' ', @fields ) . "\n"; } # load the fields @fields = sort @fields; load_fields( \@fields ); # generate the output my $number_of_fields = @fields; my %times = ( map { $_ => { load_ref_total => 0, load_object_total => 0, output_total => 0 } } @fields ); print "# Generating output $count times for $number_of_fields fields\n\n"; foreach my $i ( 1 .. $count ) { foreach my $field_zname (@fields) { # load the field ref my $start = time() * 1000; my $field_ref = $Zymonic::session->get_field_ref( $field_zname, { parent => $Zymonic::session, zname => $field_zname } ); my $end = time() * 1000; my $time_taken = ( $end - $start ); $times{$field_zname}->{load_ref_total} += $time_taken; # load the field object $start = time() * 1000; my $field = $Zymonic::session->get_field_object($field_ref); $end = time() * 1000; $time_taken = ( $end - $start ); $times{$field_zname}->{load_object_total} += $time_taken; # load the field output $start = time() * 1000; my $output = $field->output('no_display_attributes'); $end = time() * 1000; $time_taken = ( $end - $start ); $times{$field_zname}->{output_total} += $time_taken; } } # lookup field classes my %classes = (); map { push( @{ $classes{ $Zymonic::ZCONFIG{$Zymonic::system}->get_def( 'Field', $_ )->{class} || 'Standard Field' } }, $_ ) } @fields; # output broken down by class print "# " . sprintf( '%-40s', 'Field' ) . "\t Count\t Load Ref\t Load Object\t Output\n"; foreach my $class ( sort keys %classes ) { print "\n# $class\n"; foreach my $field_zname ( sort @{ $classes{$class} } ) { print sprintf( '%-40s', $field_zname ) . "\t $count\t " . sprintf( '%5.6f', $times{$field_zname}->{load_ref_total} / $count ) . "\t " . sprintf( '%5.6f', $times{$field_zname}->{load_object_total} / $count ) . "\t " . sprintf( '%5.6f', $times{$field_zname}->{output_total} / $count ) . "\n"; } print sprintf( '%-40s', 'Total' ) . "\t $count\t " . sprintf( '%5.6f', ( sum map { $times{$_}->{load_ref_total} } @{ $classes{$class} } ) / ( $count * @{ $classes{$class} } ) ) . "\t " . sprintf( '%5.6f', ( sum map { $times{$_}->{load_object_total} } @{ $classes{$class} } ) / ( $count * @{ $classes{$class} } ) ) . "\t " . sprintf( '%5.6f', ( sum map { $times{$_}->{output_total} } @{ $classes{$class} } ) / ( $count * @{ $classes{$class} } ) ) . "\n"; } } #################### subroutine header begin #################### =head2 random_fields Usage : random_fields($count); Purpose : Picks random fields from system def. Returns : array of field znames Argument : nothing Throws : nothing Comment : See Also : =cut #################### subroutine header end ################### sub random_fields { my $count = shift; # grab and shuffle fields from def my $zname_list = $Zymonic::ZCONFIG{$Zymonic::system}->{SysDef}->{zname_list}; my @fields_from_def = shuffle grep { ( $zname_list->{$_}->{type} || '' ) eq 'Field' } keys %{$zname_list}; if ( $count > @fields_from_def ) { $count = @fields_from_def; } # return the number we want from top of the list return @fields_from_def[ 0 .. $count - 1 ]; } #################### subroutine header begin #################### =head2 load_fields Usage : load_fields(\@fields); Purpose : Creates the field refs for each field and loads it. Returns : nothing Argument : array of field znames Throws : nothing Comment : See Also : =cut #################### subroutine header end ################### sub load_fields { my $fields = shift || []; # usually fields are stored from table or filter # for this script just store them all on session foreach my $field_zname ( @{$fields} ) { $Zymonic::session->store_field_ref($field_zname); } } #################### subroutine header begin #################### =head2 usage Usage : usage(); Purpose : Prints script usage. Returns : nothing Argument : nothing Throws : nothing Comment : See Also : =cut #################### subroutine header end ################### sub usage { print "Usage: field_output.pl [system] [number of iterations (defaults to 1)] " . "[field_zname 1 or number] [field_zname 2] ... [field_zname n]\n" . "Note: If field 1 is a number then will pick a random selection of fields from the system def."; exit(1); }