Refactoring. ExcelDNA.IntelliSense library add.

Add description to Excel functions.
This commit is contained in:
Sergey Chebotar 2021-12-13 20:39:41 +03:00
parent b7c65d64e9
commit e175a634ce
11 changed files with 166 additions and 137 deletions

View File

@ -8,23 +8,6 @@
<Reference Path="System.Memory.dll" Pack="true" /> <Reference Path="System.Memory.dll" Pack="true" />
<Reference Path="System.Numerics.Vectors.dll" Pack="true" /> <Reference Path="System.Numerics.Vectors.dll" Pack="true" />
<Reference Path="System.Runtime.CompilerServices.Unsafe.dll" Pack="true" /> <Reference Path="System.Runtime.CompilerServices.Unsafe.dll" Pack="true" />
<Reference Path="System.Text.Encoding.CodePages.dll" Pack="true" /> <Reference Path="System.Text.Encoding.CodePages.dll" Pack="true" />
<Reference Path="ExcelDna.IntelliSense.dll" Pack="true" />
<!-- </DnaLibrary>
The RuntimeVersion attribute above allows only the following setting:
* RuntimeVersion="v4.0" - for .NET 4.5 or higher
You can have IntelliSense (autocomplete) and validation for this file.
See https://github.com/Excel-DNA/ExcelDna/tree/master/Distribution/XmlSchemas/
Additional referenced assemblies can be specified by adding 'Reference' tags.
These libraries will not be examined and registered with Excel as add-in libraries,
but will be packed into the -packed.xll file and loaded at runtime as needed.
For example:
<Reference Path="Another.Library.dll" Pack="true" />
Excel-DNA also allows the XML for ribbon UI extensions to be specified in the .dna file.
See the main Excel-DNA site at https://excel-dna.net for downloads of the full distribution.
-->
</DnaLibrary>

View File

@ -42,6 +42,9 @@
<HintPath>packages\ExcelDna.Integration.1.5.0\lib\net452\ExcelDna.Integration.dll</HintPath> <HintPath>packages\ExcelDna.Integration.1.5.0\lib\net452\ExcelDna.Integration.dll</HintPath>
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="ExcelDna.IntelliSense, Version=1.4.4.0, Culture=neutral, PublicKeyToken=f225e9659857edbe, processorArchitecture=MSIL">
<HintPath>packages\ExcelDna.IntelliSense.1.5.0\lib\net452\ExcelDna.IntelliSense.dll</HintPath>
</Reference>
<Reference Include="ExcelDna.Registration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=f225e9659857edbe, processorArchitecture=MSIL"> <Reference Include="ExcelDna.Registration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=f225e9659857edbe, processorArchitecture=MSIL">
<HintPath>packages\ExcelDna.Registration.1.5.0\lib\net452\ExcelDna.Registration.dll</HintPath> <HintPath>packages\ExcelDna.Registration.1.5.0\lib\net452\ExcelDna.Registration.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -96,7 +99,10 @@
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Source\Assistant\MemoryCacheExtensions.cs" />
<Compile Include="Source\Assistant\ParseUtil.cs" />
<Compile Include="Source\Assistant\RequestModifier.cs" /> <Compile Include="Source\Assistant\RequestModifier.cs" />
<Compile Include="Source\Assistant\SkuExtension.cs" />
<Compile Include="Source\Ribbon\RibbonController.cs" /> <Compile Include="Source\Ribbon\RibbonController.cs" />
<Compile Include="Source\Assistant\HttpClientUtil.cs" /> <Compile Include="Source\Assistant\HttpClientUtil.cs" />
<Compile Include="Source\Assistant\StoreResponse.cs" /> <Compile Include="Source\Assistant\StoreResponse.cs" />

View File

@ -1,4 +1,5 @@
using ExcelDna.Integration; using ExcelDna.Integration;
using ExcelDna.IntelliSense;
using ExcelDna.Registration; using ExcelDna.Registration;
using Microsoft.Win32; using Microsoft.Win32;
using System.Net.Http; using System.Net.Http;
@ -24,11 +25,12 @@ namespace RehauSku
{ {
RegisterFunctions(); RegisterFunctions();
GetRegistryKeys(); GetRegistryKeys();
IntelliSenseServer.Install();
} }
public void AutoClose() public void AutoClose()
{ {
IntelliSenseServer.Uninstall();
} }
void RegisterFunctions() void RegisterFunctions()

View File

