Obs/bin/TestObservability/HelperFiles/HTMLReportHelper.psm1

<##############################################################
# #
# Copyright (C) Microsoft Corporation. All rights reserved. #
# #
##############################################################>


Import-Module $PSScriptRoot\..\HelperFiles\TestObsHelper.psm1 -Force

# CSS
$header = @"
<style>
 
    h1 {
        font-family: Arial, Helvetica, sans-serif;
        color: blue;
        font-size: 28px;
    }
 
    h2 {
        font-family: Arial, Helvetica, sans-serif;
        color: #0078d4;
        font-size: 16px;
    }
 
    p {
        margin: 5px
    }
 
   table {
        font-size: 12px;
        border: 0px;
        font-family: Arial, Helvetica, sans-serif;
    }
     
    td {
        padding: 4px;
        margin: 0px;
        border: 0;
        max-width: 350px;
        max-height: 350px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
 
    td:hover {
        overflow: visible;
        white-space: normal;
        height:auto;
        max-width:auto;
    }
     
    th {
        background: linear-gradient(#243a5e, #0078d4);
        color: #fff;
        font-size: 11px;
        padding: 10px 15px;
        vertical-align: middle;
    }
 
    tbody tr:nth-child(even) {
        background: #f0f0f2;
    }
 
    tbody tr:nth-child(odd) {
        background: white
    }
 
    .pass {
        color: green;
    }
     
    .fail {
        color: red;
    }
 
</style>
"@


function Generate-HTMLReport
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]
        $PathToTestReports,

        [parameter(Mandatory=$true)]
        [string[]]
        $TestNames,

        [parameter(Mandatory=$true)]
        [switch]
        $Repair
    )
    
    $startTime = (Get-Date)
    $title = "Test-Observability Report"
    $reportHTML = "<h1>$title</h1>"
    
    $testReports = Get-ChildItem $PathToTestReports
    $overallResult = Get-OverallResult -TestReports $TestReports -TestNames $TestNames -Repair:$Repair
    $overallResult = Style-Result -Result $overallResult
    $reportHTML += "<h1>Overall Result: $overallResult</h1>"

    $reportHTML += Get-GeneralInformation -TestReports $testReports
    $reportHTML += "<br><br>"
    foreach ($testName in $testNames)
    {
        $testHasRepairAction = Get-TestRepairAction -TestName $TestName
        if ($Repair -and $testHasRepairAction)
        {
            $reportHTML += Create-HTMLForTest -TestReports $testReports -TestName $testName -RepairAction "BeforeRepair"
            $reportHTML += Create-HTMLForTest -TestReports $testReports -TestName $testName -RepairAction "AfterRepair"
        }
        else
        {
            $reportHTML += Create-HTMLForTest -TestReports $testReports -TestName $testName -RepairAction "NoRepair"
        }
    }
    $report = ConvertTo-HTML -Body $reportHTML -Head $header -Title $title -PostContent "<p>Html Report Generated on: $(Get-Date)</p>"
    $report = $report -replace "<td>PASS</td>","<td class='pass'>PASS</td>"
    $report = $report -replace "<td>FAIL</td>","<td class='fail'>FAIL</td>"
    $outputFilePath = Split-Path $PathToTestReports -Parent
    $currentDate = (get-date).ToString("MMddyyyy_HHmm")
    $reportPath = Join-Path $outputFilePath "TestObservability-$currentDate.html"
    
    Trace-Progress "Html Report generated in $('{0:N2}' -f ((Get-Date) - $startTime).totalSeconds)s"
    $report | Out-File $reportPath
    return $reportPath
}

function Get-GeneralInformation
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [Object[]]
        $TestReports
    )

    try
    {
        $generalInfoJSON = Join-Path $PathToTestReports "GeneralReportInfo.json"
        $generalInfo = Get-Content $generalInfoJSON | ConvertFrom-JSON
        if ($null -eq $generalInfo)
        {
            return "<h2>General Information</h2><p class='fail'>Could not get general environment information.</p>"
        }
        $generalInfo = ConvertTo-Html -InputObject ($generalInfo) -As List -Fragment -PreContent "<h2>General Information</h2>"
        return $generalInfo
    }
    catch
    {
        return "<h2>General Information</h2><p class='fail'>Could not get general environment information.</p>"
    }
    
}

