Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into homepage-implemen…
Browse files Browse the repository at this point in the history
…tation
  • Loading branch information
dnenov committed Jan 26, 2024
2 parents b792e55 + 5e05efc commit 48bf145
Show file tree
Hide file tree
Showing 26 changed files with 282 additions and 143 deletions.
117 changes: 61 additions & 56 deletions .github/workflows/issue_type_predicter.yaml
Original file line number Diff line number Diff line change
@@ -1,98 +1,103 @@
name: Issue Predicter
on:
name: Issue Type Predicter
# This workflow uses https://github.com/DynamoDS/IssuesTypePredicter to predict the type of a github issue

on:
issues:
types: [opened,edited]
jobs:
issuePredicterType:
name: Issue Predicter
types: [opened, edited]

jobs:
issue_type_Predicter:
name: Issue Type Predicter
runs-on: ubuntu-latest
env:
#The 'analysis_response' variable is used to store the script response on step one,
#and then checked on step two to know if adding the label and comment is necessary.
#The initial 'undefined' value will be overridden when the script runs.
# The 'analysis_response' variable is used to store the response returned by issue_analyzer.ps1
# The initial 'undefined' value will be overridden when the script runs
analysis_response: undefined
#The 'parsed_issue_body' variable is used to store the parsed issue body (after removing some sections of the body like Stack Trace)
# The 'parsed_issue_body' variable is used to store the parsed issue body (after removing some sections of the body like Stack Trace)
parsed_issue_body: undefined
#The 'issue_json_string' variable is used to store in a json string (parsed info of the issue body)
# The 'issue_json_string' variable is used to store parsed info of the issue body as a json string
issue_json_string: undefined
#The 'is_wish_list' variable is used to store the value returned by the IssuesTypePredicter project
# The 'is_wish_list' variable is used to store the value returned by the IssuesTypePredicter project
is_wish_list: undefined
#template file name
# issue template file name
template: "ISSUE_TEMPLATE.md"
#amount of sections from the template that can be missing information for the issue to still be considered complete
# amount of sections from the template that can be missing information for the issue to still be considered valid
acceptable_missing_info: 1

steps:
#Removes conflicting characters before using the issue content as a script parameter
- uses: actions/checkout@v4
- name: Remove conflicting chars
env:
ISSUE_BODY: ${{github.event.issue.body}}
# Removes quotes before using the issue content as a script parameter
- name: Remove Quotes
id: remove_quotes
uses: frabert/[email protected]
id: remove_quotations
env:
ISSUE_BODY: ${{ github.event.issue.body }}
with:
pattern: "\""
string: ${{env.ISSUE_BODY}}
string: ${{ env.ISSUE_BODY }}
replace-with: '-'

#Checks for missing information inside the issue content
- name: Check Information
id: check-info
# Analyze for missing information inside the issue content
- name: Analyze Issue Body
env:
ISSUE_BODY: ${{ steps.remove_quotations.outputs.replaced }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ steps.remove_quotes.outputs.replaced }}
run: |
echo "analysis_response=$(pwsh .\\.github\\scripts\\issue_analyzer.ps1 "${{ env.template }}" "${{ env.acceptable_missing_info }}" )" >> $GITHUB_ENV
#Remove sections in the issue body like "Dynamo version", "Stack Trace" because won't be used to predict the issue type
echo "analysis_response=$(pwsh .\\.github\\scripts\\issue_analyzer.ps1 "${{ env.template }}" "${{ env.acceptable_missing_info }}")" >> $GITHUB_ENV
# Remove sections in the issue body like "Dynamo version", "Stack Trace" because won't be used to predict the issue type
- name: Clean Issue Body
env:
ISSUE_BODY_PARSED: ${{steps.remove_quotations.outputs.replaced}}
if: env.analysis_response == 'Valid'
id: clean-issue-body
env:
ISSUE_BODY_PARSED: ${{ steps.remove_quotes.outputs.replaced }}
run: |
echo "parsed_issue_body="$(pwsh .\\.github\\scripts\\issue_body_cleaner.ps1 )"" >> $GITHUB_ENV
echo "parsed_issue_body="$(pwsh .\\.github\\scripts\\issue_body_cleaner.ps1 )"" >> $GITHUB_ENV
#The IssuesTypePredicter program receives as a parameter a json string with the issue content, then It's creating the json string in this section based in the issue body
# Create json string from the issue body
- name: Create Issue JSON String
env:
ISSUE_NUMBER: ${{github.event.issue.number}}
ISSUE_TITLE: ${{github.event.issue.title}}
if: env.analysis_response == 'Valid'
id: create-issue-json
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
run: |
mkdir IssuesTypePredicter
echo "issue_json_string="$(pwsh .\\.github\\scripts\\get_issue_json_body.ps1 "$ISSUE_NUMBER")"" >> $GITHUB_ENV
echo "issue_json_string="$(pwsh .\\.github\\scripts\\get_issue_json_body.ps1 "$ISSUE_NUMBER")"" >> $GITHUB_ENV
#Now checkout the IssuesTypePredicter source code from the repo https://github.com/DynamoDS/IssuesTypePredicter
# Checkout the IssuesTypePredicter repo (https://github.com/DynamoDS/IssuesTypePredicter)
- name: Checkout IssuesTypePredicter
if: env.analysis_response == 'Valid'
uses: actions/checkout@v4
with:
repository: DynamoDS/IssuesTypePredicter
path: IssuesTypePredicter

