Alister West

home is where your code is ...

Perl - Request Tracker (RT) - Import Users/Tickets.

Importing users is common. This script should be shorter...

# start with http://alisterwest.com/recipes/perl-rt-script-template 

open my $fh, '<', "$Bin/rt-users.csv";
my @rows = <$fh>; chomp @rows;
shift @rows; # trim colnames

foreach my $row (@rows) {
    next unless $row;
    my ($name, $username, $memberof, $email) = split /\s*,\s*/, $row;

    # cleanup username
    $username = lc $username; $username =~ s/^\s+//; $username =~ s/\s+$//; $username =~ s/ /_/g;

    #
    # Create users
    #   - if a user already exists it will just return an error
    #
    my $user = RT::User->new($RT::SystemUser);
    my ($status, $msg) = $user->LoadByEmail($email);
    if (!$status) {
        ($status, $msg) = $user->Create(
            Name => $username,
            RealName => $name,
            Privileged => 1,
            EmailAddress => $email,
            Password => $username,
        );
        say "$status: $msg";
    }
    say "user: $username => " . $user->PrincipalId;

    my $rt_group = RT::Group->new($RT::SystemUser);
    $rt_group->LoadUserDefinedGroup($memberof);
    ($status, $msg) = $rt_group->AddMember( $user->PrincipalId );
    say "$status: $msg" if $status;
}

When you start using RT you may want to import some tickets into the system. The example below is customised to the install I wrote it for - but you can see the general idea.

Import tickets into RT

#!/usr/bin/env perl

=head1 SYNOPSIS

    rt-import-tickets.pl  export_from_other_system.csv 

=head1 DESCRIPTION

    This will import tickets into RT from a CSV file.
    It assumes the CSV file has a header line.
    Owner/Requestor fields should be email addresses.

=head1 AUTHOR

    Alister West - https://alisterwest.com

=head1 LICENCE

    http://creativecommons.org/licenses/by/2.5/

=head1 CHANGES

    * 2013/03/12 - Updated for RT v4.0.10 
                 - text/html comments
    * 2010/01/01 - Created

=cut

BEGIN {
    # Find RT.pm
    use FindBin qw/$Bin/;
    use lib "$Bin/../../local/lib", "$Bin/../../lib", "$Bin/../local/lib", "$Bin/../lib";
}

$|++;
use strict;
use warnings;
use feature qw/say/;
use Data::Dumper;
use RT;
use RT::Interface::CLI qw/CleanEnv/;
use RT::Ticket;
use RT::User;

CleanEnv();
RT::LoadConfig();
RT::Init();

use MIME::Entity;
use Text::CSV;

#
# HTML_COMMENTS
#
# If your description/data content is in html set this to 1 to get text/html.
# Otherwise it will be imported as text/plain.
#
my $HTML_COMMENTS = 0;


# Usage
# - make sure file exists and its a csv file.
my $filename = $ARGV[0] or die "Usage: $0  mydoc.csv\n";


#
# Load CSV parser
#
my $csv = Text::CSV->new({ binary => 1, eol => $/ });
open (my $fh, "<:encoding(utf8)", $filename) or die $!;


