Subversion Repositories DevTools

Rev

Rev 227 | Rev 361 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

###############################################################################
# CLASS RmPkgInfo
# Gathers information about package from the Release Manager Database
#
# By convention all members/methods that begin with an '_' are private.
#
###############################################################################

=head NAME
    RmPkgInfo: Class to allow package details to be collected from release manager

=head DATA
    By default will load details about the provided package.  
    The following information is gathered and stored in a hash with the following keys
        pkg_id          => The ID Number of the package
        pkg_name        => The Name of the package
        pv_id           => The ID number of the package version
        pkg_version     => The Version number of the package
        pv_description  => The package Version description (or default if not available)
        pv_reason       => The package Version reason or default if not available)
        pv_label        => The package Version label (or default if not available)
        pv_dlocked      => Flag to indicate package Version is locked (or default if not available)
        pv_source_path  => Package source patch (or undefined if not available) )
        pv_modified_time => DateTime the package was created


    Will also gather optional information if requested

    Attached issues will be collected if called, will gather the following in a multi level hash
    keyed by database (TDSE or DEVI) and issue number
        iss_numStr      => The Issue Number as a string
        iss_state       => The Issue State flag
        iss_id          => The Issue database number
        iss_summary     => The Issue summary
        iss_status      => The Issue Status flag
        iss_priority    => The Issue priority
        iss_type        => The Issue Type

    The package runtime dependencies are also gathered and stored in a hash if called upon.
    The following details are collected in a hash keyed by dependency name.
        rt_name         => The Runtime Dependency Name
        rt_version      => The Runtime Dependency Version
        rt_comments     => The Runtime Dependency Comments

    The package dependencies can also be retrieved.  For each dependency found it will create
    a new RmPkgInfo object and stores it in the object.  Information about a dependency can be
    retrieved from the objects themselves

=head SYNOPSIS
    
    use DeployUtils::RmPkgInfo

    my $pkg = DeployUtils::RmPkgInfo->new({ PKG_NAME => "name", PKG_VERSION => "ver" })
    or
    my $pkg = DeployUtils::RmPkgInfo->new({ PV_ID => "1234" })
    or
    my $pkg = DeployUtils::RmPkgInfo->new({ PKG_NAME => "name", RTAG_ID => "ver", WIP => 1 })
        
        Instantiates the object, connects to RM and tries to load details about the package.
        Will always return the class object (unless invalid args which will abort).  
        Failure to connect or retrieve package information will still succeed. Use the foundPkg() & foundDetails() 
        members to verify the appropriate data has been collected
        Synopsis 1
            Will try to locate pkg_id for the name supplied, foundPkg() will return true or false depending on result.
            If successfull will use pkg_id & supplied version to get package details, foundDetails() will 
            return true or false depending on result.
            In this case foundDetails() implies foundPkg()
        Synopsis 2
            Will locate package details by supplied pv_id, both foundPkg() & foundDetails() will return true 
            or false depending on result.
        Synopsis 3
            Will try to locate the current version of PkgName in Release rTagId.
            If WIP is set to non 0 then will look for the latest version is release managers Work In Progress view.
            If not defined or set to 0 then it will look in the standard release view.

    $rv = $pkg->foundPkg()
        Returns true if package information is found ie pkg_id is non 0 (see new above)

    $rv = $pkg->foundDetails()
        Returns true if package version information is found ie pv_id is non 0 (see new above)

    $str = $pkg->pkg_id()
    $str = $pkg->pkg_name()
    $str = $pkg->pv_id()
    $str = $pkg->pkg_version()
    $str = $pkg->pv_description()
    $str = $pkg->pv_reason()
    $str = $pkg->pv_label()
    $str = $pkg->pv_dlocked()
    $str = $pkg->pv_source_path()
    $str = $pkg->pv_modified_time()
        Details Accessor members, returns the associated field from the package details


    The following calls are used to access associated Issue details

    $rv = $pkg->getPkgIssues()
        Will connect to RM & CQ to get details about issues attached to current package.
        First gets a list of issue number attached from RM and then gets the issue details from CQ.
        Returns 1 for success and 0 for failure, however foundIssues() can be used to determine
        if issues have been found.

    $rv = $pkg->foundIssues()
        Returns true if Issues details for package have been retrieved.

    $typelist = $pkg->getIssueTypes()
        Returns as a list of strings, the issue types found.  This will be one or both of
        ( "tdse", "devi" ).  This will call getPkgIssues if foundIssues() is false.

    $issuelist = $pkg->getIssueNumbers(issuetype)
        Returns as a list of strings, the issue numbers (iss_id) found for the passed issue type.
        This will call getPkgIssues if foundIssues() is false.
        Will abort if parameter not passed
        example
            foreach $i ( $pkg->getIssueTypes() )
                print $pkg->getIssueNumbers($i)

    $str = $pkg->iss_numStr(issuetype, issueId)
    $str = $pkg->iss_state(issuetype, issueId)
    $str = $pkg->iss_id(issuetype, issueId)
    $str = $pkg->iss_summary(issuetype, issueId)
    $str = $pkg->iss_status(issuetype, issueId)
    $str = $pkg->iss_priority(issuetype, issueId)
    $str = $pkg->iss_type(issuetype, issueId)
        Details Accessor members, returns the associated field from the issues details for the issuetype & issueId
        This will call getPkgIssues if foundIssues() is false. Will abort if parameters not passed

    
    The following calls are used to access associated Runtime Dependencies
    
    $rv = getRtDeps()
        Will Connect to RM to get details about associated RunTime dependencies.
        Returns 1 for success and 0 for failure, however foundRtDeps() can be used to determine
        if issues have been found.

    $rv = $pkg->foundRtDeps()
        Returns true if Runtime dependencies details for package have been retrieved.

    @rtlist = $pkg->getRtDepNames()
        Returns as a list of strings, the dependency names of all dependencies
        This will call getRtDeps if foundRtDeps() is false.

    $str = $pkg->rt_name(RtDepName)
    $str = $pkg->rt_version(RtDepName)
    $str = $pkg->rt_comments(RtDepName)
        Details Accessor members, returns the associated field from the runtime dependencies for the dependency name
        This will call getRtDeps if foundRtDeps() is false.  Will abort if parameters not passed


    The following calls are used to access associated Dependencies

    $rv = $getDependenciesHash()
        Will return a hash containing package name and versions for all the dependant
        packages, without extracting all the package information.

        This function corrects a problem in other function where multiple packages of the same
        name are mistreated.

    $rv = $pkg->getDependencies()
        Will Connect to RM to get details about associated dependencies.  For each dependency will instantiate
        a new RmPkgInfo object and stores it in this object.  By default the new object will automatically try
        to retrieve details about its package.
        Returns 1 if fetched dependencies from RM, returns 0 if problems retieving data from RM.  Use foundDependencies()
        to determine if these have been found.
        The new objects created should be checked with foundPkg() &/or foundDetails() to see if they got the 
        package details.
    
    $rv = $pkg->foundDependencies()
        Returns true if dependencies details for package have been retrieved.

    @list = $pkg->getDependencyNames()
        Returns as a list of strings, the names of all dependencies found.

    $depPkg = $pkg->getDependencyObject(DepNam)
        Returns the RmPkgInfo object for the supplied Dependency name.

    
    Global Parameter modification functions

    $str = DeployUtils::RmPkgInfo->DefaultDescription(str)
    DeployUtils::RmPkgInfo->DefaultOverview(str)
    DeployUtils::RmPkgInfo->DefaultReason(str)
    DeployUtils::RmPkgInfo->DefaultLabel(str)
    DeployUtils::RmPkgInfo->DefaultDlocked(str)