function Get-OverallResult
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [Object[]]
        $TestReports,

        [parameter(Mandatory=$true)]
        [string[]]
        $TestNames,

        [parameter(Mandatory=$true)]
        [switch]
        $Repair
    )

    $fail = "FAIL"
    $pass = "PASS"

    if ($null -eq $TestReports)
    {
        return $fail
    }
    foreach ($testName in $TestNames)
    {
        $testHasRepairAction = Get-TestRepairAction -TestName $TestName
        if ($Repair -And $testHasRepairAction)
        {
            $reportFailed = Check-IfTestFailed -TestReports $TestReports -TestName $testName -RepairAction "BeforeRepair"
            # If BeforeRepair failed, OK as long as after repair passed
            # Also, if BeforeRepair passed, there shouldn't be a report for "AfterRepair", so skip test.
            if ($reportFailed)
            {
                $reportFailed = Check-IfTestFailed -TestReports $TestReports -TestName $testName -RepairAction "AfterRepair"
                if ($reportFailed)
                {
                    return $fail
                }
            }
        }  
        else
        {
            $reportFailed = Check-IfTestFailed -TestReports $TestReports -TestName $testName -RepairAction "NoRepair"
            if ($reportFailed)
            {
                return $fail
            }
        }
    }
    return $pass
}

function Check-IfTestFailed
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [Object[]]
        $TestReports,

        [parameter(Mandatory=$true)]
        [string]
        $TestName,

        [parameter(Mandatory=$true)]
        [string]
        $RepairAction
    )
    $reportData = Get-ReportData -TestReports $TestReports -TestName $testName -RepairAction $RepairAction
    return ($null -eq $reportData -or ($null -eq $reportData.OverallResult -or $reportData.OverallResult -ne $pass))
}

function Get-ReportData
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [Object[]]
        $TestReports,

        [parameter(Mandatory=$true)]
        [string]
        $TestName,

        [parameter(Mandatory=$true)]
        [string]
        $RepairAction
    )
    $jsonFileName = Get-JsonFileName -TestName $testName -RepairAction $RepairAction

    $reportName = $jsonFileName += ".json"
    $report = $TestReports | Where-Object { $_.Name -eq $ReportName }
    if ($null -eq $report)
    {
        return $null
    }
    return Get-Content -Path $report.FullName | ConvertFrom-Json
}

function No-TestData
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]
        $TestName
    )

     $reportHTML = "<b>Test Name:</b> $TestName"
     $reportHTML += "<p><b>Overall Result: </b>"
     $reportHTML += Style-Result -Result "FAIL"
     $reportHTML += "</p>"
     $reportHTML += "<p>Could not find data for this test.</p>"

     return $reportHTML
}

function Create-HTMLForTest
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [Object[]]
        $TestReports,

        [parameter(Mandatory=$true)]
        [string]
        $TestName,

        [parameter(Mandatory=$false)]
        [string]
        $RepairAction
    )

    $reportHTML = ""
    $functionName = $($MyInvocation.MyCommand.Name)

    try
    {
        $reportData = Get-ReportData -TestReports $TestReports -TestName $testName -RepairAction $RepairAction
        $jsonFileName = Get-JsonFileName -TestName $testName -RepairAction $RepairAction
        if ($null -eq $reportData)
        {
            if ($RepairAction -ieq "NoRepair" -or $RepairAction -ieq "BeforeRepair")
            {
                $reportHTML = No-TestData -TestName $jsonFileName
            }
            else
            {
                #There will be no AfterRepair report if the BeforeRepair report had an overall status of success.
                $reportDataBeforeRepair = Get-ReportData -TestReports $TestReports -TestName $testName -RepairAction "BeforeRepair"
                if ($reportDataBeforeRepair.OverallResult -ne "PASS")
                {
                    $reportHTML = No-TestData -TestName $jsonFileName
                }
            }
            return $reportHTML
        }

        $reportHTML += "<p><b>Test Name:</b> $($reportData.TestName)</p>"

        $reportHTML += "<p><b>Overall Result: </b>"
        $result = $reportData.OverallResult
        $reportHTML += Style-Result -Result $result
        $reportHTML += "</p>"

        $reportHTML += "<p><b>Has Repair Action: </b>"
        $reportHTML += $reportData.HasRepairAction
        $reportHTML += "</p>"

        $reportHTML += "<p><b>Repair Description: </b>"
        $reportHTML += $reportData.RepairDescription
        $reportHTML += "</p><br>"

        # make table of rows
        $rows = $reportData.Rows
        if ($null -eq $rows -or $rows.count -eq 0)
        {
            $reportHTML += "<p>Could not find data for this test.</p>"
            return $reportHTML
        }
        $reportHTML += "<table>"
        $row = $rows[0]
        # Create header Row
        $row.PSObject.Properties | ForEach-Object {
            $name = Convert-CamelCase -Name $_.Name
            $reportHTML += "<th>$name</th>"
        }
        foreach ($row in $rows)
        {
            $reportHTML += "<tr>"
            $row.PSObject.Properties | ForEach-Object {
                $reportHTML += "<td>$($_.Value)</td>"
            }
            $reportHTML += "</tr>"
        }
        $reportHTML += "</table>"
        $reportHTML += "<br><br><br>"
        return $reportHTML
    }
    catch
    {
        Trace-Progress "$functionName : Failed with $_"
        return $reportHTML
    }
}

