Scripts/WorkAreaUpdates/CDOT_UpdatePWProjects_v02.ps1

#####################################################################################################################################################################################################################
##############################| |####################################################################################################################################################################################
##############################| |#
############## ########| |# Name : PWPS_CDOT_UpdatePWProjects_v02
########### #####| |# Type : Script
######### ####### ####| |# Purpose : Update Project Information in Cloud Datasource
######## ############ ####| |# Author : Adrian J. Crowfoot
####### ##### ##### ####| |# Creation Date : 08/01/2019
############## ##### #####| |# Modified By : Adrian J. Crowfoot
############# ### ######| |# Modified Date : 12/03/2019
########## #######| |# Script Version : 1.3
######## ##########| |# PowerShell Version : 5.1.16299.1146
######## ##############| |# ProjectWise Version : 10.00.03.167
######### #########| |# PWPS Module Version : 10.0.2.2
######## ###### ########| |# PWPS_DAB Module Version : 1.16.3.0
####### ######### #######| |# PWPS_CDOT Module Version : 1.0.2.3
####### ########## #######| |#
###### ######### ########| |# Requirements:
###### #### ########| |# - Ability to log into a ProjectWise Datasource with Administrative privileges.
###### ##########| |# - Data Table using Get-PWRichProjectReport and other parameters
######### #############| |# - Logged into a ProjectWise datasource.
##############################| |# -
##############################| |#
#####################################################################################################################################################################################################################
##
## Description/Notes:
## - [AJC 20190917] Added in routine to update project properties only if SAP Changes have been made since last run
## - [AJC 20190917] If Project Number property is blank, it is filled in with the first five characters of the project's folder name
## - [AJC 20191127] Reworked script to require an active PW Login and to simplify logging.
## - [AJC 20191203] Line 207 - $dtPWRichProject is now filtered down to only the parent rich project
## - [AJC 20200925] Line 201 - Needed to push output from Get-PWFolders over pipeline to Get-PWFolderPathAndProperties to really get Rich Project Properties
## - [AJC 20200925] Line 267 - Now skips Update date and user if no update is needed
## - [AJC 20200928] Line 218 - New switch parameter -Full will update any property that changed regardless of SAP change date
##
#####################################################################################################################################################################################################################
##
## This script is provided for example purposes only. Bentley makes no warranty as to the effectiveness of this script in a specific situation or for a specific user.
## Use of this script or a similar script in a production environment is entirely at the risk of the user. Bentley suggests extensive testing before deployment.
##
#####################################################################################################################################################################################################################

#Requires -Version 5.0
#Requires -Modules @{ModuleName="PWPS_DAB";ModuleVersion='1.12.3.0'}
#Requires -Modules @{ModuleName="PWPS_CDOT";ModuleVersion='1.0.0.1'}
##Requires -RunAsAdministrator

<#PSScriptInfo
 
.VERSION 2.6
 
.GUID a3474eff-53fd-47b3-a11c-b5c6efabfe9f
 
.AUTHOR adrian.crowfoot@state.co.us
 
.COMPANYNAME Colorado DOT
 
.TAGS ProjectWise
 
.EXTERNALMODULEDEPENDENCIES
 PWPS_CDOT
 PWPS_DAB
 
.RELEASENOTES
 
#>


<#
 
.DESCRIPTION
 Update Rich Project Information in Datasource for a given Rich Project Type from a SQL Database
 
#>
 