=cut

package DeployUtils::RmPkgInfo;

use strict;
use DBI;
use DeployUtils::Logger;
use JatsRmApi;

# The eponymous meta object is a hash that stores class global data
# It has the same name as the package with the ":" replaced by "_".
# All keys that dont begin with a _ have accessor members
our %DeployUtils__RmPkgInfo = (
    _RM_CONN                    => undef,
    _CQ_DB                      => "dbi:ODBC:DEVI",         # SHOULD BE USED !!!!!
    _CQ_USER                    => "release_manager",
    _CQ_PASS                    => "release_manager",
    _CQ_CONN                    => undef,
    # This should match the entries in the _DETAILS hash element so accessors can be constructed.
    _DetailsKeys                => [ "pkg_id", "pkg_name", "pv_id", "pkg_version", "pv_description", "pv_overview", "pv_reason", "pv_label", "pv_dlocked", "pv_source_path", "pv_modified_time" ],
    # This should match the keys used for DEVI & TDSE issue hashes so accessors can be constructed
    _IssueKeys                  => [ "iss_numStr", "iss_state", "iss_id", "iss_summary", "iss_status", "iss_priority", "iss_type" ],
    # This should match the keys in the _RTDEPS hash for each package name so accessors can be created
    _RtDepKeys                  => [ "rt_name", "rt_version", "rt_comments" ],
    # Constants for Clearquest issues
    _enumCLEARQUEST_MASSI_ID    => 1,
    _enumCLEARQUEST_DPGIM_ID    => 2,
    _enumCLEARQUEST_TDSE_ID     => 3,
    _enumCLEARQUEST_DEVI_ID     => 4,
    _enumISSUES_STATE_FIXED     => 1,
    # Default strings for missing items
    DefaultDescription          => "No description available.",
    DefaultOverview             => "No overview available.",
    DefaultReason               => "No reason available.",
    DefaultLabel                => "No label available.",
    DefaultDlocked              => "N",
);



