diff --git a/.gitignore b/.gitignore
index 940794e..36c5bd2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,64 @@
+################################################################################
+# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
+################################################################################
+# Do not use Notepad to edit this. Use VS, vim, Notepad++, etc. Uses UNIX line endings.
+
+*.cache
+*.nuget.props
+*.nuget.targets
+*.user
+*.suo
+*.sdf
+*.opensdf
+*.nupkg
+*.opendb
+*.VC.db
+
+project.lock.json
+[Oo]bj/
+[Bb]in/
+Debug/
+Release/
+Testing/
+x86/
+x64/
+ARM/
+ipch/
+AppPackages/
+.vs/
+/Loc/Generated/
+LocalizeApp.log
+Generated Files/
+/src/packages/
+
+
+#Localized strings
+/src/SoundRecorder/Resources/*
+!/src/SoundRecorder/Resources/en-us
+/src/SoundRecorder/BundleArtifacts/*
+!lib/**/*
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# Visual Studio Files
+.vs/
+
+
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
-##
-## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
@@ -12,17 +69,20 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
+# Debug-only configuration settings
+.conf
+
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
-x64/
-x86/
+[Xx]64/
+[Xx]86/
+[Bb]uild/
bld/
[Bb]in/
[Oo]bj/
-[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
@@ -42,11 +102,9 @@ TestResult.xml
[Rr]eleasePS/
dlldata.c
-# .NET Core
+# DNX
project.lock.json
-project.fragment.lock.json
artifacts/
-**/Properties/launchSettings.json
*_i.c
*_p.c
@@ -85,7 +143,6 @@ ipch/
*.sdf
*.cachefile
*.VC.db
-*.VC.VC.opendb
# Visual Studio profiler
*.psess
@@ -113,10 +170,6 @@ _TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
-# Visual Studio code coverage results
-*.coverage
-*.coveragexml
-
# NCrunch
_NCrunch_*
.*crunch*.local.xml
@@ -148,15 +201,12 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
+# TODO: Un-comment the next line if you do not want to checkin
+# your web deploy settings because they may include unencrypted
+# passwords
+#*.pubxml
+*.publishproj
# NuGet Packages
*.nupkg
@@ -166,7 +216,7 @@ PublishScripts/
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
-# NuGet v3's project.json files produces more ignorable files
+# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
@@ -178,11 +228,12 @@ csx/
ecf/
rcf/
-# Windows Store app package directories and files
+# Microsoft Azure ApplicationInsights config file
+ApplicationInsights.config
+
+# Windows Store app package directory
AppPackages/
BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
@@ -192,19 +243,16 @@ _pkginfo.txt
# Others
ClientBin/
+[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
-*.jfm
*.pfx
*.publishsettings
+node_modules/
orleans.codegen.cs
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
# RIA/Silverlight projects
Generated_Code/
@@ -219,7 +267,6 @@ UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
-*.ndf
# Business Intelligence projects
*.rdl.data
@@ -234,10 +281,6 @@ FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
-node_modules/
-
-# Typescript v1 declaration files
-typings/
# Visual Studio 6 build log
*.plg
@@ -245,9 +288,6 @@ typings/
# Visual Studio 6 workspace options file
*.opt
-# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
-*.vbw
-
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -256,33 +296,15 @@ typings/
**/*.Server/ModelManifest.xml
_Pvt_Extensions
+# LightSwitch generated files
+GeneratedArtifacts/
+ModelManifest.xml
+
# Paket dependency manager
.paket/paket.exe
-paket-files/
# FAKE - F# Make
.fake/
-# JetBrains Rider
-.idea/
-*.sln.iml
-
-# CodeRush
-.cr/
-
-# Python Tools for Visual Studio (PTVS)
-__pycache__/
-*.pyc
-
-# Cake - Uncomment if you are using it
-# tools/**
-# !tools/packages.config
-
-# Telerik's JustMock configuration file
-*.jmconfig
-
-# BizTalk build output
-*.btp.cs
-*.btm.cs
-*.odx.cs
-*.xsd.cs
+*.StyleCop
+/API_Keys
diff --git a/Installation.md b/Installation.md
new file mode 100644
index 0000000..0f47fde
--- /dev/null
+++ b/Installation.md
@@ -0,0 +1,33 @@
+# Installation
+
+## Requirements
+
+* Windows 10
+* .Net Framework
+* WPF libraries
+
+## Installation process
+
+You can fork the project in the github repository of [SnipAI](https://github.com).
+
+Then navigate to the code location on your Windows 10 machine and double click the solution file `SnipInsight.sln`.
+
+From there, select the C# project file `SnipInsight.csproj`. You should now be able to build and run the solution.
+
+The next step to test the AI features is to generate API keys for each AI service. Read our [guide](https://github.com) to learn how to generate API keys and then paste them in the settings panel.
+
+Congratulations! You should now have a fully working application to get started. Have fun testing the project and thank you for your contribution!
+
+## Troubleshooting
+
+> No project is selected for compilation
+
+Make sure you have selected the Debug configuration and are building the project correctly.
+
+> I can't run the project
+
+You are most likely missing libraries. Right click on the SnipInsight project and select *Install missing packages* if that option is available.
+
+> I don't see the fields to paste my API keys on the settings panel
+
+That is because you have not yet enabled AI assistance. Please enable it first and then enter your API keys.
\ No newline at end of file
diff --git a/NuGet.config b/NuGet.config
new file mode 100644
index 0000000..26eee3c
--- /dev/null
+++ b/NuGet.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 72f1506..014e816 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,20 @@
+#Introduction
+TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
-# Contributing
+#Getting Started
+TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
+1. Installation process
+2. Software dependencies
+3. Latest releases
+4. API references
-This project welcomes contributions and suggestions. Most contributions require you to agree to a
-Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
-the rights to use your contribution. For details, visit https://cla.microsoft.com.
+#Build and Test
+TODO: Describe and show how to build your code and run the tests.
-When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
-a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
-provided by the bot. You will only need to do this once across all repos using our CLA.
+#Contribute
+TODO: Explain how other users and developers can contribute to make your code better.
-This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
-For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
-contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+If you want to learn more about creating good readme files then refer the following [guidelines](https://www.visualstudio.com/en-us/docs/git/create-a-readme). You can also seek inspiration from the below readme files:
+- [ASP.NET Core](https://github.com/aspnet/Home)
+- [Visual Studio Code](https://github.com/Microsoft/vscode)
+- [Chakra Core](https://github.com/Microsoft/ChakraCore)
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIComponents/AISideNavigation.xaml b/SnipInsight/AIServices/AIComponents/AISideNavigation.xaml
new file mode 100644
index 0000000..a56e7c9
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/AISideNavigation.xaml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/AISideNavigation.xaml.cs b/SnipInsight/AIServices/AIComponents/AISideNavigation.xaml.cs
new file mode 100644
index 0000000..976590b
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/AISideNavigation.xaml.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+using CommonServiceLocator;
+using SnipInsight.ViewModels;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for AISideNavigation.xaml
+ ///
+ public partial class AISideNavigation : UserControl
+ {
+ public AISideNavigation()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/CelebrityRecognitionControl.xaml b/SnipInsight/AIServices/AIComponents/CelebrityRecognitionControl.xaml
new file mode 100644
index 0000000..f7e792e
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/CelebrityRecognitionControl.xaml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIComponents/CelebrityRecognitionControl.xaml.cs b/SnipInsight/AIServices/AIComponents/CelebrityRecognitionControl.xaml.cs
new file mode 100644
index 0000000..973230e
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/CelebrityRecognitionControl.xaml.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+using SnipInsight.AIServices.AIViewModels;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for CelebrityRecognitionControl.xaml
+ ///
+ public partial class CelebrityRecognitionControl : UserControl
+ {
+ public CelebrityRecognitionControl()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIComponents/EmptyState.xaml b/SnipInsight/AIServices/AIComponents/EmptyState.xaml
new file mode 100644
index 0000000..2472d62
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/EmptyState.xaml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/EmptyState.xaml.cs b/SnipInsight/AIServices/AIComponents/EmptyState.xaml.cs
new file mode 100644
index 0000000..a5bdae6
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/EmptyState.xaml.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for EmpyState.xaml
+ ///
+ public partial class EmptyState : UserControl
+ {
+ public EmptyState()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/ImageSearchControl.xaml b/SnipInsight/AIServices/AIComponents/ImageSearchControl.xaml
new file mode 100644
index 0000000..f796739
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/ImageSearchControl.xaml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIComponents/ImageSearchControl.xaml.cs b/SnipInsight/AIServices/AIComponents/ImageSearchControl.xaml.cs
new file mode 100644
index 0000000..48bda29
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/ImageSearchControl.xaml.cs
@@ -0,0 +1,36 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows;
+using System.Windows.Controls;
+using CommonServiceLocator;
+using SnipInsight.AIServices.AIViewModels;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for ImageSearch.xaml
+ ///
+ public partial class ImageSearchControl : UserControl
+ {
+ ///
+ /// Reference to image search vm
+ ///
+ private ImageSearchViewModel ImageSearchVM = ServiceLocator.Current.GetInstance();
+
+ ///
+ /// View for the Image Search
+ ///
+ public ImageSearchControl()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Passes the width of the panel to the bounded VM
+ ///
+ private void ImageGallerySizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ ImageSearchVM.CurrentWrapPanelWidth = ImageDisplayPanel.ActualWidth;
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/InsightsPermissions.xaml b/SnipInsight/AIServices/AIComponents/InsightsPermissions.xaml
new file mode 100644
index 0000000..2dfe745
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/InsightsPermissions.xaml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ About Cognitive Services
+
+
+
+
+
+
+ Privacy Policy
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/InsightsPermissions.xaml.cs b/SnipInsight/AIServices/AIComponents/InsightsPermissions.xaml.cs
new file mode 100644
index 0000000..5cbaa25
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/InsightsPermissions.xaml.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+using SnipInsight.AIServices.AIViewModels;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for InsightsPermissions.xaml
+ ///
+ public partial class InsightsPermissions : UserControl
+ {
+ public InsightsPermissions()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/LandmarkRecognitionControl.xaml b/SnipInsight/AIServices/AIComponents/LandmarkRecognitionControl.xaml
new file mode 100644
index 0000000..e7e74cf
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/LandmarkRecognitionControl.xaml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/LandmarkRecognitionControl.xaml.cs b/SnipInsight/AIServices/AIComponents/LandmarkRecognitionControl.xaml.cs
new file mode 100644
index 0000000..ef4d7d7
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/LandmarkRecognitionControl.xaml.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+using SnipInsight.AIServices.AIViewModels;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for LandmarkRecognitionControl.xaml
+ ///
+ public partial class LandmarkRecognitionControl : UserControl
+ {
+ public LandmarkRecognitionControl()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/LoadingAnimation.xaml b/SnipInsight/AIServices/AIComponents/LoadingAnimation.xaml
new file mode 100644
index 0000000..e6d7077
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/LoadingAnimation.xaml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/LoadingAnimation.xaml.cs b/SnipInsight/AIServices/AIComponents/LoadingAnimation.xaml.cs
new file mode 100644
index 0000000..9e44126
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/LoadingAnimation.xaml.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for LoadingAnimation.xaml
+ ///
+ public partial class LoadingAnimation : UserControl
+ {
+ public LoadingAnimation()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/NewsControls.xaml b/SnipInsight/AIServices/AIComponents/NewsControls.xaml
new file mode 100644
index 0000000..ec94fcd
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/NewsControls.xaml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/NewsControls.xaml.cs b/SnipInsight/AIServices/AIComponents/NewsControls.xaml.cs
new file mode 100644
index 0000000..bde5d01
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/NewsControls.xaml.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Windows.Controls;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for NewsControls.xaml
+ ///
+ public partial class NewsControls : UserControl
+ {
+ public NewsControls()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIComponents/OCRControl.xaml b/SnipInsight/AIServices/AIComponents/OCRControl.xaml
new file mode 100644
index 0000000..4aa50f8
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/OCRControl.xaml
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SnipInsight/AIServices/AIComponents/OCRControl.xaml.cs b/SnipInsight/AIServices/AIComponents/OCRControl.xaml.cs
new file mode 100644
index 0000000..f021f55
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/OCRControl.xaml.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIViewModels;
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for OCRControl.xaml
+ ///
+ public partial class OCRControl : UserControl
+ {
+ public OCRControl()
+ {
+ InitializeComponent();
+ }
+
+ }
+
+ ///
+ /// Converter class to bind visibility to a list of boolean parameters
+ ///
+ public class MultiBooleanToVisibility : IMultiValueConverter
+ {
+ ///
+ /// Converts enabled property of navigation buttons to clip visibility.
+ ///
+ /// Current status of nagivation buttons
+ ///
+ ///
+ ///
+ /// Visible if at least one button is disabled, otherwise hidden
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ foreach (object oj in values)
+ {
+ if ((Visibility)oj == Visibility.Visible)
+ {
+ return Visibility.Visible;
+ }
+ }
+
+ return Visibility.Collapsed;
+ }
+
+ ///
+ /// Not Implemented, does not need to be implementeds but required to be overriden as part of converter interface
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIComponents/ProductSearchControl.xaml b/SnipInsight/AIServices/AIComponents/ProductSearchControl.xaml
new file mode 100644
index 0000000..8fb2bbd
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/ProductSearchControl.xaml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIComponents/ProductSearchControl.xaml.cs b/SnipInsight/AIServices/AIComponents/ProductSearchControl.xaml.cs
new file mode 100644
index 0000000..3875efe
--- /dev/null
+++ b/SnipInsight/AIServices/AIComponents/ProductSearchControl.xaml.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using CommonServiceLocator;
+using SnipInsight.AIServices.AIViewModels;
+using System.Windows.Controls;
+
+namespace SnipInsight.AIServices.AIComponents
+{
+ ///
+ /// Interaction logic for ProductSearchControl.xaml
+ ///
+ public partial class ProductSearchControl : UserControl
+ {
+ ///
+ /// Reference to product search vm
+ ///
+ private ProductSearchViewModel ProductSearchVM = ServiceLocator.Current.GetInstance();
+
+ public ProductSearchControl()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Passes the width of the panel to the bounded VM
+ ///
+ private void ProductGallerySizeChanged(object sender, System.Windows.SizeChangedEventArgs e)
+ {
+ ProductSearchVM.CurrentWrapPanelWidth = ProductDisplayPanel.ActualWidth;
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/ContentModerationHandler.cs b/SnipInsight/AIServices/AILogic/ContentModerationHandler.cs
new file mode 100644
index 0000000..998b86d
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/ContentModerationHandler.cs
@@ -0,0 +1,189 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using SnipInsight.Properties;
+using SnipInsight.Util;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// Class to make API request image content moderator cognitive service
+ /// Parse API response and find the moderation result
+ ///
+ ///
+ internal class ContentModerationHandler
+ {
+ private HttpClient contentModerationClient;
+ private Uri URI { get; set; }
+ private String key;
+
+ ///
+ /// Number of total retries in case of request failure
+ ///
+ private const int RetryCount = 6;
+
+ ///
+ /// Delay in ms between each retry
+ ///
+ private const int RetryDelay = 500;
+
+ ///
+ /// Constructor to initialize API key and client for http request
+ ///
+ /// API Key
+ /// HTTP client for making the call
+ public ContentModerationHandler(string keyFile, HttpClient client=null)
+ {
+ RetrieveKey(keyFile);
+ contentModerationClient = client ?? new HttpClient();
+ contentModerationClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
+ BuildURI();
+ }
+
+ ///
+ /// Returns the result of the API call
+ ///
+ /// Captured image used for the call
+ /// object of httpclient
+ /// Returns true if content was recognized as inappropriate; false otherwise
+ public bool GetResult(MemoryStream stream)
+ {
+ try
+ {
+ var result = Run(stream);
+ return ExtractResult(result.Content.ReadAsStringAsync().Result);
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message);
+ return false;
+ }
+ }
+
+ ///
+ /// Build the URI for the API request
+ ///
+ private void BuildURI()
+ {
+ URI = new UriBuilder
+ {
+ Scheme = "https",
+ Host = "westus.api.cognitive.microsoft.com",
+ Path = "contentmoderator/moderate/v1.0/ProcessImage/Evaluate",
+ Query = "CacheImage=true"
+ }.Uri;
+ }
+
+ ///
+ /// Run the call and get the response message and records telemetry event with time to complete api call and status code
+ ///
+ /// Captured image used for the call
+ /// The HttpResponseMessage containing the Json result
+ private HttpResponseMessage Run(MemoryStream stream)
+ {
+ using (var content = new StreamContent(stream))
+ {
+ HttpResponseMessage result = null;
+
+ Stopwatch stopwatch = new Stopwatch();
+ stopwatch.Start();
+ try
+ {
+ // Execute the REST API call.
+ result = RequestAndRetry(() => contentModerationClient.PostAsync(URI, content).Result);
+ result.EnsureSuccessStatusCode();
+ }
+ finally
+ {
+ stopwatch.Stop();
+ string responseStatusCode = Telemetry.PropertyValue.NoResponse;
+ if (result != null)
+ {
+ responseStatusCode = result.StatusCode.ToString();
+ }
+ Telemetry.ApplicationLogger.Instance.SubmitApiCallEvent(Telemetry.EventName.CompleteApiCall,Telemetry.EventName.ContentModerationApi, stopwatch.ElapsedMilliseconds, responseStatusCode);
+ }
+ content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
+
+ return result;
+ }
+ }
+
+ ///
+ /// Run the function and retry if it fails
+ ///
+ /// Action to retry in case of failure
+ /// The response message containing the result
+ private HttpResponseMessage RequestAndRetry(Func action)
+ {
+ int retriesLeft = RetryCount;
+ int delay = RetryDelay;
+ HttpResponseMessage response = null;
+
+ while (retriesLeft > 0)
+ {
+ response = action();
+ if ((int)response.StatusCode != 429)
+ break;
+
+ Task.Delay(delay);
+ retriesLeft--;
+ delay *= 2;
+ }
+
+ return response;
+ }
+
+ ///
+ /// Converts the string response to deserialized object and get data from it
+ ///
+ /// Json from the API call
+ /// Returns true if content was recognized as inappropriate; false otherwise
+ private bool ExtractResult(string json)
+ {
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ return false;
+ }
+
+ using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
+ {
+ DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentModerationModel));
+ ContentModerationModel model = (ContentModerationModel)ser.ReadObject(ms);
+ return ParseModel(model);
+ }
+ }
+
+ ///
+ /// Gets the image strength from deserialized json response
+ ///
+ /// Deserialized ContentModerationModel object of API result
+ /// Returns true if content was recognized as inappropriate; false otherwise
+ private bool ParseModel(ContentModerationModel model)
+ {
+ if (model.AdultClassificationScore > (1 - (double)UserSettings.ContentModerationStrength / 100))
+ return true;
+ if (model.RacyClassificationScore > (1 - (double)UserSettings.ContentModerationStrength / 100))
+ return true;
+ return false;
+ }
+
+ private void RetrieveKey(string keyFile)
+ {
+ key = UserSettings.GetKey(keyFile);
+
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ Debug.WriteLine(keyFile + Resources.API_Key_Not_Found);
+ }
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/EntitySearchHandler.cs b/SnipInsight/AIServices/AILogic/EntitySearchHandler.cs
new file mode 100644
index 0000000..8e400ec
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/EntitySearchHandler.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using System;
+using System.Diagnostics;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// Backend logic to access API endpoint for Bing Entity Search service
+ ///
+ class EntitySearchHandler : CloudService
+ {
+ ///
+ /// Constructor that get implemented based on CloudService
+ /// when passing in the api key and http client
+ ///
+ /// API key for Bing Entity Search
+ public EntitySearchHandler(string keyFile): base(keyFile)
+ {
+ Host = "api.cognitive.microsoft.com";
+ Endpoint = "bing/v7.0/entities";
+ }
+
+ ///
+ /// Returns the result of the API call
+ ///
+ /// Captured image used for the call
+ /// object of httpclient to be used for the request
+ /// Data extracted from successful API response, default in case of failure
+ public async Task GetResult(string entityName)
+ {
+ try
+ {
+ var result = await Run(entityName);
+ return ExtractResult(await result.Content.ReadAsStringAsync());
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message + URI);
+ return default(EntitySearchModel);
+ }
+ }
+
+ ///
+ /// Run the API call and get the response message and records telemetry event with time to complete api call and status code
+ ///
+ /// Name of entity to be used for the call
+ /// The HttpResponseMessage containing the Json result
+ protected async Task Run(string entityName)
+ {
+ // Construct the uri to perform Bing Entity Search
+ RequestParams = "mkt=en-us&q=" + System.Net.WebUtility.UrlEncode(entityName);
+ BuildURI();
+
+ Stopwatch stopwatch = new Stopwatch();
+ stopwatch.Start();
+ HttpResponseMessage response = null;
+ try
+ {
+ // Execute the REST API GET call asynchronously
+ response = await RequestAndRetry(() => CloudServiceClient.GetAsync(URI));
+ response.EnsureSuccessStatusCode();
+ }
+ finally
+ {
+ stopwatch.Stop();
+ string responseStatusCode = Telemetry.PropertyValue.NoResponse;
+ if (response != null)
+ {
+ responseStatusCode = response.StatusCode.ToString();
+ }
+ Telemetry.ApplicationLogger.Instance.SubmitApiCallEvent(Telemetry.EventName.CompleteApiCall,Telemetry.EventName.EntitySearchApi, stopwatch.ElapsedMilliseconds, responseStatusCode);
+ }
+
+ return response;
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/HandWrittenTextHandler.cs b/SnipInsight/AIServices/AILogic/HandWrittenTextHandler.cs
new file mode 100644
index 0000000..eabda1b
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/HandWrittenTextHandler.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// Handwritten OCR call
+ ///
+ class HandWrittenTextHandler : CloudService
+ {
+ ///
+ /// Initalizes handler with correct endpoint
+ ///
+ public HandWrittenTextHandler(string keyFile): base(keyFile)
+ {
+ Host = "westcentralus.api.cognitive.microsoft.com";
+ Endpoint = "vision/v1.0/recognizeText";
+ RequestParams = "handwriting=true";
+ }
+
+ /// Run the stream asynchronously, return the HttpResonseMessage and records telemetry event with time to complete api call and status code
+ ///
+ /// Captured Image
+ /// ResponseMessage of the API request/call
+ protected override async Task Run(MemoryStream stream)
+ {
+ var result = await base.Run(stream);
+
+ if (!result.IsSuccessStatusCode)
+ {
+ return null;
+ }
+
+ string operationLocation = result.Headers.GetValues("Operation-Location").FirstOrDefault();
+ HttpResponseMessage response = await RequestAndRetry(() => CloudServiceClient.GetAsync(operationLocation));
+
+ Stopwatch stopwatch = new Stopwatch();
+ stopwatch.Start();
+ try
+ {
+ response.EnsureSuccessStatusCode();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ // Pass the exception to the next level
+ throw e;
+ }
+ finally{
+ stopwatch.Stop();
+ string responseStatusCode = Telemetry.PropertyValue.NoResponse;
+ if (result != null)
+ {
+ responseStatusCode = result.StatusCode.ToString();
+ }
+ Telemetry.ApplicationLogger.Instance.SubmitApiCallEvent(Telemetry.EventName.CompleteApiCall, Telemetry.EventName.HandWrittenTextApi, stopwatch.ElapsedMilliseconds, responseStatusCode);
+ }
+
+ return response;
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/ImageAnalysisHandler.cs b/SnipInsight/AIServices/AILogic/ImageAnalysisHandler.cs
new file mode 100644
index 0000000..289c74d
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/ImageAnalysisHandler.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// API logic for the Image Search service
+ ///
+ class ImageAnalysisHandler : CloudService
+ {
+ ///
+ /// Constructor to initialize API and client
+ ///
+ /// string of API key
+ /// Instance of HttpClient to be used for the API call
+ internal ImageAnalysisHandler(string key): base(key)
+ {
+ Host = "westus.api.cognitive.microsoft.com";
+ Endpoint = "/vision/v1.0/analyze";
+ RequestParams = "visualFeatures=Tags,Description&language=en&details=Celebrities,Landmarks";
+
+ BuildURI();
+ }
+
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/ImageSearchHandler.cs b/SnipInsight/AIServices/AILogic/ImageSearchHandler.cs
new file mode 100644
index 0000000..3e8ee69
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/ImageSearchHandler.cs
@@ -0,0 +1,80 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using System.Diagnostics;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// API logic for the Image Search service
+ ///
+ class ImageSearchHandler : CloudService
+ {
+ ///
+ /// Expected number of results for highest confidence on response
+ ///
+ private const double ConfidenceThreshold = 10.0;
+
+ ///
+ /// Normalize confidence of result to avoid dominance in suggested search
+ ///
+ private const double ConfidenceOffset = 0.2;
+
+ ///
+ /// Constructor to initialize API and client
+ ///
+ /// string of API key
+ /// Instance of HttpClient to be used for the API call
+ public ImageSearchHandler(string keyFile): base(keyFile)
+
+ {
+ Host = "api.cognitive.microsoft.com";
+ Endpoint = "/bing/v7.0/images/details";
+ RequestParams = "modules=SimilarImages";
+
+ BuildURI();
+ }
+
+ ///
+ /// Make the API call with supplied request contents and records telemetry event with time to complete api call and status code
+ ///
+ /// MemoryStream to read the image byte array
+ /// String response from the API call
+ protected override async Task Run(MemoryStream stream)
+ {
+ var strContent = new StreamContent(stream);
+ strContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { FileName = "AnyNameWorks" };
+
+ var content = new MultipartFormDataContent();
+ content.Add(strContent);
+
+ HttpResponseMessage result = null;
+ Stopwatch stopwatch = new Stopwatch();
+ stopwatch.Start();
+
+ try
+ {
+ // Execute the REST API call.
+ result = await RequestAndRetry(() => CloudServiceClient.PostAsync(URI, content));
+ result.EnsureSuccessStatusCode();
+ }
+ finally
+ {
+ stopwatch.Stop();
+ string responseStatusCode = Telemetry.PropertyValue.NoResponse;
+ if (result != null)
+ {
+ responseStatusCode = result.StatusCode.ToString();
+ }
+ Telemetry.ApplicationLogger.Instance.SubmitApiCallEvent(Telemetry.EventName.CompleteApiCall, Telemetry.EventName.CelebrityRecognitionApi, stopwatch.ElapsedMilliseconds, responseStatusCode);
+ }
+
+ return result;
+ }
+
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/LUISInsights.cs b/SnipInsight/AIServices/AILogic/LUISInsights.cs
new file mode 100644
index 0000000..4bf230c
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/LUISInsights.cs
@@ -0,0 +1,68 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using SnipInsight.Util;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ public class LUISInsights : CloudService
+ {
+ ///
+ /// Backend logic to access LUIS API
+ ///
+ ///
+ public LUISInsights(string keyFile) : base(keyFile)
+ {
+ Host = "westus.api.cognitive.microsoft.com";
+ Endpoint = "luis/v2.0/apps/" + UserSettings.GetKey("LUISAppId");
+ }
+
+ ///
+ /// Returns the result of the API call
+ ///
+ /// OCR text string used for the call
+ /// Data extracted from successful API response, default in case of failure
+ public async Task GetResult(string ocrResult)
+ {
+ try
+ {
+ var result = await Run(ocrResult);
+ return ExtractResult(await result.Content.ReadAsStringAsync());
+ }
+ catch (WebException e)
+ {
+ Debug.WriteLine(e.Message + URI);
+ return default(LUISModel);
+ }
+ }
+
+ ///
+ /// Run the API call and get the response message and records telemetry event with time to complete api call and status code
+ ///
+ /// OCR results to be used for the call
+ /// The HttpResponseMessage containing the Json result
+ protected async Task Run(string ocrResult)
+ {
+ var queryString = HttpUtility.ParseQueryString(string.Empty);
+ queryString["q"] = ocrResult;
+ // These optional request parameters are set to their default values
+ queryString["timezoneOffset"] = "0";
+ queryString["verbose"] = "false";
+ queryString["spellCheck"] = "false";
+ queryString["staging"] = "false";
+
+ RequestParams = queryString.ToString();
+ BuildURI();
+
+ HttpResponseMessage response = await RequestAndRetry(() => CloudServiceClient.GetAsync(URI));
+ response.EnsureSuccessStatusCode();
+
+ return response;
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/NewsHandler.cs b/SnipInsight/AIServices/AILogic/NewsHandler.cs
new file mode 100644
index 0000000..ea9fd74
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/NewsHandler.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ class NewsHandler: CloudService
+ {
+ ///
+ /// Constructor that get implemented based on CloudService
+ /// when passing in the api key and http client
+ ///
+ /// API key for Bing News Search
+ public NewsHandler(string keyFile): base(keyFile)
+ {
+ Host = "api.cognitive.microsoft.com";
+ Endpoint = "bing/v7.0/news/search";
+ }
+
+ ///
+ /// Returns the result of the API call
+ ///
+ /// Captured name used for the call
+ /// Data extracted from successful API response, default in case of failure
+ public async Task GetResult(string entityName)
+ {
+ try
+ {
+ var result = await Run(entityName);
+ return ExtractResult(await result.Content.ReadAsStringAsync());
+ }
+ catch (WebException e)
+ {
+ Debug.WriteLine(e.Message + URI);
+ return default(RawNewsModel);
+ }
+ }
+
+ ///
+ /// Run the API call and get the response message and records telemetry event with time to complete api call and status code
+ ///
+ /// Name of entity to be used for the call
+ /// The HttpResponseMessage containing the Json result
+ protected async Task Run(string entityName)
+ {
+ // Construct the uri to perform Bing Entity Search
+ RequestParams = "q=" + System.Net.WebUtility.UrlEncode(entityName);
+ BuildURI();
+
+ HttpResponseMessage response = null;
+ // Execute the REST API GET call asynchronously and
+ // await for non-blocking API call to Bing Entity Search
+ response = await RequestAndRetry(() => CloudServiceClient.GetAsync(URI));
+ response.EnsureSuccessStatusCode();
+
+ return response;
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/PrintedTextHandler.cs b/SnipInsight/AIServices/AILogic/PrintedTextHandler.cs
new file mode 100644
index 0000000..576982d
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/PrintedTextHandler.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// Printed Text OCR call
+ ///
+ class PrintedTextHandler : CloudService
+ {
+ ///
+ /// Initalizes handler with correct endpoint
+ ///
+ public PrintedTextHandler(string keyFile) : base(keyFile)
+ {
+ Host = "westcentralus.api.cognitive.microsoft.com";
+ Endpoint = "vision/v1.0/ocr";
+ RequestParams = "language=unk&detectOrientation=true";
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AILogic/ProductSearchHandler.cs b/SnipInsight/AIServices/AILogic/ProductSearchHandler.cs
new file mode 100644
index 0000000..804576c
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/ProductSearchHandler.cs
@@ -0,0 +1,58 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.AIServices.AIModels;
+using System.IO;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Threading.Tasks;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// Handler for the product search API
+ /// Takes a json API model in param and returns a list of products
+ ///
+ class ProductSearchHandler : CloudService
+ {
+ ///
+ /// Expected number of results for highest confidence on response
+ ///
+ private const double ConfidenceThreshold = 10.0;
+
+ ///
+ /// Normalize confidence of result to avoid dominance in suggested search
+ ///
+ private const double ConfidenceOffset = 0.1;
+
+ ///
+ /// Initalizes handler with correct endpoint
+ ///
+ public ProductSearchHandler(string keyFile) : base(keyFile)
+ {
+ Host = "api.cognitive.microsoft.com";
+ Endpoint = "/bing/v7.0/images/details";
+ RequestParams = "modules=SimilarProducts";
+
+ BuildURI();
+ }
+
+ ///
+ /// Run the HTTP call to get a list of similar products
+ ///
+ /// Captured Image
+ /// The HTTP response for the call
+ protected override async Task Run(MemoryStream stream)
+ {
+ var strContent = new StreamContent(stream);
+ strContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data") { FileName = "AnyNameWorks" };
+
+ var content = new MultipartFormDataContent();
+ content.Add(strContent);
+
+ // Execute the REST API call.
+ var result = await RequestAndRetry(() => CloudServiceClient.PostAsync(URI, content));
+ result.EnsureSuccessStatusCode();
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AILogic/TranslationHandler.cs b/SnipInsight/AIServices/AILogic/TranslationHandler.cs
new file mode 100644
index 0000000..7394d55
--- /dev/null
+++ b/SnipInsight/AIServices/AILogic/TranslationHandler.cs
@@ -0,0 +1,162 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using SnipInsight.Util;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using System.Xml;
+
+namespace SnipInsight.AIServices.AILogic
+{
+ ///
+ /// Handle the translation service
+ ///
+ public class TranslationHandler: CloudService
+ {
+ ///
+ /// Language codes for the translation
+ ///
+ private static string[] languageCodes;
+ private string detectedLanguage = string.Empty;
+
+ public TranslationHandler(string keyFile): base(keyFile)
+ {
+ Host = "api.microsofttranslator.com";
+
+ LanguageCodesAndTitles = new SortedDictionary(
+ Comparer.Create((a, b) => string.Compare(a, b, true))
+ );
+
+ Task.Run(async () =>
+ {
+ try
+ {
+ await GetLanguagesForTranslate();
+ await GetLanguageNames();
+
+ TranslatorEnable = true;
+ }
+ catch (WebException e)
+ {
+ Diagnostics.LogException(e);
+
+ TranslatorEnable = false;
+ }
+ });
+ }
+
+ ///
+ /// Enable translation if languages loaded
+ ///
+ public bool TranslatorEnable { get; set; }
+
+ ///
+ /// Maps the language codes to their full name
+ ///
+ public SortedDictionary LanguageCodesAndTitles { get; set; }
+
+ ///
+ /// Translate the text and return the translated text and records telemetry
+ ///
+ /// Text to translate
+ /// Translated text
+ public async Task GetResult(string textToTranslate, string fromLanguage, string toLanguage)
+ {
+ //TODO: After Refactoring Log results status and api run time
+ Telemetry.ApplicationLogger.Instance.SubmitApiCallEvent(Telemetry.EventName.CompleteApiCall, Telemetry.EventName.TranslationApi, -1, "N/A");
+
+ Endpoint = "/v2/Http.svc/Translate";
+ RequestParams = string.Format("text={0}&from={1}&to={2}",
+ HttpUtility.UrlEncode(textToTranslate),
+ fromLanguage,
+ toLanguage);
+
+ BuildURI();
+
+ var webRequest = WebRequest.Create(URI);
+ webRequest.Headers.Add("Ocp-Apim-Subscription-Key", Key);
+
+ try
+ {
+ WebResponse response = webRequest.GetResponse();
+
+ using (StreamReader translatedStream = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")))
+ {
+ String stringText = translatedStream.ReadToEnd();
+ int startPos = stringText.IndexOf(">")+1;
+
+ return stringText.Substring(startPos, stringText.IndexOf("<", startPos) - startPos);
+ }
+ }
+ catch (Exception e) when (e is WebException || e is XmlException )
+ {
+ Diagnostics.LogException(e);
+
+ return textToTranslate;
+ }
+ }
+
+ ///
+ /// Get the languages for the translation
+ ///
+ private async Task GetLanguagesForTranslate()
+ {
+ Endpoint = "/v2/Http.svc/GetLanguagesForTranslate";
+ RequestParams = "scope=text";
+ BuildURI();
+
+ WebRequest request = WebRequest.Create(URI);
+ request.Headers.Add("Ocp-Apim-Subscription-Key", this.Key);
+
+ WebResponse response = request.GetResponse();
+
+ using (Stream stream = response.GetResponseStream())
+ {
+ DataContractSerializer serializer = new DataContractSerializer(typeof(string[]));
+ languageCodes = (string[])serializer.ReadObject(stream);
+ }
+ }
+
+ ///
+ /// Populate the array of string with a list of language codes
+ ///
+ private async Task GetLanguageNames()
+ {
+ Endpoint = "/v2/Http.svc/GetLanguageNames";
+ RequestParams = "locale=en";
+ BuildURI();
+
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URI);
+ request.Headers.Add("Ocp-Apim-Subscription-Key", Key);
+ request.ContentType = "text/xml";
+ request.Method = "POST";
+
+ DataContractSerializer serializer = new DataContractSerializer(typeof(string[]));
+ using (Stream stream = request.GetRequestStream())
+ {
+ serializer.WriteObject(stream, languageCodes);
+ }
+
+ // Read and parse the XML response
+ var response = request.GetResponse();
+
+ string[] languageNames;
+ using (Stream stream = response.GetResponseStream())
+ {
+ languageNames = (string[])serializer.ReadObject(stream);
+ }
+
+ // Load the dictionary for the combo box
+ for (int i = 0; i < languageNames.Length; i++)
+ {
+ //Sorted by the language name for diaplay
+ LanguageCodesAndTitles.Add(languageNames[i], languageCodes[i]);
+ }
+ }
+ }
+}
diff --git a/SnipInsight/AIServices/AIManager.cs b/SnipInsight/AIServices/AIManager.cs
new file mode 100644
index 0000000..6118c12
--- /dev/null
+++ b/SnipInsight/AIServices/AIManager.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using CommonServiceLocator;
+using SnipInsight.AIServices.AIViewModels;
+using SnipInsight.ViewModels;
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace SnipInsight.AIServices
+{
+ ///
+ /// Manage the API calls asynchronously
+ /// Controls what gets displayed into the panel
+ /// Secure the keys and feed the data to the handlers
+ ///
+ class AIManager
+ {
+ public Byte[] ImageBytes { get; set; }
+
+ ///
+ /// Run all the Azure API calls asynchronously.
+ ///
+ internal void RunAllAsyncCalls()
+ {
+ var AIViewModel = ServiceLocator.Current.GetInstance();
+
+ AIViewModel.AIControlsVisibility = Visibility.Collapsed;
+ AIViewModel.EmptyStateVisibility = Visibility.Collapsed;
+ AIViewModel.LoadingVisibility = Visibility.Visible;
+
+ AppManager.TheBoss.ViewModel.CelebritiesCanvas = null;
+
+ // If we decide to go back to the previously used panel after each snip
+ // Then removing this line would allow it
+ AIViewModel.SuggestedCommand.Execute(null);
+
+ MemoryStream imageSearchStream = new MemoryStream(ImageBytes);
+ var imageSearchVM = ServiceLocator.Current.GetInstance();
+ var imageSearchTask = imageSearchVM.LoadImages(imageSearchStream);
+
+ MemoryStream productSearchStream = new MemoryStream(ImageBytes);
+ var productSearchVM = ServiceLocator.Current.GetInstance();
+ var productSearchTask = productSearchVM.LoadProducts(productSearchStream);
+
+ MemoryStream imageAnalysisStream = new MemoryStream(ImageBytes);
+ var imageAnalysisVM = ServiceLocator.Current.GetInstance();
+ var imageAnalysisTask = imageAnalysisVM.LoadAnalysis(imageAnalysisStream);
+
+ MemoryStream writtenStream = new MemoryStream(ImageBytes);
+ MemoryStream printedStream = new MemoryStream(ImageBytes);
+ var ocrVM = ServiceLocator.Current.GetInstance();
+ var ocrTask = ocrVM.LoadText(writtenStream, printedStream);
+
+ var completionTask = Task.WhenAll(imageSearchTask, productSearchTask, imageAnalysisTask, ocrTask);
+
+ completionTask.ContinueWith(t =>
+ {
+ AIViewModel.LoadingVisibility = Visibility.Collapsed;
+
+ if (LookForEmptyState())
+ {
+ AIViewModel.EmptyStateVisibility = Visibility.Visible;
+ }
+ else
+ {
+ AIViewModel.AIControlsVisibility = Visibility.Visible;
+ }
+ });
+ }
+
+ ///
+ /// Check if we should display the empty state panel or not
+ ///
+ private bool LookForEmptyState()
+ {
+ /// Temporary solution to display the empty state in case of no result.
+ /// Will be changed to work with the Task at a later date but this
+ /// Workaround does the job without being detrimental for now.
+ return ServiceLocator.Current.GetInstance().IsVisible == Visibility.Collapsed &&
+ ServiceLocator.Current.GetInstance().IsVisible == Visibility.Collapsed &&
+ ServiceLocator.Current.GetInstance().IsVisible == Visibility.Collapsed &&
+ ServiceLocator.Current.GetInstance().IsPeopleVisible == Visibility.Collapsed &&
+ ServiceLocator.Current.GetInstance().IsPlaceVisible == Visibility.Collapsed;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SnipInsight/AIServices/AIModels/ContentModerationModel.cs b/SnipInsight/AIServices/AIModels/ContentModerationModel.cs
new file mode 100644
index 0000000..ae80ff3
--- /dev/null
+++ b/SnipInsight/AIServices/AIModels/ContentModerationModel.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace SnipInsight.AIServices.AIModels
+{
+ [DataContract]
+ public class Status
+ {
+ [DataMember(Name = "Code")]
+ public int Code { get; set; }
+ [DataMember(Name = "Description")]
+ public string Description { get; set; }
+ [DataMember(Name = "Exception")]
+ public object Exception { get; set; }
+ }
+
+ [DataContract]
+ public class ContentModerationModel
+ {
+ [DataMember(Name = "AdultClassificationScore")]
+ public double AdultClassificationScore { get; set; }
+ [DataMember(Name = "IsImageAdultClassified")]
+ public bool IsImageAdultClassified { get; set; }
+ [DataMember(Name = "RacyClassificationScore")]
+ public double RacyClassificationScore { get; set; }
+ [DataMember(Name = "IsImageRacyClassified")]
+ public bool IsImageRacyClassified { get; set; }
+ [DataMember(Name = "AdvancedInfo")]
+ public List