[CmdletBinding()]
param(
    ###########################################################################################
    # Parameters for Rich Project Comparison Parameters
    ###########################################################################################
    [Parameter(Mandatory =$true)]
    [System.Data.DataTable] $dtPWRichProjects, # Input Rich Project Datatable
    [Parameter( Mandatory = $false )]
    [string] $PWProjectType = 'CDOT_INFO',
    [Parameter( Mandatory = $false )]
    [string] $PWProjectNumberProperty = 'PROJECT_Project_Code',
    [Parameter( Mandatory = $false )]
    [string] $PWProjectTimeStampUpdateProperty = 'PROJECT_SAP_Change_Date',
    
    ###########################################################################################
    # Define SQL Parameters
    ###########################################################################################

    # SQL Server Information (uses Windows Authentication)
    [Parameter(
            HelpMessage = "SQL Server that houses input database and table",
            Mandatory = $true )]
    [string] $SQLServer, # = 'IntProdListener',
    [Parameter(
            HelpMessage = "SQL Server Instance that houses input database and table",
            Mandatory = $true )]
    [string] $SQLInstanceName, # = 'Default',
    [Parameter(
            HelpMessage = "SQL database that contains input table(s)",
            Mandatory = $true )]
    [string] $SQLDatabase, # = 'CDOTDATA',

    # Edit the SQL Query file to get the correct columns from CDOTDATA
    [Parameter(
            HelpMessage = "SQL Query file that returns the input datatable",
            Mandatory = $true )]
    [string] $SqlQueryFile, # = "$(Get-CDOTModuleLocation)\CDOTDATA_ProjectInfo.sql",

    # Add the WHERE part of the SELECT Statement here - use to switch based on parameter later
    # Full Processing
    [Parameter(
            HelpMessage = "(optional) Where clause to append to the SQL SELECT Statement",
            Mandatory = $false )]
    [string] $SqlWhereCondition = "",

    ###########################################################################################
    # Script and Log File Information
    ###########################################################################################
    [switch] $Full,
    [ValidateNotNullOrEmpty()]
    [ValidateScript( { Test-Path -Path $_ -PathType Container })]
    [Parameter( 
            HelpMessage = "Location of script file and log folder.",
            Mandatory = $true,
        Position = 3 )]
    [string] $Path,    
    [ValidateNotNullOrEmpty()]
    [Parameter(
            HelpMessage = "Log file to be generated for each run of the script.",
            Mandatory = $true,
        Position = 7 )]
    [string] $LogFilePathName

)

BEGIN {

    $CmdletName = $MyInvocation.MyCommand.Name
    
    Write-PWPSLog -Message " - " -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
    Write-PWPSLog -Message "[BEGIN] Entering Update Rich Projects script." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
    
    $ScriptStartTime = Get-Date
    Write-PWPSLog -Message "[BEGIN] Start time: $ScriptStartTime" -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
    
    #region Check Database results
        # SQL Connection string to load from correct instance of CDOTDATA
        $SQLConnectionString = "Server=$SQLServer; Database=$SQLDatabase; Integrated Security=true;Connect Timeout=30"

        # A. Build the SQL Query from the SQL Query File and the SQL Where Condition
        $SqlQuery = $(Get-Content $SqlQueryFile -Raw) + " " + $SqlWhereCondition

        # B. Create Data Table using Select-SQLDataTable function in PWPS_CDOT

        try {
            $dtCDOTDATA = Select-CDOTSQLDataTable -SQLConnectionString $SQLConnectionString -SqlQuery $SqlQuery -TableName $PWProjectType -InformationAction Stop -InformationVariable info

            if($dtCDOTDATA.Rows.Count -eq 0) {
                throw "No Rows in Data table"
            }

            Write-PWPSLog -Message "[BEGIN] $($dtCDOTDATA.Rows.Count) Rows in Data table" -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
        }
        catch {
            $Line = $($Error[0].InvocationInfo.ScriptLineNumber)
            $ErrorMessage = $($Error[0].Exception.Message)      
            Write-PWPSLog -Message "[BEGIN] Occurred on line: $Line. $ErrorMessage" -Path $LogFilePathName -Level Warn -Cmdlet $CmdletName
            break
        }
        
    #endregion Check Database results
    
} # end BEGIN