#==============================================================================
#   Constructor
# Parameter is a hash that must contain the following
# PKG_NAME = X & PKG_VERSION = X or
# PV_ID = X
#==============================================================================
sub new
{
    my $obclass = shift;
    my $class = ref($obclass) || $obclass;
    my $args = shift;

    LogError("RmPkgInfo::new Must supply Hash with PKG_NAME & PKG_VERSION keys or PKG_NAME & RTAG_ID or PV_ID key to instatiate object")
        unless ( ( defined($args->{PKG_NAME}) && defined($args->{PKG_VERSION}) ) ||
                 ( defined($args->{PKG_NAME}) && defined($args->{RTAG_ID}) ) ||
                 ( defined($args->{PV_ID}) ) );

    my $globals = _classobj();
    my $nowarn = $args->{NO_WARN} || 0;

    LogDebug("RmPkgInfo::new Instantiating new object of class $class");
    bless my $self = {
        _ARG_PKGNAME    => $args->{PKG_NAME},
        _ARG_PKGVER     => $args->{PKG_VERSION},
        _ARG_PV_ID      => $args->{PV_ID},
        _ARG_RTAG_ID    => $args->{RTAG_ID},
        _ARG_WIP        => $args->{WIP},
        _DETAILS        => { 
                                pkg_id          => 0,
                                pkg_name        => "",
                                pv_id           => 0,
                                pkg_version     => 0,
                                pv_description  => $globals->{DefaultDescription},
                                pv_overview     => $globals->{DefaultOverview},
                                pv_reason       => $globals->{DefaultReason},
                                pv_label        => $globals->{DefaultLabel},
                                pv_dlocked      => $globals->{DefaultDlocked},
                                pv_modified_time => undef,
                                pv_source_path  => undef
                           },
        # Contains the issue information in multi level hashs
        # The main hash is keyed on issue type, currently devi & tdse
        # The issue type hash contains another hash that is keyed on issue ID
        # and those issue id hashes are keyed on items defined in _IssueKeys in global hash
        _ISSUES         => { },
        # Records if getPkgIssues has been run
        _GETPKGISSUES   => 0,
        # Stores the runtime dependencies
        # The hash is keyed on package name and contains another hash with keys defined by _RTKeys
        _RTDEPS         => { },
        # Records if getRtDeps has been run
        _GETRTDEPS      => 0, 
        # A hash of this packages dependencies, the key is the dependency package name and the value is a RmPkgInfo object
        _DEPOBJECTS     => { },
        # Records if getDependencies has been run
        _GETDEPENDENCIES => 0
    } => ( $class );

    if ( $self->_connectRM() )
    {
        # PVID is the minimum so if we have it use it.
        if ( defined($self->{_ARG_PV_ID}) )
        {
            if ( ! $self->_getPkgDetailsByPVID() )
            {
                LogWarn("RmPkgInfo::new Unable to get Package Details By PVID From Release Manager") unless $nowarn;
            }
        }
        # else we need to try to use name, ver oand/or rtag ID
        else
        {
            # if we have Name & RTAG but no version then get version from name & rtag
            if ( defined($self->{_ARG_RTAG_ID}) && defined($self->{_ARG_PKGNAME}) && ! defined($self->{_ARG_PKGVER}) )
            {
                $self->{_ARG_PKGVER} = $self->_getLatestPkgVersion();
            }
            
            # now we should have name & ver if not report so 
            if ( defined($self->{_ARG_PKGNAME}) && defined($self->{_ARG_PKGVER}) )
            {
                if ( ! $self->_getPkgDetailsByName() )
                {
                    LogWarn("RmPkgInfo::new Unable to get Package Details By Name From Release Manager")unless $nowarn;
                }
            }
            else
            { 
                LogWarn("RmPkgInfo::new Dont have enough info to get any information")unless $nowarn;
            }
        }
    }

    return($self);
}   # new



#==============================================================================
# _connectRM
# Connects the DBI to release Manager if not connected
#==============================================================================
sub _connectRM
{
    my $self = shift;
    my $globals = _classobj();

    connectRM (\$globals->{_RM_CONN} ) if ( ! defined($globals->{_RM_CONN}));

    return 1;
}   # _connectRM


#==============================================================================
# _connectCQ
# Connects the DBI to Clear Quest if not connected
#==============================================================================
sub _connectCQ
{
    my $self = shift;
    my $globals = _classobj();

    if ( ! defined($globals->{_CQ_CONN}) )
    {
        $globals->{_CQ_CONN} = DBI->connect($globals->{_CQ_DB}, $globals->{_CQ_USER}, $globals->{_CQ_PASS});

        if ( ! defined($globals->{_CQ_CONN}) )
        {
            LogError("-x", "RmPkgInfo::_connectCQ Failed to connect to database [$globals->{_CQ_DB}]. [$DBI::errstr].");
            return 0;
        }
        else
        {
            LogDebug("RmPkgInfo::_connectCQ Connected to database [$globals->{_CQ_DB}].");
        }
    }
    return 1;
}   # _connectCQ


