Skip to content

Improve the survey and telemetry #14804

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ public void ResetWaitingTasks()
HistoryTaskCompletionSource = new TaskCompletionSource();
RequestPredictionTaskCompletionSource = new TaskCompletionSource();
SendTelemetryTaskCompletionSource = new TaskCompletionSource();
HistoryData = default;
RequestPredictionData = default;
}

protected override TelemetryClient GetApplicationInsightTelemetryClient()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

<PropertyGroup>
<PackageId>Az.Tools.Predictor</PackageId>
<Version>0.2.0</Version>
<Version>0.2.1</Version>
<Authors>Microsoft Corporation</Authors>
<Company>Microsoft Corporation</Company>
<Copyright>Microsoft Corporation. All rights reserved.</Copyright>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
RootModule = ''

# Version number of this module.
ModuleVersion = '0.2.0'
ModuleVersion = '0.2.1'

# Supported PSEditions
CompatiblePSEditions = 'Core'
Expand Down Expand Up @@ -43,11 +43,11 @@ PowerShellVersion = '7.2'
# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @(@{ModuleName="PSReadLine"; ModuleVersion="2.2.0-beta2"})

NestedModules = @("Microsoft.Azure.PowerShell.Tools.AzPredictor.dll")
# NestedModules = @("Microsoft.Azure.PowerShell.Tools.AzPredictor.dll")

ScriptsToProcess = @("PromptSurvey.ps1")

CmdletsToExport = @("Enable-AzPredictor", "Disable-AzPredictor")
CmdletsToExport = @("Enable-AzPredictor", "Disable-AzPredictor", "Open-AzSurvey")

# Format files (.ps1xml) to be loaded when importing this module

Expand All @@ -69,7 +69,8 @@ PrivateData = @{
# IconUri = ''

# ReleaseNotes of this module
ReleaseNotes = '* second preview release
ReleaseNotes = '* Add a cmdlet to open survey page and update message
* Second preview release
* Add Enable-AzPredictor and Disable-AzPredictor cmdlet to simplify configuration
* Use PSReadline new API'

Expand Down
22 changes: 9 additions & 13 deletions tools/Az.Tools.Predictor/Az.Tools.Predictor/AzPredictor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ public void StartEarlyProcessing(string clientId, IReadOnlyList<string> history)
{
string secondToLastLine = history.TakeLast(AzPredictorConstants.CommandHistoryCountToProcess).First();
var secondToLastCommand = GetAstAndMaskedCommandLine(secondToLastLine);
_lastTwoMaskedCommands.Enqueue(secondToLastCommand.Item2);
_lastTwoMaskedCommands.Enqueue(secondToLastCommand.IsSupported ? secondToLastCommand.MaskedValue : AzPredictorConstants.CommandPlaceholder);

if (!string.Equals(AzPredictorConstants.CommandPlaceholder, secondToLastCommand.Item2, StringComparison.Ordinal))
if (secondToLastCommand.IsSupported)
{
_service.RecordHistory(secondToLastCommand.Item1);
_service.RecordHistory(secondToLastCommand.Ast);
}
}
else
Expand All @@ -139,7 +139,7 @@ public void StartEarlyProcessing(string clientId, IReadOnlyList<string> history)

string lastLine = history.Last();
var lastCommand = GetAstAndMaskedCommandLine(lastLine);
bool isLastCommandSupported = !string.Equals(AzPredictorConstants.CommandPlaceholder, lastCommand.Item2, StringComparison.Ordinal);
bool isLastCommandSupported = lastCommand.IsSupported;

if (isLastCommandSupported)
{
Expand All @@ -162,7 +162,7 @@ public void StartEarlyProcessing(string clientId, IReadOnlyList<string> history)
_lastTwoMaskedCommands.Enqueue(existingInQueue);
}

_telemetryClient.OnHistory(new HistoryTelemetryData(clientId, lastCommand.Item2));
_telemetryClient.OnHistory(new HistoryTelemetryData(clientId, lastCommand.MaskedValue ?? AzPredictorConstants.CommandPlaceholder));

if (isLastTwoCommandsChanged)
{
Expand Down Expand Up @@ -202,21 +202,17 @@ public void StartEarlyProcessing(string clientId, IReadOnlyList<string> history)
}
}

