diff options
-rw-r--r-- | CancellationDisposable.cs | 38 | ||||
-rw-r--r-- | DefaultDisposable.cs | 21 | ||||
-rw-r--r-- | ExcelTaskUtil.cs | 133 | ||||
-rw-r--r-- | Functions.cs | 24 | ||||
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | Rehau.Sku.Assist-AddIn-packed.xll | bin | 0 -> 653312 bytes | |||
-rw-r--r-- | Rehau.Sku.Assist-AddIn.dna | 2 | ||||
-rw-r--r-- | Rehau.Sku.Assist.csproj | 41 | ||||
-rw-r--r-- | SkuAssist.cs | 56 | ||||
-rw-r--r-- | app.config | 15 | ||||
-rw-r--r-- | packages.config | 11 | ||||
-rw-r--r-- | src/Assistant/IProduct.cs | 9 | ||||
-rw-r--r-- | src/Assistant/Product.cs | 21 | ||||
-rw-r--r-- | src/Assistant/SkuAssist.cs | 45 | ||||
-rw-r--r-- | src/ExcelDNA/AddIn.cs | 24 | ||||
-rw-r--r-- | src/ExcelDNA/Functions.cs | 21 |
16 files changed, 177 insertions, 288 deletions
diff --git a/CancellationDisposable.cs b/CancellationDisposable.cs deleted file mode 100644 index 700a2c2..0000000 --- a/CancellationDisposable.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Threading; - -namespace Rehau.Sku.Assist -{ - sealed class CancellationDisposable : IDisposable - { - - readonly CancellationTokenSource cts; - public CancellationDisposable(CancellationTokenSource cts) - { - if (cts == null) - { - throw new ArgumentNullException("cts"); - } - - this.cts = cts; - } - - public CancellationDisposable() - : this(new CancellationTokenSource()) - { - } - - public CancellationToken Token - { - get { return cts.Token; } - } - - public void Dispose() - { - cts.Cancel(); - } - } - -} - - diff --git a/DefaultDisposable.cs b/DefaultDisposable.cs deleted file mode 100644 index f991181..0000000 --- a/DefaultDisposable.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace Rehau.Sku.Assist -{ - sealed class DefaultDisposable : IDisposable - { - - public static readonly DefaultDisposable Instance = new DefaultDisposable(); - - DefaultDisposable() - { - } - - public void Dispose() - { - } - } - -} - - diff --git a/ExcelTaskUtil.cs b/ExcelTaskUtil.cs deleted file mode 100644 index c7d1b94..0000000 --- a/ExcelTaskUtil.cs +++ /dev/null @@ -1,133 +0,0 @@ -using ExcelDna.Integration; -using System.Threading.Tasks; -using System; -using System.Threading; - -namespace Rehau.Sku.Assist -{ - internal static class ExcelTaskUtil - { - public static object Run<TResult>(string callerFunctionName, object callerParameters, Func<CancellationToken, Task<TResult>> taskSource) - { - return ExcelAsyncUtil.Observe(callerFunctionName, callerParameters, delegate - { - var cts = new CancellationTokenSource(); - var task = taskSource(cts.Token); - return new ExcelTaskObservable<TResult>(task, cts); - }); - } - - public static object Run<TResult>(string callerFunctionName, object callerParameters, Func<Task<TResult>> taskSource) - { - return ExcelAsyncUtil.Observe(callerFunctionName, callerParameters, delegate - { - var task = taskSource(); - return new ExcelTaskObservable<TResult>(task); - }); - } - - class ExcelTaskObservable<TResult> : IExcelObservable - { - readonly Task<TResult> _task; - readonly CancellationTokenSource _cts; - public ExcelTaskObservable(Task<TResult> task) - { - _task = task; - } - - public ExcelTaskObservable(Task<TResult> task, CancellationTokenSource cts) - : this(task) - { - _cts = cts; - } - - public IDisposable Subscribe(IExcelObserver observer) - { - switch (_task.Status) - { - case TaskStatus.RanToCompletion: - observer.OnNext(_task.Result); - observer.OnCompleted(); - break; - case TaskStatus.Faulted: - observer.OnError(_task.Exception.InnerException); - break; - case TaskStatus.Canceled: - observer.OnError(new TaskCanceledException(_task)); - break; - default: - _task.ContinueWith(t => - { - switch (t.Status) - { - case TaskStatus.RanToCompletion: - observer.OnNext(t.Result); - observer.OnCompleted(); - break; - case TaskStatus.Faulted: - observer.OnError(t.Exception.InnerException); - break; - case TaskStatus.Canceled: - observer.OnError(new TaskCanceledException(t)); - break; - } - }); - break; - } - - if (_cts != null) - { - return new CancellationDisposable(_cts); - } - - return DefaultDisposable.Instance; - } - } - - sealed class DefaultDisposable : IDisposable - { - - public static readonly DefaultDisposable Instance = new DefaultDisposable(); - - DefaultDisposable() - { - } - - public void Dispose() - { - } - } - - sealed class CancellationDisposable : IDisposable - { - - readonly CancellationTokenSource cts; - public CancellationDisposable(CancellationTokenSource cts) - { - if (cts == null) - { - throw new ArgumentNullException("cts"); - } - - this.cts = cts; - } - - public CancellationDisposable() - : this(new CancellationTokenSource()) - { - } - - public CancellationToken Token - { - get { return cts.Token; } - } - - public void Dispose() - { - cts.Cancel(); - } - } - - } -} - diff --git a/Functions.cs b/Functions.cs deleted file mode 100644 index 999a942..0000000 --- a/Functions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using ExcelDna.Integration; - -namespace Rehau.Sku.Assist -{ - public class Functions : IExcelAddIn - { - [ExcelFunction(description: "Получение наименования и артикула позиции")] - public static string RAUNAME(string request) - { - SkuAssist.EnsureHttpInitialized(); - return SkuAssist.GetSku(request); - } - - public void AutoClose() - { - ExcelIntegration.RegisterUnhandledExceptionHandler( - delegate (object ex) { return string.Format("!!!ERROR: {0}", ex.ToString()); }); - } - - public void AutoOpen() - { - } - } -}
\ No newline at end of file @@ -20,9 +20,9 @@ ----- ### Ограничения прототипа -1. Не реализована многопоточность выполнения метода формулы RAUNAME. Не рекомендуется попытка выполнения большого числа запросов одновременно. +1. ~~Не реализована многопоточность выполнения метода формулы RAUNAME. Не рекомендуется попытка выполнения большого числа запросов одновременно.~~ 2. Относительно невысокая скорость работы из-за отсутствия поискового API сайта магазина. -3. Из-за особенностей реализации интернет-магазина приходится фильтровать часть результатов выдачи, что может сказываться на качестве распознавания запроса. +3. ~~Из-за особенностей реализации интернет-магазина приходится фильтровать часть результатов выдачи, что может сказываться на качестве распознавания запроса.~~ ## Установка 1. Скачать архив *Rehau.Sku.Assist.zip* по ссылке https://github.com/schebotar/Rehau.Sku.Assist/releases/tag/v0.1.0-alpha diff --git a/Rehau.Sku.Assist-AddIn-packed.xll b/Rehau.Sku.Assist-AddIn-packed.xll Binary files differnew file mode 100644 index 0000000..aea355f --- /dev/null +++ b/Rehau.Sku.Assist-AddIn-packed.xll diff --git a/Rehau.Sku.Assist-AddIn.dna b/Rehau.Sku.Assist-AddIn.dna index b7322e9..bdf8847 100644 --- a/Rehau.Sku.Assist-AddIn.dna +++ b/Rehau.Sku.Assist-AddIn.dna @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <DnaLibrary Name="Rehau.Sku.Assist Add-In" RuntimeVersion="v4.0" xmlns="http://schemas.excel-dna.net/addin/2020/07/dnalibrary"> - <ExternalLibrary Path="Rehau.Sku.Assist.dll" ExplicitExports="false" LoadFromBytes="true" Pack="true" IncludePdb="false" /> + <ExternalLibrary Path="Rehau.Sku.Assist.dll" ExplicitRegistration="true" LoadFromBytes="true" Pack="true" IncludePdb="false" /> <!-- The RuntimeVersion attribute above allows only the following setting: diff --git a/Rehau.Sku.Assist.csproj b/Rehau.Sku.Assist.csproj index 37744c7..5b7438a 100644 --- a/Rehau.Sku.Assist.csproj +++ b/Rehau.Sku.Assist.csproj @@ -14,6 +14,7 @@ <Deterministic>true</Deterministic> <NuGetPackageImportStamp> </NuGetPackageImportStamp> + <TargetFrameworkProfile /> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -35,37 +36,59 @@ <ItemGroup> <Reference Include="AngleSharp, Version=0.16.1.0, Culture=neutral, PublicKeyToken=e83494dcdc6d31ea, processorArchitecture=MSIL"> <HintPath>packages\AngleSharp.0.16.1\lib\net472\AngleSharp.dll</HintPath> + <Private>True</Private> </Reference> <Reference Include="ExcelDna.Integration, Version=1.1.0.0, Culture=neutral, PublicKeyToken=f225e9659857edbe, processorArchitecture=MSIL"> <HintPath>packages\ExcelDna.Integration.1.5.0\lib\net452\ExcelDna.Integration.dll</HintPath> + <Private>False</Private> + </Reference> + <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> + <Private>True</Private> </Reference> <Reference Include="System" /> <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <HintPath>packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath> </Reference> + <Reference Include="System.Configuration" /> <Reference Include="System.Core" /> - <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> - <HintPath>packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath> + <Reference Include="System.Data.OracleClient" /> + <Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath> + </Reference> + <Reference Include="System.Net" /> + <Reference Include="System.Numerics" /> + <Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath> + </Reference> + <Reference Include="System.Runtime.Caching" /> + <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath> </Reference> - <Reference Include="System.Text.Encoding.CodePages, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> - <HintPath>packages\System.Text.Encoding.CodePages.5.0.0\lib\net461\System.Text.Encoding.CodePages.dll</HintPath> + <Reference Include="System.Security" /> + <Reference Include="System.ServiceProcess" /> + <Reference Include="System.Text.Encoding.CodePages, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>packages\System.Text.Encoding.CodePages.6.0.0\lib\net461\System.Text.Encoding.CodePages.dll</HintPath> </Reference> + <Reference Include="System.Transactions" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Net.Http" /> <Reference Include="System.Xml" /> + <Reference Include="WindowsBase" /> </ItemGroup> <ItemGroup> - <Compile Include="CancellationDisposable.cs" /> - <Compile Include="DefaultDisposable.cs" /> - <Compile Include="ExcelTaskUtil.cs" /> - <Compile Include="Functions.cs" /> + <Compile Include="src\ExcelDNA\AddIn.cs" /> + <Compile Include="src\Assistant\IProduct.cs" /> + <Compile Include="src\Assistant\Product.cs" /> + <Compile Include="src\ExcelDNA\Functions.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="SkuAssist.cs" /> + <Compile Include="src\Assistant\SkuAssist.cs" /> </ItemGroup> <ItemGroup> + <None Include="app.config" /> <None Include="Rehau.Sku.Assist-AddIn.dna" /> <None Include="packages.config" /> <None Include="Properties\ExcelDna.Build.props" /> diff --git a/SkuAssist.cs b/SkuAssist.cs deleted file mode 100644 index b873104..0000000 --- a/SkuAssist.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Net.Http; -using System.Threading.Tasks; -using AngleSharp; -using System.Linq; -using System.Net; - -namespace Rehau.Sku.Assist -{ - static class SkuAssist - { - static private HttpClient _httpClient; - - public static void EnsureHttpInitialized() - { - if (_httpClient == null) - { - _httpClient = new HttpClient(); - } - } - - public static string GetSku(string request) - { - string url = "https://shop-rehau.ru/catalogsearch/result/?q=" + request; - HttpResponseMessage response = GetResponse(url).Result; - var document = GetDocument(response).Result; - - var name = document - .All - .Where(e => e.ClassName == "product-item__desc-top") - .Select(e => new { sku = e.Children[0].TextContent, name = e.Children[1].TextContent.Trim(new[] { '\n', ' ' }) }) - .Where(t => !t.sku.Any(c => char.IsLetter(c))) - .FirstOrDefault(); - - return name == null ? "Не найдено" : $"{name.name} ({name.sku})"; - } - - private static async Task<HttpResponseMessage> GetResponse(string url) - { - ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; - HttpResponseMessage response = await _httpClient.GetAsync(url); - response.EnsureSuccessStatusCode(); - return response; - } - - private static async Task<AngleSharp.Dom.IDocument> GetDocument(HttpResponseMessage response) - { - IConfiguration config = Configuration.Default; - IBrowsingContext context = BrowsingContext.New(config); - - string source = await response.Content.ReadAsStringAsync(); - return await context.OpenAsync(req => req.Content(source)); - } - } -} - - diff --git a/app.config b/app.config new file mode 100644 index 0000000..3f5b7b7 --- /dev/null +++ b/app.config @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="System.Text.Encoding.CodePages" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup></configuration> diff --git a/packages.config b/packages.config index 07a90ac..cc383d8 100644 --- a/packages.config +++ b/packages.config @@ -1,9 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="AngleSharp" version="0.16.1" targetFramework="net48" /> - <package id="ExcelDna.AddIn" version="1.5.0" targetFramework="net48" /> - <package id="ExcelDna.Integration" version="1.5.0" targetFramework="net48" /> + <package id="ExcelDna.AddIn" version="1.5.0" targetFramework="net480" /> + <package id="ExcelDna.Integration" version="1.5.0" targetFramework="net480" /> + <package id="ExcelDna.Registration" version="1.5.0" targetFramework="net480" /> <package id="System.Buffers" version="4.5.1" targetFramework="net48" /> - <package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net48" /> - <package id="System.Text.Encoding.CodePages" version="5.0.0" targetFramework="net48" /> + <package id="System.Memory" version="4.5.4" targetFramework="net48" /> + <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" /> + <package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" /> + <package id="System.Text.Encoding.CodePages" version="6.0.0" targetFramework="net48" /> </packages>
\ No newline at end of file diff --git a/src/Assistant/IProduct.cs b/src/Assistant/IProduct.cs new file mode 100644 index 0000000..aca3ff5 --- /dev/null +++ b/src/Assistant/IProduct.cs @@ -0,0 +1,9 @@ +namespace Rehau.Sku.Assist +{ + interface IProduct + { + string Sku { get; } + string Name { get; } + string Uri { get; } + } +} diff --git a/src/Assistant/Product.cs b/src/Assistant/Product.cs new file mode 100644 index 0000000..17a7065 --- /dev/null +++ b/src/Assistant/Product.cs @@ -0,0 +1,21 @@ +namespace Rehau.Sku.Assist +{ + public class Product : IProduct + { + public string Sku { get; } + public string Name { get; } + + public string Uri => throw new System.NotImplementedException(); + + public Product(string sku, string name) + { + Sku = sku; + Name = name; + } + + public override string ToString() + { + return $"{this.Name} ({this.Sku})"; + } + } +}
\ No newline at end of file diff --git a/src/Assistant/SkuAssist.cs b/src/Assistant/SkuAssist.cs new file mode 100644 index 0000000..dc36dc0 --- /dev/null +++ b/src/Assistant/SkuAssist.cs @@ -0,0 +1,45 @@ +using AngleSharp; +using AngleSharp.Dom; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Rehau.Sku.Assist +{ + static class SkuAssist + { + public async static Task<string> GetContent(string request, HttpClient httpClient) + { + string uri = "https://shop-rehau.ru/catalogsearch/result/?q=" + request._CleanRequest(); + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; + + return await httpClient.GetStringAsync(uri); + } + + public async static Task<IDocument> GetDocument(Task<string> source) + { + IConfiguration config = Configuration.Default; + IBrowsingContext context = BrowsingContext.New(config); + + return await context.OpenAsync(req => req.Content(source.Result)); + } + + public static IProduct GetProductFromDocument(IDocument document) + { + return document + .All + .Where(e => e.ClassName == "product-item__desc-top") + .Select(e => new Product(e.Children[0].TextContent, e.Children[1].TextContent.Trim(new[] { '\n', ' ' }))) + .FirstOrDefault(); + } + + private static string _CleanRequest(this string input) + { + return input.Replace("+", " plus "); + } + } +} + + diff --git a/src/ExcelDNA/AddIn.cs b/src/ExcelDNA/AddIn.cs new file mode 100644 index 0000000..dd99667 --- /dev/null +++ b/src/ExcelDNA/AddIn.cs @@ -0,0 +1,24 @@ +using ExcelDna.Integration; +using ExcelDna.Registration; + +namespace Rehau.Sku.Assist +{ + public class AddIn : IExcelAddIn + { + public void AutoOpen() + { + RegisterFunctions(); + } + + public void AutoClose() + { + } + + void RegisterFunctions() + { + ExcelRegistration.GetExcelFunctions() + .ProcessAsyncRegistrations(nativeAsyncIfAvailable: false) + .RegisterFunctions(); + } + } +} diff --git a/src/ExcelDNA/Functions.cs b/src/ExcelDNA/Functions.cs new file mode 100644 index 0000000..ec9c607 --- /dev/null +++ b/src/ExcelDNA/Functions.cs @@ -0,0 +1,21 @@ +using AngleSharp.Dom; +using ExcelDna.Integration; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Rehau.Sku.Assist +{ + public class Functions + { + private static HttpClient _httpClient = new HttpClient(); + + [ExcelFunction] + public static async Task<string> RAUNAME(string request) + { + Task<string> contentTask = Task.Run(() => SkuAssist.GetContent(request, _httpClient)); + Task<IDocument> documentTask = await contentTask.ContinueWith(content => SkuAssist.GetDocument(content)); + IProduct product = await documentTask.ContinueWith(doc => SkuAssist.GetProductFromDocument(doc.Result)); + return product == null ? ExcelError.ExcelErrorNull.ToString() : product.ToString(); + } + } +}
\ No newline at end of file |