#------------------------------------------------------------------------------
# _getPkgDetailsByName
# This function retrieves the package version comment from the
# release manager database, given the RM package id and version.
#------------------------------------------------------------------------------
sub _getPkgDetailsByName
{
    my $self = shift;
    my $globals = _classobj();
    my $foundPkg = 0;
    my (@row);

    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectRM() );

    # First get the pkg_id from the name
    my ($m_sqlstr ) = "SELECT pkg_id, pkg_name FROM RELEASE_MANAGER.PACKAGES WHERE pkg_name=?";
    my ($sth )  = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_ARG_PKGNAME}) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $self->{_DETAILS}{pkg_id}   = $row[0];
                    $self->{_DETAILS}{pkg_name} = $row[1];
                    LogDebug("RmPkgInfo::_getPkgDetailsByName Found PkgID & Name [$self->{_DETAILS}{pkg_id}] [$self->{_DETAILS}{pkg_name}]");
                    $foundPkg = 1;
                    last;
                }
            }
            $sth->finish();
            if ( ! $foundPkg )
            {
                LogWarn("RmPkgInfo::_getPkgDetailsByName Unable to find Package [$self->{_ARG_PKGNAME}]");
                return 0;
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::_getPkgDetailsByName Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::_getPkgDetailsByName Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    # Now get details using found pkg_id & version
    $m_sqlstr = "SELECT pkg_id, pv_id, pv_description, pv_overview, comments, pkg_label, dlocked, pkg_version, SRC_PATH, TO_CHAR(MODIFIED_STAMP, 'DD-MON-YYYY') AS MODIFIED_STAMP" .
                " FROM RELEASE_MANAGER.PACKAGE_VERSIONS" .
                " WHERE pkg_id=? AND pkg_version=?";
    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_DETAILS}{pkg_id}, $self->{_ARG_PKGVER} ) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $self->{_DETAILS}{pkg_id}         = $row[0];
                    $self->{_DETAILS}{pv_id}          = $row[1];
                    $self->{_DETAILS}{pv_description} = $row[2] if ( $row[2] );
                    $self->{_DETAILS}{pv_overview}    = $row[3] if ( $row[3] );
                    $self->{_DETAILS}{pv_reason}      = $row[4] if ( $row[4] );
                    $self->{_DETAILS}{pv_label}       = $row[5] if ( $row[5] );
                    $self->{_DETAILS}{pv_dlocked}     = $row[6] if ( $row[6] );
                    $self->{_DETAILS}{pkg_version}    = $row[7] if ( $row[7] );
                    $self->{_DETAILS}{pv_source_path} = $row[8] if ( $row[8] );
                    $self->{_DETAILS}{pv_modified_time}= $row[9] if ( $row[9] );

                    LogDebug("RmPkgInfo::_getPkgDetailsByName Found Details for Package");
                    return 1;
                }
                LogWarn("RmPkgInfo::_getPkgDetailsByName Unable to find Package Details for [$self->{_ARG_PKGNAME}, $self->{_ARG_PKGVER}]");
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::_getPkgDetailsByName Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::_getPkgDetailsByName Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }
    return 0;
}



#------------------------------------------------------------------------------
# _getPkgDetailsByPVID
# This function retrieves the package version comment from the
# release manager database, given the Package Version id PVID.
#------------------------------------------------------------------------------
sub _getPkgDetailsByPVID
{
    my $self = shift;
    my $globals = _classobj();
    my $foundDetails = 0;
    my (@row);

    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectRM() );

    # First get details from pv_id
    my $m_sqlstr = "SELECT pkg_id, pv_id, pv_description, pv_overview, comments, pkg_label, dlocked, pkg_version, SRC_PATH, TO_CHAR(MODIFIED_STAMP, 'DD-MON-YYYY') AS MODIFIED_STAMP" .
                   " FROM RELEASE_MANAGER.PACKAGE_VERSIONS" .
                   " WHERE pv_id=?";
    my $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_ARG_PV_ID} ) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $self->{_DETAILS}{pkg_id}         = $row[0];
                    $self->{_DETAILS}{pv_id}          = $row[1];
                    $self->{_DETAILS}{pv_description} = $row[2] if ( $row[2] );
                    $self->{_DETAILS}{pv_overview}    = $row[3] if ( $row[3] );
                    $self->{_DETAILS}{pv_reason}      = $row[4] if ( $row[4] );
                    $self->{_DETAILS}{pv_label}       = $row[5] if ( $row[5] );
                    $self->{_DETAILS}{pv_dlocked}     = $row[6] if ( $row[6] );
                    $self->{_DETAILS}{pkg_version}    = $row[7] if ( $row[7] );
                    $self->{_DETAILS}{pv_source_path} = $row[8] if ( $row[8] );
                    $self->{_DETAILS}{pv_modified_time}= $row[9] if ( $row[9] );
                    LogDebug("RmPkgInfo::_getPkgDetailsByPVID Found Details for Package");
                    $foundDetails = 1;
                    last;
                }
            }
            $sth->finish();
            if ( ! $foundDetails )
            {
                LogWarn("RmPkgInfo::_getPkgDetailsByPVID Unable to find Package [$self->{_ARG_PV_ID}]");
                return 0;
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::_getPkgDetailsByPVID Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::_getPkgDetailsByPVID Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    # now get pkgName from found pkg_id
    $m_sqlstr = "SELECT pkg_id, pkg_name FROM RELEASE_MANAGER.PACKAGES WHERE pkg_id=?";
    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_DETAILS}{pkg_id} ) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $self->{_DETAILS}{pkg_id}   = $row[0];
                    $self->{_DETAILS}{pkg_name} = $row[1];
                    LogDebug("RmPkgInfo::_getPkgDetailsByPVID Found PkgID & Name [$self->{_DETAILS}{pkg_id}] [$self->{_DETAILS}{pkg_name}]");
                    return 1;
                }
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::_getPkgDetailsByPVID Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::_getPkgDetailsByPVID Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }
    return 0;
}