# Assume CSV file has the column fields as the first row.
my @COLNAMES = @{ $csv->getline($fh) };
@COLNAMES = map { s/[\s]+/ /xmsg; s/^\s+|\s+$//; $_ } @COLNAMES;
# print Dumper \@COLNAMES;


#
# Automatically find indexes for key fields.
#
my %INDEX_FOR; # GLOBAL
for (my $i = 0; $i < @COLNAMES; $i++) {
    $INDEX_FOR{subject}   = $i if $COLNAMES[$i] =~ /^\s*subject\s*$/i;
    $INDEX_FOR{queue}     = $i if $COLNAMES[$i] =~ /^\s*(queue|department)\s*$/i;
    $INDEX_FOR{status}    = $i if $COLNAMES[$i] =~ /^\s*status\s*$/i;
    $INDEX_FOR{priority}  = $i if $COLNAMES[$i] =~ /^\s*priority\s*$/i;
    $INDEX_FOR{owner}     = $i if $COLNAMES[$i] =~ /^\s*owner\s*$/i;
    $INDEX_FOR{requestor} = $i if $COLNAMES[$i] =~ /^\s*requestor\s*$/i;
    # will break if you have 2 cols with created-date and created-time
    $INDEX_FOR{created}   = $i if $COLNAMES[$i] =~ /^\s*creat(e|ed|ion)([-_\s](date|time|on))?\s*$/i;
}

#
# Import tickets from CSV one at a time.
#
my $counter;
while (my $data = $csv->getline($fh)) {
    $counter++;

    my @data = @$data;
    my $id = import_ticket( @data );
    warn "Failed to create ticket on row $counter" if ! $id;

    # exit if $counter > 2;
    last;
}
say "Done. $counter tickets imported!";
exit;


#
# Import Ticket
#
sub import_ticket {
# ------------------------------------------------------------------------------

    # data fields from a csv file.
    my @data = @_;

    my $subject   = defined $INDEX_FOR{subject}   ? substr( $data[ $INDEX_FOR{subject} ], 0, 50) : "[no subject]";
    my $queue     = defined $INDEX_FOR{queue}     ? $data[ $INDEX_FOR{queue}     ] : "General";
    my $status    = defined $INDEX_FOR{status}    ? $data[ $INDEX_FOR{status}    ] : 'new';
    my $priority  = defined $INDEX_FOR{priority}  ? $data[ $INDEX_FOR{priority}  ] : undef;
    my $created   = defined $INDEX_FOR{created}   ? $data[ $INDEX_FOR{created}   ] : undef;
    my $owner     = defined $INDEX_FOR{owner}     ? $data[ $INDEX_FOR{owner}     ] : undef;
    my $requestor = defined $INDEX_FOR{requestor} ? $data[ $INDEX_FOR{requestor} ] : undef;

    # RT only understands emails or user-id's
    $requestor = undef unless $requestor =~ /@/;
    $owner     = undef unless $owner =~ /@/;
    $priority  = undef unless $priority =~ /^\d+$/;

    # Check queue exists
    my $rtq = RT::Queue->new(RT->SystemUser);
    if (! $rtq->Load($queue)) {
        warn "Could not load : $queue. Changing to General";
        $queue = "General";
    }


    # 
    # Comment containing all imported data.
    # - add a space above a multiline text block
    #
    my @lines = ("imported from $filename." . ($HTML_COMMENTS ? "<br><br>" : "") . "\n\n");
    for (my $i = 0; $i < @COLNAMES; $i++) {
        my $eol = $HTML_COMMENTS ? "<br>\n" : "\n";
        push @lines, $eol if $data[$i] =~ /<br|\n/xms;
        push @lines, sprintf("%24s : %s %s", $COLNAMES[$i], ($data[$i] || ''), $eol);
    }
    my $mime_comment = MIME::Entity->build( Data => \@lines, Type => ($HTML_COMMENTS ? 'text/html' : 'text/plain') );
    # print "comment: ". join '', @lines;


    #
    # $ticket->Create !!
    #
    # * Most actions in RT return a [ $status, $msg ] tupple, but Ticket::Create returns a tripple!
    # * ->Import requires data for all fields. Use Create to get default values.
    #
    my $ticket = RT::Ticket->new(RT->SystemUser);
    my %args = (
        Status    => $status,
        Subject   => $subject,
        Queue     => $queue,
        Priority  => ($priority ? $priority : 0),
        Requestor => ($requestor ? [$requestor] : []),
        Owner     => ($owner ? $owner : RT->Nobody->Id ),
        Created   => ($created ? $created : \'NOW()'),
        Updated   => \'NOW()',
        _RecordTransaction => 0,  # don't send emails/trigger scrips
    );
    # print Dumper \%args;
    my ($ticket_id, $ok, $msg) = $ticket->Create(%args);
    print "created: $ticket_id, $ok, $msg!\n";

    # Add the comment to the Ticket.
    if ($ticket_id) {
        $ticket->Comment( MIMEObj => $mime_comment, CommitScrips => 0 ) if $ticket_id;
    } else {
        warn "Couldn't create Ticket for:". Dumper \%args;
    }


    # Custom Fields
    #
    # XXX CHANGE ME to map CSV.FieldIndex to RT.CustomField.ID XXX
    # if (my $val = $data[5]) {
    #     ($ok, $msg) = $ticket->AddCustomFieldValue( Field => 45, Value => $val, RecordTransaction => 0); say "$ok: $msg.";
    # }

    return $ticket_id;
}
By Alister West