@ -5,16 +5,52 @@ namespace RehauSku
{ {
public class Functions public class Functions
{ {
[ExcelFunction] [ExcelFunction(Description = "Получение названия первого продукта по запросу в интернет-магазин REHAU")]
public static object RAUNAME(string request) public static object RAUNAME([ExcelArgument(Name = "Запрос", Description = "Запрос в свободной форме или ячейка с запросом")] string request)
=> SkuAssist.GetProduct(request, ProductField.Name); => MakeRequest(request, ProductField.Name);
[ExcelFunction] [ExcelFunction(Description = "Получение артикула первого продукта по запросу в интернет-магазин REHAU")]
public static object RAUSKU(string request) public static object RAUSKU([ExcelArgument(Name = "Запрос", Description = "Запрос в свободной форме или ячейка с запросом")] string request)
=> SkuAssist.GetProduct(request, ProductField.Id); => MakeRequest(request, ProductField.Id);
[ExcelFunction] [ExcelFunction(Description = "Получение цены первого продукта по запросу в интернет-магазин REHAU")]
public static object RAUPRICE(string request) public static object RAUPRICE([ExcelArgument(Name = "Запрос", Description = "Запрос в свободной форме или ячейка с запросом")] string request)
=> SkuAssist.GetProduct(request, ProductField.Price); => MakeRequest(request, ProductField.Price);
private static object MakeRequest(string request, ProductField field)
{
object result;
if (request.IsCached())
result = request.GetFromCache();
else
{
result = ExcelAsyncUtil.Run("Request", request, delegate
{
return request.RequestAndCache().GetAwaiter().GetResult();
});
}
if (result == null)
return "Не найдено :(";
if (result.Equals(ExcelError.ExcelErrorNA))
return "Загрузка...";
IProduct product = result as IProduct;
switch (field)
{
case ProductField.Name:
return product.Name;
case ProductField.Id:
return product.Id;
case ProductField.Price:
return double.Parse(product.Price, System.Globalization.CultureInfo.InvariantCulture);
default:
return null;
}
}
} }
} }

View File