sub _getLatestPkgVersion
{ 
    my ( $self ) = shift;
    my $globals = _classobj();
    my $foundPkg = 0;
    my $pkgver = undef;
    my (@row);

    # if we are not or cannot connect then return 0 as we have not found anything
    return undef if ( ! $self->_connectRM() );

    # First get the pkg_id from the name
    my ($m_sqlstr );
    if ( defined($self->{_ARG_WIP}) && $self->{_ARG_WIP} != 0 )
    {
        LogDebug("RmPkgInfo::_getLatestPkgVersion Using Work In Progress View");
        $m_sqlstr = "SELECT pv.pkg_version FROM RELEASE_MANAGER.work_in_progress wip, RELEASE_MANAGER.package_versions pv, RELEASE_MANAGER.PACKAGES pkg WHERE ( wip.pv_id=pv.pv_id AND pv.pkg_id=pkg.pkg_id AND wip.rtag_id=? AND pkg.pkg_name=? )";
    }
    else
    {
        LogDebug("RmPkgInfo::_getLatestPkgVersion Using Released View");
        $m_sqlstr = "SELECT pv.pkg_version FROM RELEASE_MANAGER.release_content rel, RELEASE_MANAGER.package_versions pv, RELEASE_MANAGER.PACKAGES pkg WHERE ( rel.pv_id=pv.pv_id AND pv.pkg_id=pkg.pkg_id AND rel.rtag_id=? AND pkg.pkg_name=? )";
    }

    my ($sth )  = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_ARG_RTAG_ID}, $self->{_ARG_PKGNAME}) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $pkgver = $row[0];
                    LogDebug("RmPkgInfo::_getLatestPkgVersion Found Latest PVID [$pkgver]");
                    $foundPkg++;
                }
            }
            $sth->finish();
            if ( ! $foundPkg )
            {
                LogWarn("RmPkgInfo::_getLatestPkgVersion Unable to find Latest Version for [$self->{_ARG_PKGNAME}] in [$self->{_ARG_RTAG_ID}]");
                return undef;
            }
            elsif ( $foundPkg > 1 )
            { 
                LogWarn("RmPkgInfo::_getLatestPkgVersion Found more than 1 version of [$self->{_ARG_PKGNAME}] in [$self->{_ARG_RTAG_ID}]");
                return undef;
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::_getPkgDetailsByName Error executing query [$m_sqlstr] : " . $sth->errstr);
            return undef;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::_getPkgDetailsByName Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return undef;
    }

    return $pkgver;
}




#------------------------------------------------------------------------------
# getPkgIssues
# Updates the ISSUES hash containing information about assigned issues.
#------------------------------------------------------------------------------
sub getPkgIssues
{
    my $self = shift;
    my $globals = _classobj();

    my ($sth);
    my (@row);
    my ($m_sqlstr);
    my ($TDSEissues)  = "-1";
    my ($DEVIiss )    = "-1";

    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectRM() );

    $m_sqlstr = "SELECT iss_db, iss_id, iss_state, notes FROM RELEASE_MANAGER.CQ_ISSUES WHERE pv_id=?";
    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_DETAILS}{pv_id} ) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    if ( $row[0] == $globals->{_enumCLEARQUEST_DEVI_ID} )
                    {
                        $DEVIiss = "$DEVIiss" . "," . $row[1];
    
                        $self->{_ISSUES}{devi}{$row[1]}{iss_numStr} = $row[1];
                        $self->{_ISSUES}{devi}{$row[1]}{iss_state}  = $row[2];
                    }
                    elsif ( $row[0] == $globals->{_enumCLEARQUEST_TDSE_ID} )
                    {
                        $TDSEissues = "$TDSEissues" . "," . $row[1];

                        $self->{_ISSUES}{tdse}{$row[1]}{iss_numStr} = $row[1];
                        $self->{_ISSUES}{tdse}{$row[1]}{iss_state}  = $row[2];
                    }
                    else
                    {
                        LogWarn("RmPkgInfo::getPkgIssues Issue type [$row[0]] not supported.");
                    }
                }
            }
            $sth->finish();        
        }
        else
        {
            LogError("-x", "RmPkgInfo::getPkgIssues Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::getPkgIssues Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    LogInfo($self->pkg_name() . ":" . $self->pkg_version() . "DEVIiss =[$DEVIiss]");
    LogInfo($self->pkg_name() . ":" . $self->pkg_version() . "TDSEiss =[$TDSEissues]");

    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectCQ() );

    # now we create the SQL statement to get the detaisl from the CQ database
    if ( "$DEVIiss" ne "" || "$TDSEissues" ne "" )
    {
        $m_sqlstr = "\n" .
        "SELECT * FROM ( \n" .
        "       SELECT " . $globals->{_enumCLEARQUEST_DEVI_ID} . " AS iss_db,\n" .
        "               si.dbid AS iss_id, \n" .
        "               si.new_num AS iss_num, \n" .
        "               si.headline AS summary, \n" .
        "               sdef.name AS status, \n" .
        "               si.priority AS priority,\n" .
        "               si.issue_type AS issue_type\n".
        "       FROM  DEVI_PROD.admin.software_issue si INNER JOIN DEVI_PROD.admin.statedef sdef ON si.state = sdef.id\n" .
        "       WHERE  si.dbid IN ( $DEVIiss )\n" .
        "       UNION ALL\n" .
        "       SELECT " . $globals->{_enumCLEARQUEST_TDSE_ID} . " AS iss_db,\n" .
        "               si.dbid AS iss_id, \n" .
        "               si.job_number AS iss_num, \n" .
        "               si.problem_summary AS summary, \n" .
        "               sdef.name AS status, \n" .
        "               si.priority AS priority,\n" .
        "               NULL AS issue_type\n" .
        "       FROM  TDSE_2002.admin.request si INNER JOIN TDSE_2002.admin.statedef sdef ON si.state = sdef.id\n" .
        "       WHERE  si.dbid IN ( $TDSEissues )\n" .
        "     ) AS issues ORDER BY iss_num ASC";
    
        undef($sth);
        $sth = $globals->{_CQ_CONN}->prepare($m_sqlstr);
        if ( defined($sth) )
        {
            if ( $sth->execute() )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    if ( $row[0] == $globals->{_enumCLEARQUEST_DEVI_ID} )
                    {
                        $self->{_ISSUES}{devi}{$row[1]}{iss_id}       = $row[1];
                        $self->{_ISSUES}{devi}{$row[1]}{iss_numStr}   = $row[2];
                        $self->{_ISSUES}{devi}{$row[1]}{iss_summary}  = $row[3];
                        $self->{_ISSUES}{devi}{$row[1]}{iss_status}   = $row[4];
                        $self->{_ISSUES}{devi}{$row[1]}{iss_priority} = $row[5];

                        if ( $row[6] )
                        {
                            $self->{_ISSUES}{devi}{$row[1]}{iss_type} = $row[6];
                        }                                             
                        else
                        {
                            $self->{_ISSUES}{devi}{$row[1]}{iss_type} = "Not Known";
                        }
            
                    }
                    elsif ( $row[0] == $globals->{_enumCLEARQUEST_TDSE_ID} )
                    {
                        $self->{_ISSUES}{tdse}{$row[1]}{iss_id}       = $row[1];
                        $self->{_ISSUES}{tdse}{$row[1]}{iss_numStr}   = $row[2];
                        $self->{_ISSUES}{tdse}{$row[1]}{iss_summary}  = $row[3];
                        $self->{_ISSUES}{tdse}{$row[1]}{iss_status}   = $row[4];
                        $self->{_ISSUES}{tdse}{$row[1]}{iss_priority} = $row[5];

                        if ( $row[6] )
                        {
                            $self->{_ISSUES}{tdse}{$row[1]}{iss_type} = $row[6];
                        }
                        else
                        {
                            $self->{_ISSUES}{tdse}{$row[1]}{iss_type} = "Not Known";
                        }
                    }
                    else
                    {
                        LogWarn("RmPkgInfo::getPkgIssues Issue type [$row[0]] not supported.");
                    }
                }
            }
            else
            {
                LogError("-x", "RmPkgInfo::getPkgIssues Error executing query [$m_sqlstr] : " . $sth->errstr);
                return 0;
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::getPkgIssues Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
            return 0;
        }
    }

    $self->{_GETPKGISSUES} = 1;
    return 1;
}