PROCESS {
    Write-PWPSLog -Message "[PROCESS] Continuing with processing script." -LogPath $LogFilePathName -Level Info -Cmdlet $CmdletName

##################################################################################################################################
#
# EDIT THIS SECTION [20191205 AJC] - CYCLE THROUGH INPUT PROJECTS TABLE AND UPDATE PROPS
# 1. If project number is null or 0, write error folder path not processed, then 'break' loop
# 2. Get rows from SQL Datatable for project number
# 3. Get folder object for project
# 4. Compare rich project props to SQL dt row values - log differences discreetly
# 5. Update Rich Project Props and folder if values are different
#
##################################################################################################################################
    # Process each row in Rich Project Datatable to find which ones need SAP Update
    foreach ($row in $dtPWRichProjects.Rows){
        
        # Get Project Number and retrieve data row(s) with Project Number
        $projectNumber = $row.$PWProjectNumberProperty

        # Filter table to rows containing asset number
        $drFilteredRows = $dtCDOTDATA.Select("$PWProjectNumberProperty = '$projectNumber'")

        if ($drFilteredRows.Count -gt 0) {
            # Project Code exists in SAP
            #region update rich project if needed
                # get this from current row in rich project list
                $richProjectFolder = Get-PWFolders -FolderPath $row.ExistingProjectPath -JustOne | Get-PWFolderPathAndProperties

                $PWRichProjectKeys = $richProjectFolder.ProjectProperties.Keys -like "PROJECT_*"


                # Get update date in CDOTDATA and rich project
                try {$databasePropertyValue = $($drFilteredRows[0].$PWProjectTimeStampUpdateProperty).ToString(“yyyy-MM-dd") }
                catch { $databasePropertyValue =  ""}

                try { $PWPropertyValue = $([datetime]$richProjectFolder.ProjectProperties.$PWProjectTimeStampUpdateProperty).ToString(“yyyy-MM-dd") }
                catch { $PWPropertyValue =  "" }


                # If the values are different OR if Full Run, update the project props - make hash table first
                if($databasePropertyValue -ne $PWPropertyValue -or $Full) {
                    Write-PWPSLog -Message "[PROCESS] UPDATING $($richProjectFolder.FullPath)" -Path $LogFilePathName -Level Info -Cmdlet $CmdletName

                    # Reset HashTable for PW Rich Project Properties
                    $PWRichProject_Properties = @{}

                    foreach ($key in $PWRichProjectKeys){
                        if ($drFilteredRows[0].$key) {
                            # Reset compare values
                            $dbCompareValue = ''
                            $PWCompareValue = ''

            
                            try { $dbCompareValue = $($drFilteredRows[0].$key).ToString(“yyyy-MM-dd hh:mm:ss") }
                            catch{
                
                                try {
                                    $dbCompareValue = $drFilteredRows[0].$key
                                    $dbCompareValue = $dbCompareValue.Trim()
                                }
                                catch { $dbCompareValue = '' }
                            }

                            try {$PWCompareValue = ([datetime]$richProjectFolder.ProjectProperties.$key).ToString(“yyyy-MM-dd hh:mm:ss")}
                            catch{$PWCompareValue = $richProjectFolder.ProjectProperties.$key}
            
                            if ($dbCompareValue -ne $PWCompareValue) {
                                $PWRichProject_Properties.$key = $dbCompareValue
                                Write-PWPSLog -Message "[PROCESS] $projectNumber`: $key changed from $PWCompareValue to $dbCompareValue." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
                            }
                        }
                    }

                    # Count Updated properties. Skip update if no changes are found
                    if ($PWRichProject_Properties.Count -gt 0) {
                        # Try update
                        # adjust the update date and user
                        $PWRichProject_Properties.PROJECT_PWZ_Project_Update_User = $PWUserName
                        $PWRichProject_Properties.PROJECT_PWZ_Project_Update_Date = (Get-Date).ToString(“yyyy-MM-dd hh:mm:ss")

                        if( -not (Update-PWRichProjectProperties -InputFolder $richProjectFolder -ProjectProperties $PWRichProject_Properties -Verbose)){
                            Write-PWPSLog -Message "[PROCESS] $projectNumber`: Failed to update properties for $($richProjectFolder.FullPath)." -Path $LogFilePathName -Level Warn -Cmdlet $CmdletName
                        }
                        else {
                            Write-PWPSLog -Message "[PROCESS] $projectNumber`: $($richProjectFolder.FullPath) was changed." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
                        }
                    }
                    else {
                        # Log NO UPDATE NEEDED
                        Write-PWPSLog -Message "[PROCESS] $projectNumber`: No changes needed for $($richProjectFolder.FullPath)." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
                    }

    
                }
                else {
                    Write-PWPSLog -Message "[PROCESS] $projectNumber`: NO CHANGES ($($richProjectFolder.FullPath))." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
                }
            #endregion update rich project if needed
        }
        else {
            # Project Code does not exist in SAP
            Write-PWPSLog -Message "[PROCESS] '$projectNumber'`: Does not exist in SAP ($($row.ExistingProjectPath))." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
        }

            
    } # end foreach $row


} # end PROCESS

END {
    $ScriptEndTime = Get-Date
    Write-PWPSLog -Message "[END] It took $([Math]::Round($ScriptEndTime.Subtract($ScriptStartTime).TotalMinutes, 2)) minutes to complete the process." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName

    Write-PWPSLog -Message "[END] Leaving script." -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
    Write-PWPSLog -Message " - " -Path $LogFilePathName -Level Info -Cmdlet $CmdletName
    
} # end END