#!/usr/bin/env /opt/rational/clearquest/bin/cqperl ############################################################################## # # Name: cqtool # # Description: cqtool is an interface to Clearquest to perform some simple # actions to the RANCQ database. It is used primarily by ucmwb # but it also supports a command line interface. # # The following commands are supported: # # activate : # Activate WOR # assign : # Assign the WOR # clone : # Clones a WOR # comment # Add a comment to the Notes_Entry field for the WOR # complete : # Complete WOR # createhd: # Create a new Help Desk Ticket # createwor: # Create a new WOR # effort : # Update the WOR's actual hours # exit|quit: # Exits cqtool # help: # This display # link : # Link a parent WOR to a child WOR # resolve : # Resolve WOR # set # Set to for the # usage: # Displays command line usage # version: # Displays version of cqtool # # Many of these commands simply perform actions on a wor. Two # of these commands, createwor and createhd have Perl/Tk GUI # interfaces. # # Command line usage: # # Usage: cqtool\t[-usage|help] [-verbose] [-debug] # [-userid ] [-password ] [] # # Where: # # -usage|help: Display usage # -verbose: Turn on verbose mode # -debug: Turn on debug mode # -userid: User ID to log into Clearquest database as # -password: Password to use # If specified then cqtool executes and # exits # # Environment: cqtool supports the following environment variables # that are used mostly for tesing purposes # # CQ_DBSET: Clearquest DBSET to open (e.g. XTST3 for testing - # default RANCQ) # CQ_USER: User name to log into the $CQ_DBSET database with # CQ_PASSWORD: Password to use to log into the $CQ_DBSET with. # # Author: Andrew@DeFaria.com # # (c) Copyright 2007, General Dynamics, all rights reserved # ############################################################################## use strict; use warnings; use CQPerlExt; use FindBin; use Getopt::Long; use Term::ANSIColor qw (:constants); use lib ("$FindBin::Bin", "$FindBin::Bin/../lib"); use SCCM::Misc; use Display; use CQTool; use CreateWORUI; use CreateHelpDeskUI; use Logger; my $VERSION = BOLD GREEN . "1.1" . RESET; my $PROMPT = BOLD YELLOW . ">>" . RESET; my $UCMWB_PROMPT = ">>"; my $DESC = BOLD RED . "$FindBin::Script" . RESET " Version " . $VERSION . CYAN ": Program to talk to Clearquest" . RESET; # Globals my $_userid = $ENV{CQ_USER} ? $ENV{CQ_USER} : $ENV{USER}; my $_password = $ENV{CQ_PASSWORD}; my $_db_name = $ENV{CQ_DBSET} ? $ENV{CQ_DBSET} : "RANCQ"; my $_ucmwb; my $_log; if (get_debug) { $_log = new Logger ( path => "/tmp", append => 1, ); } # if my %_commands = ( activate => \&activate, assign => \&assign, clone => \&clone, comment => \&comment, complete => \&complete, createhd => \&createHelpDesk, createwor => \&createWOR, effort => \&effort, exit => \&shutdown, help => \&help, link => \&linkParentWor2ChildWor, quit => \&shutdown, resolve => \&resolve, set => \&set, usage => \&usage, version => \&announce, ); ############################################################################## # Forwards ############################################################################## sub commandLoop (@); ############################################################################## # Main ############################################################################## MAIN: { GetOptions ( "usage" => sub { usage () }, "verbose" => sub { set_verbose () }, "debug" => sub { set_debug () }, "userid=s" => \$_userid, "password=s" => \$_password, "database=s" => \$_db_name, "ucmwb" => \$_ucmwb, ) || usage (); exit (commandLoop(@ARGV)); } # MAIN ############################################################################## # Subroutines ############################################################################## #----------------------------------------------------------------------------- # shutdown (): Ends program #----------------------------------------------------------------------------- sub shutdown () { exit (0); } # exit #----------------------------------------------------------------------------- # help (): Displays help #----------------------------------------------------------------------------- sub help () { display ($DESC); display < : Activate WOR assign : Assign the WOR clone : Clones a WOR comment Add a comment to the Notes_Entry field for the WOR complete : Complete WOR createhd: Create a new Help Desk Ticket createwor: Create a new WOR effort : Update the WOR's actual hours exit|quit: Exits $FindBin::Script help: This display link : Link a parent WOR to a child WOR resolve : Resolve WOR set Set to for the usage: Displays command line usage version: Displays version of $FindBin::Script END } # help #----------------------------------------------------------------------------- # announce (): Announce ourselves #----------------------------------------------------------------------------- sub announce () { display ($DESC); } # Announce #----------------------------------------------------------------------------- # dberror ($): Handle errors when talking to Clearquest. Note we need to reset # the database connection if an error happens. #----------------------------------------------------------------------------- sub dberror ($) { my ($msg) = @_; # Need to not only report the error but to reopen the # database. Something gets corruppted if we don't! error ($msg); closeDB (); openDB ($_userid, $_password, $_db_name); } # DBError #----------------------------------------------------------------------------- # getEntity ($$): Get an entity from Clearquest #----------------------------------------------------------------------------- sub getEntity ($$) { my ($recordname, $wor) = @_; my $entity; eval { $entity = $CQTool::session->GetEntity ($recordname, $wor); }; if ($@) { chomp $@; dberror ($@); return undef; } else { return $entity; } # if } # getEntity #----------------------------------------------------------------------------- # set ($$$): Set $field to $value for $wor #----------------------------------------------------------------------------- sub set ($$@) { my ($wor, $field, $value) = @_; if (!$wor or $wor eq "") { error ("WOR is required"); return 1; } # if if (!$field or $field eq "") { error ("Field is required"); return 1; } # if my $entity = getEntity ("WOR", $wor); return 1 if !$entity; $session->EditEntity ($entity, "modify"); $_log->msg ("Modifying $field to \"$value\"") if get_debug; eval { $entity->SetFieldValue ($field, $value); }; if ($@) { dberror ("$field set failed for WOR $wor:\n$@"); return 2; } # if my $status = $entity->Validate (); if ($status ne "") { $entity->Revert (); error ("$field validate failed for WOR $wor:\n$status"); return 2; } # if $status = $entity->Commit (); if ($status ne "") { error ("$field update failed during Submit for $wor:\n$status"); return 2; } # if return 0; } # set #----------------------------------------------------------------------------- # clone ($): Clone a WOR #----------------------------------------------------------------------------- sub clone ($) { my ($wor) = @_; if (!$wor) { error ("WOR not specified!"); return 1; } # if $entity = getEntity ("WOR", $wor); return 1 if !$entity; # Check state my $state = $entity->GetFieldValue ("state")->GetValue (); if ($state ne "Closed") { error ("WOR $wor not closed - Unable to clone!"); return 1; } # if verbose ("Cloning WOR $wor..."); my $result = 0; eval { # Currently Clone doesn't return a proper result but eventually... $result = $CQTool::session->FireRecordScriptAlias ($entity, "Clone"); }; if ($@) { chomp $@; dberror ($@); return 1; } # if return $result; } # clone #----------------------------------------------------------------------------- # effort ($$): Update actual hours for a WOR #----------------------------------------------------------------------------- sub effort ($$) { my ($wor, $actualHrs) = @_; return set $wor, "ActualEffort", $actualHrs; } # effort #----------------------------------------------------------------------------- # comment (): Update the Notes_Entry comment field for a WOR #----------------------------------------------------------------------------- sub comment ($) { my ($wor) = @_; if (!$wor) { error "WOR not defined in call to comment!"; return 1; } # if if (!$_ucmwb) { display ("Enter comments below. When finished, enter \".\" on a line by itself or hit ^D:"); } else { # We still need to prompt for the comments however signal UCMWB # that command is ready for more input. display_nolf ($UCMWB_PROMPT); } # if my $comments; while () { last if $_ eq ".\n"; $comments .= $_; } # while chomp $comments; $_log->msg ("Comments:\n$comments") if get_debug; return set $wor, "Note_Entry", $comments; } # Comment #----------------------------------------------------------------------------- # linkParentWor2ChildWor ($$): Link a child WOR to a parent WOR #----------------------------------------------------------------------------- sub linkParentWor2ChildWor ($$) { my ($parentWor, $childWor) = @_; my $status; verbose ("Linking $parentWor -> $childWor..."); my $childentity = getEntity ("WOR", $childWor); my $parententity = getEntity ("WOR", $parentWor); return 1 unless $childentity and $parententity; $session->EditEntity ($parententity, "modify"); $parententity->AddFieldValue ("wor_children", $childWor); $status = $parententity->Validate (); if ($status ne "") { $parententity->Revert (); error ("Validation failed while attempting to add child WOR $childWor to parent WOR $parentWor:\n$status"); return 1; } # if eval { $status = $parententity->Commit (); }; $status = $@ if $@; if ($status ne "") { (error "Commit failed while trying to add child WOR $childWor to parent WOR $parentWor:\n$status"); return 2; } # if debug "Modifying child $childWor..."; $session->EditEntity ($childentity, "modify"); $childentity->SetFieldValue ("wor_parent", $parentWor); $status = $childentity->Validate (); if ($status ne "") { $childentity->Revert (); error "Validation failed while attempting to add parent WOR $parentWor to child WOR $childWor:\n$status"; return 1; } # if eval { $status = $childentity->Commit (); }; $status = $@ if $@; if ($status ne "") { error "Commit failed while trying to add parent WOR $parentWor to child WOR $childWor:\n$status"; return 2; } # if return 0; } # linkParentWor2ChildWor #----------------------------------------------------------------------------- # assign ($$$$): Assign a WOR #----------------------------------------------------------------------------- sub assign ($$$$$) { my ($wor, $assignee, $project, $plannedHrs, $startDate) = @_; if (!$wor or $wor eq "") { error ("WOR is required"); return 1; } # if if (!$assignee or $assignee eq "") { error ("Assignee must be specified"); return 1; } # if if (!$project or $project eq "") { error ("UCM Project is required"); return 1; } # if if (!$startDate or $startDate eq "") { error ("Planned Start Date is required"); return 1; } # if my $entity = getEntity ("WOR", $wor); return 1 if !$entity; my $state = $entity->GetFieldValue ("state")->GetValue (); if ($state ne "Submitted") { error ("WOR $wor is not in Submitted state!\nState: $state"); return 2; } # if $session->EditEntity ($entity, "assign"); $entity->SetFieldValue ("ucm_project", $project) if $project ne ""; $entity->SetFieldValue ("PlannedStart", $startDate) if $startDate ne ""; $entity->SetFieldValue ("PlannedEffort", $plannedHrs) if $plannedHrs ne ""; $entity->SetFieldValue ("Owner", $assignee) if $assignee ne ""; my $status = $entity->Validate (); if ($status ne "") { $entity->Revert (); error ("Assign failed for WOR $wor:\n$status"); return 2; } # if $status = $entity->Commit (); if ($status ne "") { error ("Assign failed during Submit for WOR $wor:\n$status"); return 2; } # if return 0; } # assign #----------------------------------------------------------------------------- # activate (): Activate a WOR #----------------------------------------------------------------------------- sub activate ($$$$$) { my ($wor, $project, $estHrs, $startDate, $endDate) = @_; if (!$wor or $wor eq "") { error ("WOR is required"); return 1; } # if if (!$project or $project eq "") { error ("UCM Project is required"); return 1; } # if if (!$startDate or $startDate eq "") { error ("Planned Start Date is required"); return 1; } # if if (!$endDate or $endDate eq "") { error ("Planned End Date is required"); return 1; } # if my $entity = getEntity ("WOR", $wor); return 1 if !$entity; my $state = $entity->GetFieldValue ("state")->GetValue (); if ($state ne "Assessing") { error ("WOR $wor is not in Assessing state!\nstate: $state"); return 2; } # if $session->EditEntity ($entity, "activate"); $entity->SetFieldValue ("ucm_project", $project) if $project ne ""; $entity->SetFieldValue ("EstimatedEffort", $estHrs) if $estHrs ne ""; $entity->SetFieldValue ("PlannedStart", $startDate) if $startDate ne ""; $entity->SetFieldValue ("PlannedEnd", $endDate) if $endDate ne ""; my $status = $entity->Validate (); if ($status ne "") { $entity->Revert (); error ("Activate failed for WOR $wor:\n$status"); return 2; } # if $status = $entity->Commit (); if ($status ne "") { error ("Activate failed during Submit for WOR $wor:\n$status"); return 2; } # if return 0; } # activate #----------------------------------------------------------------------------- # resolve ($): Resolve a WOR #----------------------------------------------------------------------------- sub resolve ($) { my ($wor) = @_; if (!$wor or $wor eq "") { error ("WOR is required"); return 1; } # if my $entity = getEntity ("WOR", $wor); return 1 if !$entity; my $state = $entity->GetFieldValue ("state")->GetValue (); if ($state ne "Working") { error ("WOR $wor is not in Working state!\nState: $state"); return 2; } # if $session->EditEntity ($entity, "resolve"); my $status = $entity->Validate (); if ($status ne "") { $entity->Revert (); error ("Resolve failed for WOR $wor:\n$status"); return 2; } # if $status = $entity->Commit (); if ($status ne "") { error ("Resolve failed during Submit for WOR $wor:\n$status"); return 2; } # if return 0; } # resolve #----------------------------------------------------------------------------- # complete ($$): Complete a WOR #----------------------------------------------------------------------------- sub complete ($$) { my ($wor, $actualHrs) = @_; if (!$wor or $wor eq "") { error ("WOR is required"); return 1; } # if if (!$wor or $wor eq "") { error ("Actual Hours are required"); return 1; } # if my $entity = getEntity ("WOR", $wor); return 1 if !$entity; my $state = $entity->GetFieldValue ("state")->GetValue (); if ($state ne "Verifying") { error ("WOR $wor is not in Verifying state!\nState:$state"); return 2; } # if $session->EditEntity ($entity, "complete"); $entity->SetFieldValue ("ActualEffort", $actualHrs) if $actualHrs ne ""; my $status = $entity->Validate (); if ($status ne "") { $entity->Revert (); error ("Complete failed for WOR $wor:\n$status"); return 2; } # if $status = $entity->Commit (); if ($status ne "") { error ("Complete failed during Submit for WOR $wor:\n$status"); return 2; } # if return 0; } # Complete #----------------------------------------------------------------------------- # executeCommand (@): Executes a cqtool command #----------------------------------------------------------------------------- sub executeCommand (@) { my (@args) = @_; my $cmd = lc shift @args; return if $cmd eq ""; if ($_commands{$cmd}) { if (!$CQTool::session) { if ( # Commands that do not require a database connection !($cmd eq "exit" or $cmd eq "quit" or $cmd eq "help" or $cmd eq "usage" or $cmd eq "verbose")) { verbose "Opening $_db_name as $_userid..."; if (!$_password) { display_nolf ("${_userid}'s password:"); `stty -echo`; $_password = ; chomp $_password; display (""); `stty echo`; } # if openDB ($_userid, $_password, $_db_name); } # if } # if # Treat args: Args that are enclosed in quotes must be # combined. For simplicity's sake we will only support matched # pairs of double quotes. Anything else results in undefined # behavior. my (@new_args); foreach (@args) { # Quoted argument starting if (/^\"(.*)\"$/s) { push @new_args, $1; } else { push @new_args, $_; } # if } # foreach $_log->msg ("$cmd (" . join (",", @new_args) . ")") if get_debug; return $_commands{$cmd} (@new_args); } else { error ("Unknown command \"$cmd\" (try help)"); return 1; } # if } # executeCommand #----------------------------------------------------------------------------- # commandLoop (@): This is the interactive command loop #----------------------------------------------------------------------------- sub commandLoop (@) { my (@args) = @_; # For single, command line, commands... return executeCommand (@args) if @args; announce if !$_ucmwb; while () { if (!$_ucmwb) { display_nolf ($PROMPT . RESET . UNDERLINE); } else { display_nolf ($UCMWB_PROMPT); } # if # Read command into $_ $_ = ; chomp; # If we are not being called by ucmwb, display RESET to stop the # UNDERLINE we were using. This keeps the output from being # underlined. In ucmwb mode we are not using any of the terminal # sequences. display_nolf (RESET) if !$_ucmwb; # If the user hit Control-d then a ^D is displayed but we remain # on the same line. So output a carriage return and exit 0. if (!$_) { display (""); exit 0; } # if # Special handling for set command since we want to take # everything after to be a value, and we may get long # values that are space separated and space significant # (e.g. description?) if (/^\s*(\w+)\s+(\w+)\s+(\w+)\s+(.*)/) { if (lc $1 eq "set") { my $cmd = $1; my $wor = $2; my $field = $3; my $value = $4; # Change "\n"'s back to \n's $value =~ s/\\n/\n/g; executeCommand ($cmd, $wor, $field, "\"$value\""); } else { executeCommand (split); } # if } else { executeCommand (split); } # if } # while } # commandLoop