#------------------------------------------------------------------------------
# getRtDeps
# Populates the RTDeps Hash with runtime dependencies
#------------------------------------------------------------------------------
sub getRtDeps
{
    my $self = shift;
    my $globals = _classobj();

    my ($sth );
    my (@row);
    my ($m_sqlstr);

    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectRM() );

    $m_sqlstr = "SELECT pkg.pkg_name, pv.pkg_version, rd.rtd_comments, rd.rtd_url, rd.mod_date, \n" .
                "usr.full_name, usr.user_email \n" .
                "FROM RELEASE_MANAGER.runtime_dependencies rd, RELEASE_MANAGER.package_versions pv, RELEASE_MANAGER.PACKAGES pkg, RELEASE_MANAGER.users usr \n".
                "WHERE pv.pkg_id = pkg.pkg_id \n" .
                "AND rd.rtd_id = pv.pv_id \n" .
                "AND rd.mod_user = usr.user_id \n" .
                "AND rd.pv_id = ?\n" .
                "ORDER BY UPPER(pkg.pkg_name) ASC";

    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_DETAILS}{pv_id} ) ) 
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $self->{_RTDEPS}{$row[0]}{rt_name}    = $row[0];
                    $self->{_RTDEPS}{$row[0]}{rt_version} = $row[1];
                    if ( $row[2] )
                    {
                        $self->{_RTDEPS}{$row[0]}{rt_comments} = $row[2];
                    }
                    else
                    {
                        $self->{_RTDEPS}{$row[0]}{rt_comments} = "None";
                    }
                }
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::getRtDeps Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::getRtDeps Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    $self->{_GETRTDEPS} = 1;
    return 1;
}