@ -1,6 +1,4 @@
using AngleSharp; using System;
using AngleSharp.Dom;
using System;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,8 +9,10 @@ namespace RehauSku.Assistant
{ {
private static HttpClient _httpClient = AddIn.httpClient; private static HttpClient _httpClient = AddIn.httpClient;
public async static Task<string> GetContentByUriAsync(Uri uri) public async static Task<string> GetContentByRequest(string request)
{ {
Uri uri = request.ConvertToUri();
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls12 |
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls11 |
@ -21,15 +21,7 @@ namespace RehauSku.Assistant
return await _httpClient.GetStringAsync(uri); return await _httpClient.GetStringAsync(uri);
} }
public async static Task<IDocument> ContentToDocAsync(Task<string> content) private static Uri ConvertToUri(this string request)
{
IConfiguration config = Configuration.Default;
IBrowsingContext context = BrowsingContext.New(config);
return await context.OpenAsync(req => req.Content(content.Result));
}
public static Uri ConvertToUri(this string request)
{ {
UriBuilder baseUri = new UriBuilder("https", "shop-rehau.ru"); UriBuilder baseUri = new UriBuilder("https", "shop-rehau.ru");

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Caching;
using System.Threading.Tasks;
namespace RehauSku.Assistant
{
static class MemoryCacheExtensions
{
public static bool IsCached(this string request)
{
return MemoryCache.Default.Contains(request);
}
public static IProduct GetFromCache(this string request)
{
return MemoryCache.Default[request] as IProduct;
}
public static async Task<IProduct> RequestAndCache(this string request)
{
IProduct product = await SkuAssist.GetProductAsync(request);
if (product == null)
return null;
MemoryCache.Default.Add(request, product, DateTime.Now.AddMinutes(10));
return product;
}
}
}

View File

@ -0,0 +1,55 @@
using AngleSharp;
using AngleSharp.Dom;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RehauSku.Assistant
{
static class ParseUtil
{
public async static Task<IDocument> ContentToDocAsync(string content)
{
IConfiguration config = Configuration.Default;
IBrowsingContext context = BrowsingContext.New(config);
return await context.OpenAsync(req => req.Content(content));
}
public static IProduct GetProduct(IDocument document)
{
try
{
string script = document
.Scripts
.Where(s => s.InnerHtml.Contains("dataLayer"))
.FirstOrDefault()
.InnerHtml;
string json = script
.Substring(script.IndexOf("push(") + 5)
.TrimEnd(new[] { ')', ';', '\n', ' ' });
if (!json.Contains("impressions"))
return null;
StoreResponce storeResponse = JsonConvert.DeserializeObject<StoreResponce>(json);
IProduct product = storeResponse
.Ecommerce
.Impressions
.Where(p => p.Id.IsRehauSku())
.FirstOrDefault();
return product;
}
catch (NullReferenceException e)
{
MessageBox.Show(e.Message, "Ошибка получения данных", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
}
}

View File

@ -1,13 +1,4 @@
using AngleSharp.Dom; using System.Threading.Tasks;
using ExcelDna.Integration;
using Newtonsoft.Json;
using System;
using System.Globalization;
using System.Linq;
using System.Runtime.Caching;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace RehauSku.Assistant namespace RehauSku.Assistant
{ {
@ -20,91 +11,12 @@ namespace RehauSku.Assistant
static class SkuAssist static class SkuAssist
{ {
public static async Task<IProduct> GetProduct(string request) public static async Task<IProduct> GetProductAsync(string request)
{ {
Uri uri = request.ConvertToUri(); var content = await HttpClientUtil.GetContentByRequest(request);
var document = await ParseUtil.ContentToDocAsync(content);
Task<string> contentTask = Task.Run(() => HttpClientUtil.GetContentByUriAsync(uri)); return ParseUtil.GetProduct(document);
Task<IDocument> documentTask = await contentTask.ContinueWith(content => HttpClientUtil.ContentToDocAsync(content));
return GetProduct(documentTask.Result);
}
public static IProduct GetProduct(IDocument document)
{
try
{
string script = document
.Scripts
.Where(s => s.InnerHtml.Contains("dataLayer"))
.FirstOrDefault()
.InnerHtml;
string json = script
.Substring(script.IndexOf("push(") + 5)
.TrimEnd(new[] { ')', ';', '\n', ' ' });
if (!json.Contains("impressions"))
return null;
StoreResponce storeResponse = JsonConvert.DeserializeObject<StoreResponce>(json);
IProduct product = storeResponse
.Ecommerce
.Impressions
.Where(p => p.Id.IsRehauSku())
.FirstOrDefault();
return product;
}
catch (NullReferenceException e)
{
MessageBox.Show(e.Message, "Ошибка получения данных", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
public static object GetProduct(string request, ProductField field)
{
IProduct product;
if (MemoryCache.Default.Contains(request))
{
product = MemoryCache.Default[request] as IProduct;
}
else
{
object result = ExcelAsyncUtil.Run("RauName", new[] { request },
delegate
{
Task<IProduct> p = Task.Run(() => GetProduct(request));
return p.Result;
});
if (result == null)
return "Не найдено :(";
if (result.Equals(ExcelError.ExcelErrorNA))
return "Загрузка...";
product = result as IProduct;
MemoryCache.Default.Add(request, product, DateTime.Now.AddMinutes(10));
}
switch (field)
{
case ProductField.Name:
return product.Name;
case ProductField.Id:
return product.Id;
case ProductField.Price:
return double.Parse((string)product.Price, CultureInfo.InvariantCulture);
default:
return ExcelError.ExcelErrorValue;
}
}
public static bool IsRehauSku(this string line)
{
return Regex.IsMatch(line, @"^[1]\d{6}[1]\d{3}$");
} }
} }
} }

View File

@ -0,0 +1,12 @@
using System.Text.RegularExpressions;
namespace RehauSku.Assistant
{
static class SkuExtension
{
public static bool IsRehauSku(this string line)
{
return Regex.IsMatch(line, @"^[1]\d{6}[1]\d{3}$");
}
}
}

View File

@ -52,7 +52,7 @@ namespace RehauSku.DataExport
object current = SelectedCells[row, column]; object current = SelectedCells[row, column];
if (current.GetType() == typeof(string) if (current.GetType() == typeof(string)
&& SkuAssist.IsRehauSku((string)current)) && ((string)current).IsRehauSku())
sku = (string)current; sku = (string)current;
else if (current.GetType() == typeof(string) else if (current.GetType() == typeof(string)

View File

@ -3,6 +3,7 @@
<package id="AngleSharp" version="0.16.1" targetFramework="net48" /> <package id="AngleSharp" version="0.16.1" targetFramework="net48" />
<package id="ExcelDna.AddIn" version="1.5.0" targetFramework="net480" /> <package id="ExcelDna.AddIn" version="1.5.0" targetFramework="net480" />
<package id="ExcelDna.Integration" version="1.5.0" targetFramework="net480" /> <package id="ExcelDna.Integration" version="1.5.0" targetFramework="net480" />
<package id="ExcelDna.IntelliSense" version="1.5.0" targetFramework="net48" />
<package id="ExcelDna.Interop" version="14.0.1" targetFramework="net48" /> <package id="ExcelDna.Interop" version="14.0.1" targetFramework="net48" />
<package id="ExcelDna.Registration" version="1.5.0" targetFramework="net480" /> <package id="ExcelDna.Registration" version="1.5.0" targetFramework="net480" />
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" /> <package id="Newtonsoft.Json" version="13.0.1" targetFramework="net48" />