#Builds the solution IssuesTypePredicter.sln (this contains two VS2019 ML.NET projects)
- name: Build Issues Type Predicter
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
dotnet-version: '3.1.0'

# Build the solution IssuesTypePredicter.sln (this contains two VS2019 ML.NET projects)
- name: Build Issues Type Predicter
if: env.analysis_response == 'Valid'
run: |
run: |
dotnet build ./IssuesTypePredicter/IssuesTypePredicter.sln --configuration Release
cp ./IssuesTypePredicter/IssuesTypePredicterML.ConsoleApp/bin/Release/netcoreapp3.1/MLModel.zip .
#Execute the IssuesTypePredicter program and pass as a parameter the json string (which contains the issue info)
# Execute the IssuesTypePredicter program and pass 'issue_json_string' as a parameter
- name: Run Issues Type Predicter
if: env.analysis_response == 'Valid'
run: |
echo "is_wish_list="$(dotnet run -p ./IssuesTypePredicter/IssuesTypePredicterML.ConsoleApp/IssuesTypePredicterML.ConsoleApp.csproj -v q "${{env.issue_json_string}}")"" >> $GITHUB_ENV
run: |
echo "is_wish_list="$(dotnet run -p ./IssuesTypePredicter/IssuesTypePredicterML.ConsoleApp/IssuesTypePredicterML.ConsoleApp.csproj -v q "${{ env.issue_json_string }}")"" >> $GITHUB_ENV
#If the is_wish_list variable contains 1 means that is a wishlist issue and label the issue with the word "Wishlist"
- name: Label Wishlist
if: contains(env.is_wish_list,'IsWishlist:1') && env.analysis_response == 'Valid'
# If the is_wish_list variable contains 1, label the issue as "Wishlist"
- name: Label issue as 'Wishlist'
if: env.analysis_response == 'Valid' && contains(env.is_wish_list, 'IsWishlist:1')
env:
GH_TOKEN: ${{ secrets.DYNAMO_ISSUES_TOKEN }}
run: |
curl -v -u admin:${{ secrets.DYNAMOBOTTOKEN }} -d '{"labels": ["Wishlist"]}' ${{ github.event.issue.url }}/labels
gh issue edit ${{ github.event.issue.number }} --add-label "Wishlist" --repo ${{ github.repository }}
#When the issue is missing important information (don't follow the template structure) the issue will be labeled as "NotMLEvaluated"
- name: Label NotMLEvaluated
# If the issue is missing important information (don't follow the template structure), label the issue as "NotMLEvaluated"
- name: Label issue as 'NotMLEvaluated'
if: env.analysis_response != 'Valid' || env.issue_json_string == ''
env:
GH_TOKEN: ${{ secrets.DYNAMO_ISSUES_TOKEN }}
run: |
curl -v -u admin:${{ secrets.DYNAMOBOTTOKEN }} -d '{"labels": ["NotMLEvaluated"]}' ${{ github.event.issue.url }}/labels
gh issue edit ${{ github.event.issue.number }} --add-label "NotMLEvaluated" --repo ${{ github.repository }}
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ You can learn more about developing libraries for Dynamo on the [Dynamo wiki](ht
You will need the following to build the latest Dynamo on Windows:

- [Microsoft Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) (any edition)
- [Microsoft .NET Framework 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) (included with Visual Studio 2022)
- [Microsoft .NET Framework 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (included with Visual Studio 2022)
- [Node.js LTS](https://nodejs.org/en/download/) and npm
- [NUnit Test Adapter 2](https://marketplace.visualstudio.com/items?itemName=NUnitDevelopers.NUnitTestAdapter) (For runnning Dynamo tests within Visual Studio)

If you are working on legacy branches, you may need to install legacy .NET Framework versions through Visual Studio `Tools > Get Tools and Features...` or downloading from [the archive here](https://www.microsoft.com/net/download/archives).

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<UserControl x:Class="Dynamo.DocumentationBrowser.DocumentationBrowserView"
<UserControl x:Class="Dynamo.DocumentationBrowser.DocumentationBrowserView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:Dynamo.UI;assembly=DynamoCoreWpf"
xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
xmlns:wv2="clr-namespace:Dynamo.Wpf.Utilities;assembly=DynamoCoreWpf"
mc:Ignorable="d"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Expand All @@ -22,7 +22,7 @@
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<wv2:WebView2 Name="documentationBrowser"
<wv2:DynamoWebView2 Name="documentationBrowser"
Grid.Row="0"
HorizontalAlignment="Stretch"
Visibility="{Binding Path=ShowBrowser,Converter={StaticResource BooleanToVisibilityConverter}}"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public partial class DocumentationBrowserView : UserControl, IDisposable
public DocumentationBrowserView(DocumentationBrowserViewModel viewModel)
{
InitializeComponent();

this.DataContext = viewModel;
this.viewModel = viewModel;

Expand Down
59 changes: 46 additions & 13 deletions src/DynamoCore/Utilities/LuceneSearchUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ internal class LuceneSearchUtility
/// </summary>
internal static readonly LuceneStartConfig DefaultPkgIndexStartConfig = new LuceneStartConfig(LuceneSearchUtility.LuceneStorage.FILE_SYSTEM, LuceneConfig.PackagesIndexingDirectory);

private bool hasEmptySpaces { get; set; }

public enum LuceneStorage
{
//Lucene Storage will be located in RAM and all the info indexed will be lost when Dynamo app is closed
Expand All @@ -82,6 +84,18 @@ public enum LuceneStorage
FILE_SYSTEM
}

/// <summary>
/// This enum will be used to identify which can of search should be executed based in the user search criteria
/// </summary>
public enum SearchType
{
//Normal search using just one word matching a specific node name
Normal,

//Search by category using the "." character for example "list.re"
ByCategory
}

// Used for creating the StandardAnalyzer
internal Analyzer Analyzer;

Expand Down Expand Up @@ -264,7 +278,14 @@ internal void SetDocumentFieldValue(Document doc, string field, string value, bo
/// <returns></returns>
internal string CreateSearchQuery(string[] fields, string SearchTerm)
{
//By Default the search will be normal
SearchType searchType = SearchType.Normal;
int fuzzyLogicMaxEdits = LuceneConfig.FuzzySearchMinEdits;
hasEmptySpaces = false;

//Max number of nodes allowed in the search when is a ByEmptySpace search
const int MaxNodeNamesRepeated = 20;

// Use a larger max edit value - more tolerant with typo when search term is longer than threshold
if (SearchTerm.Length > LuceneConfig.FuzzySearchMaxEditsThreshold)
{
Expand All @@ -273,13 +294,21 @@ internal string CreateSearchQuery(string[] fields, string SearchTerm)

var booleanQuery = new BooleanQuery();
string searchTerm = QueryParser.Escape(SearchTerm);
var bCategoryBasedSearch = searchTerm.Contains('.') ? true : false;

if (searchTerm.Contains('.'))
searchType = SearchType.ByCategory;
else if (searchTerm.Contains(' '))
hasEmptySpaces = true;
else
searchType = SearchType.Normal;

var trimmedSearchTerm = hasEmptySpaces == true ? searchTerm.Replace(" ", "") : searchTerm;

foreach (string f in fields)
{
//Needs to be again due that now a query can contain different values per field (e.g. CategorySplitted:list, Name:tr)
searchTerm = QueryParser.Escape(SearchTerm);
if (bCategoryBasedSearch == true)
if (searchType == SearchType.ByCategory)
{
//This code section should be only executed if the search criteria is CategoryBased like "category.nodename"
if (f != nameof(LuceneConfig.NodeFieldsEnum.NameSplitted) &&
Expand All @@ -297,26 +326,26 @@ internal string CreateSearchQuery(string[] fields, string SearchTerm)
}
}

FuzzyQuery fuzzyQuery;
if (searchTerm.Length > LuceneConfig.FuzzySearchMinimalTermLength)
{
fuzzyQuery = new FuzzyQuery(new Term(f, searchTerm), fuzzyLogicMaxEdits);
booleanQuery.Add(fuzzyQuery, Occur.SHOULD);
}

//For normal search we don't consider the fields NameSplitted and CategorySplitted
if ((f == nameof(LuceneConfig.NodeFieldsEnum.NameSplitted) ||
f == nameof(LuceneConfig.NodeFieldsEnum.CategorySplitted)) && bCategoryBasedSearch == false)
f == nameof(LuceneConfig.NodeFieldsEnum.CategorySplitted)) && searchType != SearchType.ByCategory)
continue;

//This case is for when the user type something like "list.", I mean, not specifying the node name or part of it
if (string.IsNullOrEmpty(searchTerm))
continue;

var fieldQuery = CalculateFieldWeight(f, searchTerm);
var wildcardQuery = CalculateFieldWeight(f, searchTerm, true);
FuzzyQuery fuzzyQuery;
if (searchTerm.Length > LuceneConfig.FuzzySearchMinimalTermLength)
{
fuzzyQuery = new FuzzyQuery(new Term(f, hasEmptySpaces == true ? trimmedSearchTerm : searchTerm), fuzzyLogicMaxEdits);
booleanQuery.Add(fuzzyQuery, Occur.SHOULD);
}

var fieldQuery = CalculateFieldWeight(f, hasEmptySpaces == true ? trimmedSearchTerm : searchTerm);
var wildcardQuery = CalculateFieldWeight(f, hasEmptySpaces == true ? trimmedSearchTerm : searchTerm, true);

if (bCategoryBasedSearch && f == nameof(LuceneConfig.NodeFieldsEnum.CategorySplitted))
if (searchType == SearchType.ByCategory && f == nameof(LuceneConfig.NodeFieldsEnum.CategorySplitted))
{
booleanQuery.Add(fieldQuery, Occur.MUST);
booleanQuery.Add(wildcardQuery, Occur.MUST);
Expand All @@ -331,6 +360,10 @@ internal string CreateSearchQuery(string[] fields, string SearchTerm)
{
foreach (string s in searchTerm.Split(' ', '.'))
{
//If is a ByEmptySpace search and the splitted words match with more than MaxNodeNamesRepeated nodes then the word is skipped
int nodesFrequency = dynamoModel.SearchModel.Entries.Where(entry => entry.Name.ToLower().Contains(s) && !string.IsNullOrEmpty(s)).Count();
if (nodesFrequency > MaxNodeNamesRepeated) continue;

if (string.IsNullOrEmpty(s)) continue;

if (s.Length > LuceneConfig.FuzzySearchMinimalTermLength)
Expand Down
45 changes: 42 additions & 3 deletions src/DynamoCoreWpf/Utilities/WebView2Utilities.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,49 @@
using System.Windows;
using Dynamo.Wpf.Properties;
using Dynamo.Wpf.Utilities;
using DynamoUtilities;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;

namespace Dynamo.Utilities
namespace Dynamo.Wpf.Utilities
{
/// <summary>
/// Custom Webview2 class designed to have a safer cleanup logic and give better debugging capabilities
/// </summary>
public class DynamoWebView2 : WebView2
{
#region API/Data used for debugging/testing
private string tag;
#endregion

public DynamoWebView2() : base()
{
tag = TestUtilities.WebView2Tag;
}

protected override void Dispose(bool disposing)
{
if (System.Environment.CurrentManagedThreadId != Dispatcher.Thread.ManagedThreadId)
{
System.Console.WriteLine($"WebView2 instance with stamp {tag} is being disposed of on non-UI thread");
}
// We should dispose of webview2 only in the UI thread.
// Dispose can be called from the Finalizer (which can run on a non UI thread)
if (Dispatcher != null)
{
Dispatcher.Invoke(() =>
{
base.Dispose(disposing);
});
}
else
{
System.Console.WriteLine($"WebView2 instance with stamp {tag} is being disposed of but has no valid Dispatcher");
// Should we still try to dispose ? (might crash if not on UI thread)
base.Dispose(disposing);
}
}
}

/// <summary>
/// This class will contain several utility functions that will be used for the WebView2 component
/// </summary>
Expand All @@ -25,7 +64,7 @@ public static bool ValidateWebView2RuntimeInstalled()
catch (WebView2RuntimeNotFoundException)
{
var messageStr = Resources.ResourceManager.GetString("WebView2RequiredMessage");
if(messageStr.IndexOf("\\n") >= 0)
if (messageStr.IndexOf("\\n") >= 0)
messageStr = messageStr.Replace("\\n", "\n");

MessageBoxService.Show(messageStr,
Expand Down
Loading

0 comments on commit 48bf145

Please sign in to comment.