function Style-Result
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$false)]
        [string]
        $Result
    )

    if ($Result -ieq "PASS")
    {
        $class = "pass"
    }
    else
    {
        $class = "fail"
    }

    return "<span class=$class>$Result</span>"
}

function Convert-CamelCase
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [string]
        $Name
    )

    return $Name -creplace '(?<=\w)([A-Z])', ' $1'
}

Export-ModuleMember -Function Generate-HTMLReport -Verbose:$false
# SIG # Begin signature block
# MIIoOQYJKoZIhvcNAQcCoIIoKjCCKCYCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCRcM1EixxQpv8j
# 4Et17OWclu7IWqO7SQ1z+jojGGTsKqCCDYUwggYDMIID66ADAgECAhMzAAADri01
# UchTj1UdAAAAAAOuMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMxMTE2MTkwODU5WhcNMjQxMTE0MTkwODU5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQD0IPymNjfDEKg+YyE6SjDvJwKW1+pieqTjAY0CnOHZ1Nj5irGjNZPMlQ4HfxXG
# yAVCZcEWE4x2sZgam872R1s0+TAelOtbqFmoW4suJHAYoTHhkznNVKpscm5fZ899
# QnReZv5WtWwbD8HAFXbPPStW2JKCqPcZ54Y6wbuWV9bKtKPImqbkMcTejTgEAj82
# 6GQc6/Th66Koka8cUIvz59e/IP04DGrh9wkq2jIFvQ8EDegw1B4KyJTIs76+hmpV
# M5SwBZjRs3liOQrierkNVo11WuujB3kBf2CbPoP9MlOyyezqkMIbTRj4OHeKlamd
# WaSFhwHLJRIQpfc8sLwOSIBBAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhx/vdKmXhwc4WiWXbsf0I53h8T8w
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwMTgzNjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AGrJYDUS7s8o0yNprGXRXuAnRcHKxSjFmW4wclcUTYsQZkhnbMwthWM6cAYb/h2W
# 5GNKtlmj/y/CThe3y/o0EH2h+jwfU/9eJ0fK1ZO/2WD0xi777qU+a7l8KjMPdwjY
# 0tk9bYEGEZfYPRHy1AGPQVuZlG4i5ymJDsMrcIcqV8pxzsw/yk/O4y/nlOjHz4oV
# APU0br5t9tgD8E08GSDi3I6H57Ftod9w26h0MlQiOr10Xqhr5iPLS7SlQwj8HW37
# ybqsmjQpKhmWul6xiXSNGGm36GarHy4Q1egYlxhlUnk3ZKSr3QtWIo1GGL03hT57
# xzjL25fKiZQX/q+II8nuG5M0Qmjvl6Egltr4hZ3e3FQRzRHfLoNPq3ELpxbWdH8t
# Nuj0j/x9Crnfwbki8n57mJKI5JVWRWTSLmbTcDDLkTZlJLg9V1BIJwXGY3i2kR9i
# 5HsADL8YlW0gMWVSlKB1eiSlK6LmFi0rVH16dde+j5T/EaQtFz6qngN7d1lvO7uk
# 6rtX+MLKG4LDRsQgBTi6sIYiKntMjoYFHMPvI/OMUip5ljtLitVbkFGfagSqmbxK
# 7rJMhC8wiTzHanBg1Rrbff1niBbnFbbV4UDmYumjs1FIpFCazk6AADXxoKCo5TsO
# zSHqr9gHgGYQC2hMyX9MGLIpowYCURx3L7kUiGbOiMwaMIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAOuLTVRyFOPVR0AAAAA
# A64wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJSi
# 8Q9N4sgjT1f3A2tRArtv+IMiRAwvrPXxyyhXNMKKMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAqfQhHF4Sh2QTVLpBFWunFkgd5rmjXeYmprnq
# xvrZL4b7aVYmWfXVDDgX5f39uy3lEd71L0+VqzGeFjA56lMlnnj3lgczeAO+kYob
# xD+GBl6U+YjSwY/q+SkprVuY9JFSOEcpb7g9NpxlVmIP/NyYyQMKrWNmC+pEg/pU
# L5wCblXJu44Ojysvp4jXSGXw4CU1bP1fjMas9r0a//CwXepVO/9tWD51FMVnUcFC
# 7rmLGkw/o+miqaESs0P8sNMEHL5Z7rxX1l9uuMx/mi5YeLfo5WjsvkoSP98tfoBd
# ymIVyFvljBEkv7wRB+Lmnx9UsBeFYPrKvt61oX5eUXleJQkZI6GCF5QwgheQBgor
# BgEEAYI3AwMBMYIXgDCCF3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCCbiC118pqd6OZ1BhMBaUJmchQkcD5zLKsr
# NZS96FHunQIGZeen668lGBMyMDI0MDMxMTE4MTgzMS41NTZaMASAAgH0oIHRpIHO
# MIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL
# ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk
# IFRTUyBFU046ODYwMy0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l
# LVN0YW1wIFNlcnZpY2WgghHqMIIHIDCCBQigAwIBAgITMwAAAfGzRfUn6MAW1gAB
# AAAB8TANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MDAeFw0yMzEyMDYxODQ1NTVaFw0yNTAzMDUxODQ1NTVaMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046ODYwMy0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCxulCZttIf8X97rW9/J+Q4
# Vg9PiugB1ya1/DRxxLW2hwy4QgtU3j5fV75ZKa6XTTQhW5ClkGl6gp1nd5VBsx4J
# b+oU4PsMA2foe8gP9bQNPVxIHMJu6TYcrrn39Hddet2xkdqUhzzySXaPFqFMk2Vi
# fEfj+HR6JheNs2LLzm8FDJm+pBddPDLag/R+APIWHyftq9itwM0WP5Z0dfQyI4Wl
# VeUS+votsPbWm+RKsH4FQNhzb0t/D4iutcfCK3/LK+xLmS6dmAh7AMKuEUl8i2kd
# WBDRcc+JWa21SCefx5SPhJEFgYhdGPAop3G1l8T33cqrbLtcFJqww4TQiYiCkdys
# CcnIF0ZqSNAHcfI9SAv3gfkyxqQNJJ3sTsg5GPRF95mqgbfQbkFnU17iYbRIPJqw
# gSLhyB833ZDgmzxbKmJmdDabbzS0yGhngHa6+gwVaOUqcHf9w6kwxMo+OqG3QZIc
# wd5wHECs5rAJZ6PIyFM7Ad2hRUFHRTi353I7V4xEgYGuZb6qFx6Pf44i7AjXbptU
# olDcVzYEdgLQSWiuFajS6Xg3k7Cy8TiM5HPUK9LZInloTxuULSxJmJ7nTjUjOj5x
# wRmC7x2S/mxql8nvHSCN1OED2/wECOot6MEe9bL3nzoKwO8TNlEStq5scd25GA0g
# MQO+qNXV/xTDOBTJ8zBcGQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFLy2xe59sCE0
# SjycqE5Erb4YrS1gMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8G
# A1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
# Y3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBs
# BggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy
# MDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH
# AwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQDhSEjSBFSCbJyl
# 3U/QmFMW2eLPBknnlsfID/7gTMvANEnhq08I9HHbbqiwqDEHSvARvKtL7j0znICY
# BbMrVSmvgDxU8jAGqMyiLoM80788So3+T6IZV//UZRJqBl4oM3bCIQgFGo0VTeQ6
# RzYL+t1zCUXmmpPmM4xcScVFATXj5Tx7By4ShWUC7Vhm7picDiU5igGjuivRhxPv
# bpflbh/bsiE5tx5cuOJEJSG+uWcqByR7TC4cGvuavHSjk1iRXT/QjaOEeJoOnfes
# bOdvJrJdbm+leYLRI67N3cd8B/suU21tRdgwOnTk2hOuZKs/kLwaX6NsAbUy9pKs
# DmTyoWnGmyTWBPiTb2rp5ogo8Y8hMU1YQs7rHR5hqilEq88jF+9H8Kccb/1ismJT
# GnBnRMv68Ud2l5LFhOZ4nRtl4lHri+N1L8EBg7aE8EvPe8Ca9gz8sh2F4COTYd1P
# Hce1ugLvvWW1+aOSpd8NnwEid4zgD79ZQxisJqyO4lMWMzAgEeFhUm40FshtzXud
# AsX5LoCil4rLbHfwYtGOpw9DVX3jXAV90tG9iRbcqjtt3vhW9T+L3fAZlMeraWfh
# 7eUmPltMU8lEQOMelo/1ehkIGO7YZOHxUqeKpmF9QaW8LXTT090AHZ4k6g+tdpZF
# fCMotyG+E4XqN6ZWtKEBQiE3xL27BDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKb
# SZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj
# YXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIy
# NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXI
# yjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjo
# YH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1y
# aa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v
# 3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pG
# ve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viS
# kR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYr
# bqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlM
# jgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSL
# W6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AF
# emzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIu
# rQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIE
# FgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWn
# G1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW
# M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5
# Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi
# AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV
# 9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAx
# MC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2
# LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv
# 6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZn
# OlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1
# bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4
# rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU
# 6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDF
# NLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/
# HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdU
# CbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKi
# excdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTm
# dHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZq
# ELQdVTNYs6FwZvKhggNNMIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp
# Y2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjg2MDMtMDVF
# MC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK
# AQEwBwYFKw4DAhoDFQD7n7Bk4gsM2tbU/i+M3BtRnLj096CBgzCBgKR+MHwxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6ZlmUjAi
# GA8yMDI0MDMxMTExMTUzMFoYDzIwMjQwMzEyMTExNTMwWjB0MDoGCisGAQQBhFkK
# BAExLDAqMAoCBQDpmWZSAgEAMAcCAQACAgHNMAcCAQACAhOTMAoCBQDpmrfSAgEA
# MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI
# AgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBABsS2alikAWAyTN2vu75E6samoC/
# Fu3+vsMG6Kex2246kK8LsOL3b7D6tqqCnOzx6LmFIrPx4iX/hi78l5XrE+VS62mN
# hsqVcOKiRJszh5/+kY+Q7GIf+IJowRM5e78lwZyeFsW2rpBxZFYMzRLaa7aQ0GwI
# yGPsGvk3WG/8H28im/1+Trqst/pDJuW3kTA8wBg92ZQC+PE1WfLzV/9ogMUgTkWt
# Cxmo87VcGdUfSZJV4MnFefTyHu8OJbExwkBRtGiCUOThO9aeswFLySOm3xtriFvZ
# o1llEehjJQOH5wiGqdqZibWoTuKpCGyeuUCWlMFHjw+SD1375f69MK580uExggQN
# MIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAfGz
# RfUn6MAW1gABAAAB8TANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0G
# CyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCBrSiev76MFcs7a/cp/H0gSMmPW
# Q11lBamQU/GdFpeYpjCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EINV3/T5h
# S7ijwao466RosB7wwEibt0a1P5EqIwEj9hF4MIGYMIGApH4wfDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp
# bWUtU3RhbXAgUENBIDIwMTACEzMAAAHxs0X1J+jAFtYAAQAAAfEwIgQgz/F7cnn6
# p3gly+XdU0y0xYq2qzE816Vs+V4lq50whJkwDQYJKoZIhvcNAQELBQAEggIAd5AL
# uu6Rd42y6qSxOtEsNBVvTz/cG5dPy+5XnhfPXCn2U1PRPlpuYQhzveDrVtxmtkdf
# 9FN+L2kqiBDL+vtc8LSZtyH+V8A4mbn6uAFqyFkpfcbbMQeDOSsbA/Ry4LT/Plxg
# RBRapLdu42/nJLtyrSVF4dQdTFXQCyoO4qIwVlq9vzILvOrYrtqtHR3zzD5HgI+i
# J8Q6pZk+P2LllHfEKG6eidHXJHmzofcyVzOnwTfuRuE2jOch2//yqInnYEkwLYDY
# pYwlI8SAtJGoiTlF5J5PgZJNByh4Sx7f1X6Com9LcfqGUrIegF6gCBwMwLhwwGMp
# UpN8k7MWMWByIvrJGUVz+gE2jHj7CiBsEMR5c6BHjw7/bJ2rdf94dWSyxSI7xmLa
# /LPzpDUIpv5DLeNKMk8/0cSly4L0N5NPk0SNFkuEzscVSxg/N7fvVZVNK/z2I6gR
# 5K83LoEqIiKrDYwjqWtIGm0Abr2pazeX6S73BXnjogAtXOenZbuoOF1x2ElKU9FE
# WVzSnruLHh6lCDCEZo6SJFkMMWSTfCfDo5C8ll7v4SC90qQqV12R8uRqW43krftz
# qx7ZMp76Idj6x9l6H8JvGb2rI7gnp5QH7tfaC/KLTBK60E2Prh/8Zl/9aWB4oZKb
# 0JIDb59hAEee/Kvn0PiKzQ5XYf2docscD1ci9dE=
# SIG # End signature block