#------------------------------------------------------------------------------
# zapProductContents
#
# This function ZAPs the contents of a product.
#
# The contents are determined by 'pv_id' and a 'machtype' both of which are
# set during build time and are globally accessible.
#
#------------------------------------------------------------------------------
sub zapProductContents
{
    my $self = shift;
    my $globals = _classobj();

    my ( $platform ) = shift;

    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectRM() );

    LogNorm("RmPkgInfo::zapProductContents Removing Contents for [$self->{_DETAILS}{pv_id}], [$platform]");

    my ($sth );
    my ($m_sqlstr);

    $m_sqlstr = "BEGIN PK_BUILDAPI.Remove_All_Product_Components(?,?); END;";

    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( ! $sth->execute( $self->{_DETAILS}{pv_id},  $platform ) )
        {
            LogError("-x", "RmPkgInfo::zapProductContents Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::zapProductContents Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    # done
    return 1;
}


#------------------------------------------------------------------------------
# insertProductContentItem
#
# This function inserts and item into the product contents table.
#
# The 'pv_id' and a 'machtype' are set during build time and are globally accessible.
# ans are not required to be passed.
#
#------------------------------------------------------------------------------
sub insertProductContentItem
{
    my $self = shift;
    my $globals = _classobj();

    my ($platform, $sOrigFilePath, $sFileName, $sDestFilePath, $nByteSize, $sCRCcksum) = @_;
    
    # if we are not or cannot connect then return 0 as we have not found anything
    return 0 if ( ! $self->_connectRM() );

    LogNorm ("RmPkgInfo::insertProductContentItem: [$self->{_DETAILS}{pv_id}], [$platform], [$sOrigFilePath], [$sFileName], [$sDestFilePath], [$nByteSize], [$sCRCcksum]");


    my ($sth );
    my ($m_sqlstr);

    $m_sqlstr = "BEGIN PK_BUILDAPI.Add_Product_Component( ?, ?, ?, ?, ?, ?, ?); END;";

    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( ! $sth->execute( $self->{_DETAILS}{pv_id}, $platform, $sOrigFilePath, $sFileName, $sDestFilePath, $nByteSize, $sCRCcksum ) )
        {
            LogError("-x", "RmPkgInfo::insertProductContentItem Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::insertProductContentItem Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }
    # done
    return 1;
}


#==============================================================================
#   getDependencies
#==============================================================================
sub getDependencies
{
    my $self = shift;
    my $globals = _classobj();

    my ($obj, $name);
    my ($m_sqlstr);
    my ($sth);
    my (@row);

    # First we get the list of dependenciy pvid's and use them to create new objects
    $m_sqlstr = "select dpv_id from RELEASE_MANAGER.package_dependencies where pv_id=?";
    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_DETAILS}{pv_id} ) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $obj = DeployUtils::RmPkgInfo->new( { PV_ID => $row[0] } );
                    if ( $obj->foundDetails() )
                    {
                        $name = $obj->pkg_name();
                        $self->{_DEPOBJECTS}{$name} = $obj;
                        LogDebug("RmPkgInfo::getDependencies Created Dependency object for $name");
                    }
                }
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::getDependencies Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::getDependencies Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    $self->{_GETDEPENDENCIES} = 1;
    return 1;
}   # getDependencies

#==============================================================================
#   getDependenciesHash
#==============================================================================
sub getDependenciesHash
{
    my $self = shift;
    my $globals = _classobj();

    my ($obj, $name);
    my ($m_sqlstr);
    my ($sth);
    my (@row);
    my %result;

    # First we get the list of dependenciy pvid's
    $m_sqlstr = "SELECT pkg.PKG_NAME, pv.PKG_VERSION FROM RELEASE_MANAGER.PACKAGE_DEPENDENCIES pd, RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg WHERE pd.PV_ID=? AND pd.DPV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID";
    $sth = $globals->{_RM_CONN}->prepare($m_sqlstr);
    if ( defined($sth) )
    {
        if ( $sth->execute( $self->{_DETAILS}{pv_id} ) )
        {
            if ( $sth->rows )
            {
                while ( @row = $sth->fetchrow_array )
                {
                    $result{$row[0]}{$row[1]} = 1;
                }
            }
        }
        else
        {
            LogError("-x", "RmPkgInfo::getDependenciesHash Error executing query [$m_sqlstr] : " . $sth->errstr);
            return 0;
        }
    }
    else
    {
        LogError("-x", "RmPkgInfo::getDependenciesHash Unable to prepare SQL Statement [$m_sqlstr] : " . $globals->{_RM_CONN}->errstr);
        return 0;
    }

    return \%result;
}   # getDependenciesHash


#==============================================================================
# getDependencyNames
# Returns the keys from the _DEPOBJECTS hash that are the names of the dependencies
#==============================================================================
sub getDependencyNames
{
    my $self = shift;

    $self->getDependencies() if ( ! $self->foundDependencies() );

    return sort keys %{$self->{_DEPOBJECTS}};
}   # getDependencyNames



#==============================================================================
# getDependencyObject
# Returns the RmPkgInfo object for name
#==============================================================================
sub getDependencyObject
{
    my $self = shift;
    my $name = shift;

    LogError("RmPkgInfo::getDependencyObject Must supply Dependency name") if ( ! defined($name) );

    $self->getDependencies() if ( ! $self->foundDependencies() );

    return $self->{_DEPOBJECTS}{$name};
}   # getDependencyObject


#==============================================================================
# getIssueTypes
# Returns the issues types stored in the ISSUES array, should be devi and/or tdse
#==============================================================================
sub getIssueTypes
{
    my $self = shift;

    # Call getPkgIssues if not already
    $self->getPkgIssues() if ( ! $self->foundIssues() );

    return keys %{$self->{_ISSUES}};
}   # getIssueTypes