ValueTuple<CommandAst, string> GetAstAndMaskedCommandLine(string commandLine)
(CommandAst Ast, string MaskedValue, bool IsSupported) GetAstAndMaskedCommandLine(string commandLine)
{
var asts = Parser.ParseInput(commandLine, out _, out _);
var allNestedAsts = asts?.FindAll((ast) => ast is CommandAst, true);
var commandAst = allNestedAsts?.LastOrDefault() as CommandAst;
string maskedCommandLine = AzPredictorConstants.CommandPlaceholder;

var commandName = commandAst?.CommandElements?.FirstOrDefault().ToString();
bool isSupported = _service.IsSupportedCommand(commandName);
string maskedCommandLine = CommandLineUtilities.MaskCommandLine(commandAst);

if (_service.IsSupportedCommand(commandName))
{
maskedCommandLine = CommandLineUtilities.MaskCommandLine(commandAst);
}

return ValueTuple.Create(commandAst, maskedCommandLine);
return (commandAst, maskedCommandLine, isSupported);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public virtual async Task<bool> RequestPredictionsAsync(IEnumerable<string> comm
{
SetCommandToRequestPrediction(localCommands);

AzPredictorService.SetHttpRequestHeader(_client?.DefaultRequestHeaders, _azContext.HashUserId, _telemetryClient.CorrelationId);
AzPredictorService.SetHttpRequestHeader(_client?.DefaultRequestHeaders, _azContext.HashUserId, _telemetryClient.RequestId);

var requestContext = new PredictionRequestBody.RequestContext()
{
Expand Down Expand Up @@ -313,7 +313,7 @@ protected virtual void RequestAllPredictiveCommands()

try
{
AzPredictorService.SetHttpRequestHeader(_client.DefaultRequestHeaders, _azContext.HashUserId, _telemetryClient.CorrelationId);
AzPredictorService.SetHttpRequestHeader(_client.DefaultRequestHeaders, _azContext.HashUserId, _telemetryClient.RequestId);

var httpResponseMessage = await _client.GetAsync(_commandsEndpoint);
hasSentHttpRequest = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.Azure.PowerShell.Tools.AzPredictor.Utilities;
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Management.Automation;
using System.Text;
using System.Text.Json;

namespace Microsoft.Azure.PowerShell.Tools.AzPredictor
{
/// <summary>
/// <para type="synopsis">Cmdlet to open a survey link in the default browser</para>
/// <para type="description">This cmdlet will open a survey link in the default browser. All data from this survey will be anonymized. See the Microsoft Privacy Policy (https://privacy.microsoft.com/) for more information </para>
/// </summary>
[Cmdlet("Open", "AzSurvey"), OutputType(typeof(bool))]
public sealed class OpenAzSurvey : PSCmdlet
{
private const string _SurveyLinkFormat = "https://aka.ms/azpredictorisurvey?SessionId={0}&Q_CHL=cmdlet";
/// <summary>
/// <para type="description">Indicates whether the user would like to receive output. </para>
/// </summary>
[Parameter(Mandatory = false)]
public SwitchParameter PassThru { get; set; }

/// <inheritdoc/>
protected override void ProcessRecord()
{
var profileSettings = Settings.GetProfileSettings();
var surveyId = profileSettings?.SurveyId?.ToString(CultureInfo.InvariantCulture) ?? "000000";

var link = string.Format(OpenAzSurveyLink._SurveyLinkFormat, surveyId, CultureInfo.InvariantCulture);

Console.WriteLine($"Opening survey {link}");

// TODO [mahuang] Ouput the link for user to copy/paste in case it's not open in the default browser.
var processStartInfo = new ProcessStartInfo();
processStartInfo.FileName = link;
processStartInfo.UseShellExecute = true;

Process.Start(processStartInfo);

if (profileSettings != null)
{
profileSettings.SurveyId = null;

try
{
var fileContent = JsonSerializer.Serialize<Settings>(profileSettings, new JsonSerializerOptions(JsonUtilities.DefaultSerializerOptions)
{
WriteIndented = true,
});
var profileSettingFilePath = Settings.GetProfileSettingsFilePath();
File.WriteAllText(profileSettingFilePath, fileContent, Encoding.UTF8);
}
catch
{
// Ignore all exceptions.
Copy link
Contributor

@dcaro dcaro Apr 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kceiw / @VeryEarly Should we display here a message with the link if we cannot open the default browser?
For example: "The default browser could not be opened, please copy the following link to a browser to give us your feedback"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just chatted with @kceiw ignore this comment, displaying the survey link on the output of the cmdlet is good.

}
}

if (PassThru.IsPresent)
{
WriteObject(true);
}
}
}
}
47 changes: 31 additions & 16 deletions tools/Az.Tools.Predictor/Az.Tools.Predictor/InterceptSurvey.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ if ($env:Azure_PS_Intercept_Survey -eq "false") {
$mutexName = "AzModulesInterceptSurvey"
$mutexTiimeout = 1000
$interceptDays = 30
$interceptLoadTimes = 3
$interceptIntervals = ( 3, 2, 5 )
$interceptMaxTimes = 3 # Show intercept this many times in total.
$today = Get-Date
$mutexTimeout = 500

Expand All @@ -54,17 +55,21 @@ function ConvertTo-String {
return $date.ToString("yyyy-MM-dd")
}

function Init-InterceptFile {
$interceptContent = @{
function Init-InterceptObject {
return @{
"lastInterceptCheckDate"=ConvertTo-String($today);
"interceptTriggered"=$false;
"interceptTriggered"=0;
"modules"=@(@{
"name"=$moduleName;
"majorVersion"=$majorVersion;
"activeDays"=1;
"lastActiveDate"=ConvertTo-String($today);
})
}
}

function Init-InterceptFile {
$interceptContent = Init-InterceptObject

ConvertTo-Json -InputObject $interceptContent | Out-File -FilePath $interceptFilePath -Encoding utf8
}
Expand Down Expand Up @@ -106,12 +111,12 @@ function Update-InterceptObject {
if ($recordedMajorVersion -ne $majorVersion) {
$thisModule.activeDays = 1
$thisModule.lastActiveDate = ConvertTo-String($today)
$interceptObject.interceptTriggered = $false
$interceptObject.interceptTriggered = 0

return $false
}

if ($interceptObject.interceptTriggered) {
if ($interceptObject.interceptTriggered -ge $interceptMaxTimes) {
return $false
}

Expand All @@ -133,10 +138,10 @@ function Update-InterceptObject {
$newActiveDays++
}

if ($newActiveDays -ge $interceptLoadTimes) {
if ($newActiveDays -ge $interceptIntervals[$interceptObject.interceptTriggered]) {
$thisModule.activeDays = 0
$thisModule.lastActiveDate = ConvertTo-String($today)
$interceptObject.interceptTriggered = $true
$interceptObject.interceptTriggered++
return $true
}

Expand Down Expand Up @@ -166,15 +171,21 @@ try
try {
$fileContent = Get-Content $interceptFilePath | Out-String
$interceptObject = ConvertFrom-Json $fileContent

if ($interceptObject.interceptTriggered.GetType() -eq $true.GetType()) {
# HACK change the use of interceptTriggered from a boolean to an integer. It's to count how many times the intercept is tirggered.
$interceptObject.interceptTriggered = 0
}
} catch {
Init-InterceptFile
}

if (-not ($interceptObject -eq $null)) {
$shouldIntercept = Update-InterceptObject($interceptObject)

ConvertTo-Json -InputObject $interceptObject | Out-File $interceptFilePath -Encoding utf8
if ($null -eq $interceptObject) {
$interceptObject = Init-InterceptObject
}

$shouldIntercept = Update-InterceptObject($interceptObject)

ConvertTo-Json -InputObject $interceptObject | Out-File $interceptFilePath -Encoding utf8
}
} catch {
}
Expand Down Expand Up @@ -207,7 +218,11 @@ if ($shouldIntercept) {
}
}

$escape = $([char]27)
Write-Host "`n$escape[7mHow was your experience using Az predictor? $escape[27m`n" -NoNewline; Write-Host "$escape[7mhttp://aka.ms/azpredictorisurvey?SessionId=$surveyId$escape[27m" -NoNewline
Write-Host "`n"
Write-Host "---------------------------------------------------";
Write-Host "Survey:" -ForegroundColor $Host.PrivateData.VerboseBackgroundColor -BackgroundColor $host.PrivateData.VerboseForegroundColor -NoNewline;
Write-Host " How was your experience using Az predictor?";
Write-Host "";
Write-Host "Run " -NoNewline; Write-Host "Open-AzSurvey" -ForegroundColor $Host.PrivateData.VerboseBackgroundColor -BackgroundColor $host.PrivateData.VerboseForegroundColor -NoNewline; Write-Host " to give us your feedback.";
Write-Host "---------------------------------------------------";

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
# ----------------------------------------------------------------------------------

$targetScript = (Join-Path -Path $PSScriptRoot -ChildPath "InterceptSurvey.ps1")
& $targetScript "Az.Tools.Predictor" 0
& $targetScript "Az.Tools.Predictor-Preview3" 0
Loading