#==============================================================================
#   getIssueNumbers
# Returns a list of the issue numbers for the associated issue type passed
#==============================================================================
sub getIssueNumbers
{
    my $self = shift;
    my $issType = shift;
    LogError("RmPkgInfo::getIssueNumbers Must supply issue type") if ( ! defined($issType) );
    # Call getPkgIssues if not already
    $self->getPkgIssues() if ( ! $self->foundIssues() );
    return sort keys %{$self->{_ISSUES}{$issType}};
}   # getIssueNumbers



#==============================================================================
#   getRtDepNames
# Returns a list of the Runtime Dependencies found
#==============================================================================
sub getRtDepNames
{
    my $self = shift;
    $self->getRtDeps() if ( ! $self->foundRtDeps() );
    return keys %{$self->{_RTDEPS}};
}   # getRtDepNames


#==============================================================================
# foundPkg
# Returns TRUE if the package has been found in RM otherwise FALSE.
# A pkg is found if the pkg_id is non 0
#==============================================================================
sub foundPkg
{
    my $self = shift;

    return ( $self->{_DETAILS}{pkg_id} != 0 ) ? 1 : 0;
}   # foundPkg



#==============================================================================
# foundDetails
# Returns TRUE if the package details has been found in RM otherwise FALSE.
# A pkg is found if the pv_id is non 0
#==============================================================================
sub foundDetails
{
    my $self = shift;

    return ( $self->{_DETAILS}{pv_id} != 0 ) ? 1 : 0;
}   # foundDetails



#==============================================================================
# foundIssues
# Returns TRUE if the package Issues has been found in RM otherwise FALSE.
#==============================================================================
sub foundIssues
{
    my $self = shift;

    return $self->{_GETPKGISSUES};
}   # foundIssues



#==============================================================================
# foundRtDeps
# Returns TRUE if the package RtDeps has been found in RM otherwise FALSE.
#==============================================================================
sub foundRtDeps
{
    my $self = shift;

    return $self->{_GETRTDEPS};
}   # foundRtDeps



#==============================================================================
# foundDependencies
# Returns TRUE if the package Dependencies has been found in RM otherwise FALSE.
#==============================================================================
sub foundDependencies
{
    my $self = shift;

    return $self->{_GETDEPENDENCIES};
}   # foundDependencies



#==============================================================================
# _classobj
# Internal member that is tri-natured: can be called as function, class method, 
# or object method.
# It returns a reference to the eponymous hash for access to class global data
#==============================================================================
sub _classobj 
{
    my $obclass = shift || __PACKAGE__;
    my $class   = ref($obclass) || $obclass;
    $class =~ s/:/_/g;  # Replace all ": with "_" to get the name of the hash
    no strict "refs";   # to convert sym ref to real one
    return \%$class;
}


#==============================================================================
#   dumpSelf, debugging member to dump selfs hash
#==============================================================================
sub dumpSelf
{
    use Data::Dumper;

    my $self = shift;

    print Data::Dumper->Dump([$self, $self->_classobj()], [ref($self), "Globals"]);
}   # dumpSelf



# This code is executed when the package is included and generates accessor methods for accessing
# global vars in the eponymous object
for my $datum (grep(!/^_/, keys %{ _classobj() }) ) 
{ 
    # turn off strict refs so that we can
    # register a method in the symbol table
    no strict "refs";       
    *$datum = sub {
        use strict "refs";
        my $self = shift->_classobj();
        $self->{$datum} = shift if @_;
        return $self->{$datum};
    }
}

# This code is executed when the package is included and generates accessor methods for accessing
# self data from the details hash
for my $datum ( @{_classobj()->{_DetailsKeys}} ) 
{ 
    # turn off strict refs so that we can
    # register a method in the symbol table
    no strict "refs";       
    *$datum = sub {
        use strict "refs";
        my $self = shift;
        return $self->{_DETAILS}{$datum};
    }
}

# This code is executed when the package is included and generates accessor methods for accessing
# self data from the Issues Hash
for my $datum ( @{_classobj()->{_IssueKeys}} ) 
{ 
    # turn off strict refs so that we can
    # register a method in the symbol table
    no strict "refs";       
    *$datum = sub {
        use strict "refs";
        my $self = shift;
        my $issType = shift;
        my $issId = shift;
        LogError("RmPkgInfo::$datum Must supply IssueType & Issue ID as parameters") if ( ! defined($issId) );
        # Call getPkgIssues if not already
        $self->getPkgIssues() if ( ! $self->foundIssues() );
        return $self->{_ISSUES}{$issType}{$issId}{$datum};
    }
}


# This code is executed when the package is included and generates accessor methods for accessing
# self data from the RTDEPS Hash
for my $datum ( @{_classobj()->{_RtDepKeys}} ) 
{ 
    # turn off strict refs so that we can
    # register a method in the symbol table
    no strict "refs";       
    *$datum = sub {
        use strict "refs";
        my $self = shift;
        my $RtName = shift;
        LogError("RmPkgInfo::$datum Must supply Runtime Dependency Name as parameter") if ( ! defined($RtName) );
        # Call getRtDeps if not already
        $self->getRtDeps() if ( ! $self->foundRtDeps() );
        return $self->{_RTDEPS}{$RtName}{$datum};
    }
}

1;