Merge pull request 'New features 2026' (#1) from new_features_2026 into main

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2026-09-03 14:28:06 +00:00
41 changed files with 534 additions and 776 deletions
+5 -5
View File
@@ -2,8 +2,8 @@
This repository contains the source code of the concordancer which is a part of the [Terminology Portal](https://github.com/clarinsi/rsdo_term_portal).
There are two main parts:
- Web application with backend (API interface) written in ASP.NET Core 6 and frontend written in React
- System Manager which is written in .NET 6 and is used to initially set up the system
- Web application with backend (API interface) written in ASP.NET Core 10 and frontend written in React
- System Manager which is written in .NET 10 and is used to initially set up the system
## Prerequisites
Web application requires following external dependencies
@@ -16,7 +16,7 @@ To build a frontend application successfully, first, [Node.js](https://nodejs.or
Use `npm install` to install all required dependencies, and `npm run build` to build the application.
For more info see the [Readme](src/apps/Rsdo.Concordancer.Api/WebApp/README.md) inside the frontend [root](src/apps/Rsdo.Concordancer.Api/WebApp) folder.
### Backend (ASP.NET Core 6) and System Manager (.NET 6)
### Backend (ASP.NET Core 10) and System Manager (.NET 10)
Open [Rsdo.Concordancer.sln](src/Rsdo.Concordancer.sln) inside of your IDE and build the solution, or use `dotnet build` command to build the solution. All required dependencies will be installed via NuGet packages.
## Creating docker images
@@ -38,13 +38,13 @@ Use following [Dockerfile](src/apps/Rsdo.Concordancer.SystemManager/Dockerfile)
### Web application
Web application must be configured with the following Environment variables and values:
- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer`
- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `Host=rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer`
- `RSDO:Elastic:ConnectionString` holds connection string to OpenSearch server; e.g.: `http://rsdo_opensearch:9200`
- `RSDO:Tokenizer:ClasslaUrl` holds and url to external service which is used for tokenizing search strings; e.g.: `https://orodja.cjvt.si/oznacevalnik/ajax_api/v1/slv/process`
### System Manager
System manager must be configured with the following Environment variables and values:
- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer`
- `RSDO:Database:MasterConnectionString` holds connection string to the main database; e.g.: `Host=rsdo_db;Username=rsdo;Password=strongpassword;Database=Concordancer`
- `RSDO:Elastic:ConnectionString` holds connection string to OpenSearch server; e.g.: `http://rsdo_opensearch:9200`
## Using System Manager
Binary file not shown.
+62 -18
View File
@@ -1,7 +1,7 @@
## 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
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
@@ -9,6 +9,7 @@
*.user
*.userosscache
*.sln.docstates
*.env
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
@@ -21,17 +22,37 @@ mono_crash.*
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Build results on 'Bin' directories
**/[Bb]in/*
# Uncomment if you have tasks that rely on *.refresh files to move binaries
# (https://github.com/github/gitignore/pull/3736)
#!**/[Bb]in/*.refresh
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
@@ -43,12 +64,16 @@ Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.trx
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Approval Tests result files
*.received.*
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
@@ -61,6 +86,7 @@ BenchmarkDotNet.Artifacts/
project.lock.json
project.fragment.lock.json
artifacts/
.artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
@@ -75,6 +101,7 @@ StyleCopReport.xml
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
@@ -82,6 +109,8 @@ StyleCopReport.xml
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
@@ -153,6 +182,7 @@ coverage*.info
# NCrunch
_NCrunch_*
.NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
@@ -206,9 +236,6 @@ PublishScripts/
*.nuget.props
*.nuget.targets
# Nuget personal access tokens and Credentials
nuget.config
# Microsoft Azure Build Output
csx/
*.build.csdef
@@ -297,6 +324,14 @@ node_modules/
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -306,22 +341,22 @@ node_modules/
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
**/.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
**/.fake/
# CodeRush personal settings
.cr/personal
**/.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
**/__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
#tools/**
#!tools/packages.config
# Tabs Studio
*.tss
@@ -343,15 +378,22 @@ ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
MSBuild_Logs/
# AWS SAM Build and Temporary Artifacts folder
.aws-sam
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
**/.mfractor/
# Local History for Visual Studio
.localhistory/
**/.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
@@ -360,7 +402,7 @@ healthchecksdb
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
**/.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
@@ -371,11 +413,14 @@ FodyWeavers.xsd
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Windows Installer files from build outputs
*.cab
*.msi
@@ -385,4 +430,3 @@ FodyWeavers.xsd
# JetBrains Rider
.idea/
*.sln.iml
+11
View File
@@ -0,0 +1,11 @@
<Project>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>10-recommended</AnalysisLevel>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<Version>1.0.4</Version>
</PropertyGroup>
</Project>
+42
View File
@@ -0,0 +1,42 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Autofac" Version="9.1.0" />
<PackageVersion Include="Autofac.Extensions.DependencyInjection" Version="11.0.0" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="EFCore.NamingConventions" Version="10.0.1" />
<PackageVersion Include="FluentMigrator" Version="8.0.1" />
<PackageVersion Include="FluentMigrator.Runner" Version="8.0.1" />
<PackageVersion Include="FluentMigrator.Runner.Postgres" Version="8.0.1" />
<PackageVersion Include="FluentValidation" Version="12.1.1" />
<PackageVersion Include="Hangfire" Version="1.8.23" />
<PackageVersion Include="Hangfire.Core" Version="1.8.23" />
<PackageVersion Include="Hangfire.PostgreSql" Version="1.21.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="10.0.8" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.8" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="10.0.2" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Npgsql" Version="10.0.3" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
<PackageVersion Include="OpenSearch.Client" Version="1.8.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Settings.Configuration" Version="10.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.Email" Version="4.2.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="10.2.1" />
<PackageVersion Include="Swashbuckle.AspNetCore.Annotations" Version="10.2.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
</Project>
+7
View File
@@ -24,6 +24,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rsdo.Concordancer.Data", "c
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rsdo.Concordancer.SystemManager", "apps\Rsdo.Concordancer.SystemManager\Rsdo.Concordancer.SystemManager.csproj", "{41B26AD1-C0A5-4796-95D0-CB84FB574A83}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BC9D8CB-3F46-40F4-B769-E2605F03E0F6}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
.gitignore = .gitignore
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
-357
View File
@@ -1,357 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Browsers/Browsers/@EntryValue">C65+,E16+,FF58+,IE11+</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">VISIBLE_FILES</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeThisQualifier/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CSharpWarnings_003A_003ACS1591/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=Reformat_0020Code_0020IDE/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="Reformat Code IDE"&gt;&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;&lt;CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="False" /&gt;&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;CSFixBuiltinTypeReferences&gt;True&lt;/CSFixBuiltinTypeReferences&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;XAMLCollapseEmptyTags&gt;False&lt;/XAMLCollapseEmptyTags&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=StyleCop/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="StyleCop"&gt;&lt;CSUpdateFileHeader&gt;False&lt;/CSUpdateFileHeader&gt;&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/ThisQualifier/INSTANCE_MEMBERS_QUALIFY_MEMBERS/@EntryValue">None</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&#xD;
&lt;Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"&gt;&#xD;
&lt;TypePattern DisplayName="Non-reorderable types"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Interface" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" /&gt;&#xD;
&lt;HasAttribute Name="System.Runtime.InteropServices.ComImport" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;Kind Is="Struct" /&gt;&#xD;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" /&gt;&#xD;
&lt;HasAttribute Name="JetBrains.Annotations.NoReorder" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;HasMember&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.FactAttribute" Inherited="True" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/HasMember&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;ImplementsInterface Name="System.IDisposable" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Kind Order="Constructor" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry DisplayName="Test Methods" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="Xunit.FactAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All"&gt;&#xD;
&lt;TypePattern.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/TypePattern.Match&gt;&#xD;
&lt;Entry DisplayName="Setup/Teardown Methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;Entry DisplayName="Test Methods" Priority="100"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;HasAttribute Name="NUnit.Framework.TestAttribute" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;TypePattern DisplayName="Default Pattern (StyleCop)" RemoveRegions="All"&gt;&#xD;
&lt;Entry DisplayName="Constants"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Constant" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Static fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Fields"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Field" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Readonly /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Constructors and Destructors" Priority="200"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Constructor" /&gt;&#xD;
&lt;Kind Is="Destructor" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Kind Order="Constructor Destructor" /&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Delegates"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Delegate" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other events"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Event" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Enums"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Enum" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interfaces"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Interface" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public properties"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface properties"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other properties"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Property" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public indexers" Priority="1000"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface indexers" Priority="1000"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other indexers" Priority="1000"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Indexer" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Public methods and operators"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Or&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;Kind Is="Operator" /&gt;&#xD;
&lt;/Or&gt;&#xD;
&lt;Access Is="Public" /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Interface methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;And&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;ImplementsInterface /&gt;&#xD;
&lt;/And&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;ImplementsInterface Immediate="True" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Other methods"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Method" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Operators"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Operator" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Nested structs" Priority="600"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Struct" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="Nested classes" Priority="700"&gt;&#xD;
&lt;Entry.Match&gt;&#xD;
&lt;Kind Is="Class" /&gt;&#xD;
&lt;/Entry.Match&gt;&#xD;
&lt;Entry.SortBy&gt;&#xD;
&lt;Static /&gt;&#xD;
&lt;Access Order="Public Internal ProtectedInternal Protected Private" /&gt;&#xD;
&lt;Name /&gt;&#xD;
&lt;/Entry.SortBy&gt;&#xD;
&lt;/Entry&gt;&#xD;
&lt;Entry DisplayName="All other members" /&gt;&#xD;
&lt;/TypePattern&gt;&#xD;
&lt;/Patterns&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpUsing/KeepImports/=System/@EntryIndexedValue">System</s:String>
<s:String x:Key="/Default/CodeStyle/CSharpUsing/KeepImports/=System_002ELinq/@EntryIndexedValue">System.Linq</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MB/@EntryIndexedValue">MB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dtos/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -1,5 +1,5 @@
using System;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi;
namespace Rsdo.Concordancer.Api.Controllers;
+7 -10
View File
@@ -1,8 +1,12 @@
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# COPY SHARED MSBUILD CONFIGURATION
COPY src/Directory.Build.props /src/Directory.Build.props
COPY src/Directory.Packages.props /src/Directory.Packages.props
# INSTALL NODE.JS
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \
@@ -10,13 +14,6 @@ RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - \
nodejs \
&& rm -rf /var/lib/apt/lists/*
# COPY LOCAL NUGET PACKAGES
WORKDIR /nuget
COPY local-packages/. .
# ADD LOCAL NUGET SOURCE
RUN dotnet nuget add source /nuget
# RESTORE PACKAGES (with project files only)
WORKDIR /src/apps
COPY src/apps/Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj Rsdo.Concordancer.Api/Rsdo.Concordancer.Api.csproj
@@ -57,4 +54,4 @@ RUN dotnet publish "Rsdo.Concordancer.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Rsdo.Concordancer.Api.dll"]
ENTRYPOINT ["dotnet", "Rsdo.Concordancer.Api.dll"]
@@ -1,9 +1,9 @@
using System;
using System.Net;
using MailKit.Security;
using Serilog;
using Serilog.Configuration;
using Serilog.Events;
using Serilog.Sinks.Email;
namespace Rsdo.Concordancer.Api.Framework;
@@ -23,25 +23,25 @@ public static class SerilogExtensions
int batchPostingLimit = 100,
string mailSubject = "Log Email")
{
var connectionInfo = new EmailConnectionInfo()
{
FromEmail = fromEmail,
ToEmail = toEmail,
EmailSubject = mailSubject,
MailServer = mailServer,
Port = port,
EnableSsl = enableSsl,
};
NetworkCredential credentials = null;
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
connectionInfo.NetworkCredentials = new NetworkCredential()
credentials = new NetworkCredential()
{
UserName = userName,
Password = password,
};
}
return sinkConfiguration.Email(connectionInfo: connectionInfo, restrictedToMinimumLevel: restrictedToMinimumLevel);
return sinkConfiguration.Email(
from: fromEmail,
to: toEmail,
host: mailServer,
port: port,
connectionSecurity: enableSsl ? SecureSocketOptions.Auto : SecureSocketOptions.None,
credentials: credentials,
subject: mailSubject,
body: outputTemplate,
restrictedToMinimumLevel: restrictedToMinimumLevel);
}
}
+136 -42
View File
@@ -1,52 +1,146 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.Json.Serialization;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Hangfire;
using Hangfire.PostgreSql;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi;
using Rsdo.Concordancer.Api.Controllers;
using Rsdo.Concordancer.Api.Framework;
using Rsdo.Concordancer.Core.Constants;
using Rsdo.Concordancer.Data.CompositionRoot;
using Rsdo.Concordancer.Infrastructure.CompositionRoot;
using Rsdo.Concordancer.ServiceModel.Shared;
using Rsdo.Concordancer.Services.CompositionRoot;
using Rsdo.Concordancer.Services.Framework.Cache;
using Serilog;
namespace Rsdo.Concordancer.Api;
public class Program
{
public static int Main(string[] args)
var builder = WebApplication.CreateBuilder(
new WebApplicationOptions()
{
// Read configuration file
var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
.Build();
Args = args,
WebRootPath = "./WebApp/build",
});
// Create logger
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(configuration).CreateLogger();
builder.Host.UseSerilog(
(context, _, loggerConfiguration) =>
{
loggerConfiguration.ReadFrom.Configuration(context.Configuration);
});
// Run application
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception e)
{
Log.Fatal(e, "Host terminated unexpectedly");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(
webBuilder =>
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(
containerBuilder =>
{
containerBuilder.RegisterModule(new ServicesModule());
containerBuilder.RegisterModule(new InfrastructureModule());
containerBuilder.RegisterModule(new DataModule());
containerBuilder.RegisterBuildCallback(
(c) =>
{
var warmUps = c.Resolve<IEnumerable<ICacheWarmUp>>();
foreach (var warmUp in warmUps)
{
webBuilder.UseStartup<Startup>();
webBuilder.UseWebRoot("./WebApp/build");
});
}
warmUp.WarmUp();
}
});
});
builder.Services.AddCors(
opt =>
{
opt.AddPolicy(
"CorsPolicy",
policy =>
{
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
});
});
builder.Services.AddControllers()
.AddJsonOptions(
opts =>
{
// Bind strings to enums
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
builder.Services.AddSwaggerGen(
c =>
{
c.SwaggerDoc(
ServiceApiInfo.ApiGroupConcordancer,
info: new OpenApiInfo()
{
Title = $"{ServiceApiInfo.ServiceName} Concordancer API",
Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}",
});
c.SwaggerDoc(
ServiceApiInfo.ApiGroupDashboard,
info: new OpenApiInfo()
{
Title = $"{ServiceApiInfo.ServiceName} Dashboard API",
Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}",
});
// enable attribute annotations
c.EnableAnnotations();
// include code documentation to the swagger doc
foreach (var assembly in new[] { Assembly.GetExecutingAssembly(), typeof(ExecutionResult).Assembly })
{
var xmlFile = $"{assembly.GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
}
});
builder.Services.AddHangfire(
x =>
{
// It would be better to use ConnectionStringProvider, but in this case
// we would have to build the container which would (at this point) double singletons.
// So we are duplicated code from ConnectionStringProvider
var connectionString = builder.Configuration[ConfigurationKey.Database.MasterConnectionString];
x.UsePostgreSqlStorage(
options =>
{
options.UseNpgsqlConnection(connectionString);
});
x.UseMediator();
});
builder.Services.AddHangfireServer();
builder.Services.AddHttpClient();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(
c =>
{
c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupConcordancer}/swagger.json", $"{ServiceApiInfo.ServiceName} Concordancer API");
c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupDashboard}/swagger.json", $"{ServiceApiInfo.ServiceName} Dashboard API");
});
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthorization();
app.MapControllers();
app.MapHangfireDashboard("/hangfire");
app.MapFallbackToFile("index.html");
app.Run();
@@ -1,31 +1,25 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:9184",
"sslPort": 44312
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger/index.html",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger/index.html",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Rsdo.Concordancer.Api": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
}
@@ -1,27 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Hangfire" Version="1.7.31" />
<PackageReference Include="Hangfire.PostgreSql" Version="1.9.9" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.10" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
<PackageReference Include="Serilog.Expressions" Version="3.4.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
<PackageReference Include="Autofac" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" />
<PackageReference Include="Hangfire" />
<PackageReference Include="Hangfire.PostgreSql" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" />
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Expressions" />
<PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Serilog.Sinks.Email" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Swashbuckle.AspNetCore" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
</ItemGroup>
<ItemGroup>
-150
View File
@@ -1,150 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;
using Autofac;
using Hangfire;
using Hangfire.PostgreSql;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Rsdo.Concordancer.Api.Controllers;
using Rsdo.Concordancer.Api.Framework;
using Rsdo.Concordancer.Core.Constants;
using Rsdo.Concordancer.Data.CompositionRoot;
using Rsdo.Concordancer.Infrastructure.CompositionRoot;
using Rsdo.Concordancer.ServiceModel.Shared;
using Rsdo.Concordancer.Services.CompositionRoot;
using Rsdo.Concordancer.Services.Framework.Cache;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Rsdo.Concordancer.Api;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(
opt =>
{
opt.AddPolicy(
"CorsPolicy",
policy =>
{
policy.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
});
});
services.AddControllers()
.AddJsonOptions(
opts =>
{
// Bind strings to enums
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
services.AddSwaggerGen(
c =>
{
c.SwaggerDoc(
ServiceApiInfo.ApiGroupConcordancer,
info: new OpenApiInfo()
{
Title = $"{ServiceApiInfo.ServiceName} Concordancer API",
Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}",
});
c.SwaggerDoc(
ServiceApiInfo.ApiGroupDashboard,
info: new OpenApiInfo()
{
Title = $"{ServiceApiInfo.ServiceName} Dashboard API",
Version = $"v{ServiceApiInfo.ServiceVersion.ToString(3)}",
});
// enable attribute annotations
c.EnableAnnotations();
// include code documentation to the swagger doc
ConfigureSwaggerApiDoc(c, Assembly.GetExecutingAssembly(), typeof(ExecutionResult).Assembly);
});
services.AddHangfire(
x =>
{
// It would be better to use ConnectionStringProvider, but in this case
// we would have to build the container which would (at this point) double singletons.
// So we are duplicated code from ConnectionStringProvider
var connectionString = Configuration[ConfigurationKey.Database.MasterConnectionString];
x.UsePostgreSqlStorage(connectionString);
x.UseMediator();
});
services.AddHangfireServer();
services.AddHttpClient();
}
public void ConfigureSwaggerApiDoc(SwaggerGenOptions options, params Assembly[] assemblies)
{
foreach (var assembly in assemblies)
{
var xmlFile = $"{assembly.GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
}
}
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new ServicesModule());
builder.RegisterModule(new InfrastructureModule());
builder.RegisterModule(new DataModule());
builder.RegisterBuildCallback(
(c) =>
{
var warmUps = c.Resolve<IEnumerable<ICacheWarmUp>>();
foreach (var warmUp in warmUps)
{
warmUp.WarmUp();
}
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(
c =>
{
c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupConcordancer}/swagger.json", $"{ServiceApiInfo.ServiceName} Concordancer API");
c.SwaggerEndpoint($"{ServiceApiInfo.ApiGroupDashboard}/swagger.json", $"{ServiceApiInfo.ServiceName} Dashboard API");
});
app.UseStaticFiles();
app.UseRouting();
app.UseCors("CorsPolicy");
app.UseAuthorization();
app.UseEndpoints(
endpoints =>
{
endpoints.MapControllers();
endpoints.MapHangfireDashboard("/hangfire");
endpoints.MapFallbackToFile("index.html");
});
}
}
@@ -0,0 +1,3 @@
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.8297 5.28995L2.5897 1.04995C2.49674 0.95622 2.38613 0.881826 2.26428 0.831057C2.14242 0.780288 2.01171 0.75415 1.8797 0.75415C1.74769 0.75415 1.61698 0.780288 1.49512 0.831057C1.37326 0.881826 1.26266 0.95622 1.1697 1.04995C0.983448 1.23731 0.878906 1.49076 0.878906 1.75495C0.878906 2.01913 0.983448 2.27259 1.1697 2.45995L4.7097 5.99995L1.1697 9.53995C0.983448 9.72731 0.878906 9.98076 0.878906 10.2449C0.878906 10.5091 0.983448 10.7626 1.1697 10.9499C1.26314 11.0426 1.37395 11.116 1.49579 11.1657C1.61763 11.2155 1.74809 11.2407 1.8797 11.2399C2.01131 11.2407 2.14177 11.2155 2.26361 11.1657C2.38544 11.116 2.49626 11.0426 2.5897 10.9499L6.8297 6.70995C6.92343 6.61699 6.99782 6.50638 7.04859 6.38453C7.09936 6.26267 7.1255 6.13196 7.1255 5.99995C7.1255 5.86794 7.09936 5.73723 7.04859 5.61537C6.99782 5.49351 6.92343 5.38291 6.8297 5.28995Z" fill="#848C91"/>
</svg>

After

Width:  |  Height:  |  Size: 977 B

@@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 3.75C1.5 2.50736 2.50736 1.5 3.75 1.5H14.25C15.4926 1.5 16.5 2.50736 16.5 3.75V14.25C16.5 15.4926 15.4926 16.5 14.25 16.5H3.75C2.50736 16.5 1.5 15.4926 1.5 14.25V3.75Z" fill="#006CB7"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.6857 6.21973C12.9786 6.51262 12.9786 6.98749 12.6857 7.28039L8.03033 12.1554C7.73744 12.4483 7.26256 12.4483 6.96967 12.1554L4.71967 9.90539C4.42678 9.61249 4.42678 9.13762 4.71967 8.84473C5.01256 8.55183 5.48744 8.55183 5.78033 8.84473L7.5 10.5644L11.625 6.21973C11.9179 5.92683 12.3928 5.92683 12.6857 6.21973Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 725 B

@@ -1,15 +1,27 @@
import { Link } from 'react-router-dom';
import useSearch from '../../hooks/use-search';
import styles from './ConcordanceAggregationItem.module.scss';
import { useTranslation } from 'react-i18next';
import selectIcon from '../../assets/chevron-right.svg';
import selectedIcon from '../../assets/square-checkbox-solid.svg';
const ConcordanceAggregationItem = (props) => {
const { t } = useTranslation();
const search = useSearch();
const isSelected = search.isFiltered('TextIds', props.filterKey) === true;
const link = isSelected ? search.getClearFilterLink('TextIds', props.filterKey) : search.getFilterLink('TextIds', props.filterKey);
let icon;
if (isSelected) {
icon = <img src={selectedIcon} alt={t('shared.unselect')} />;
} else {
icon = <img src={selectIcon} alt={t('shared.select')} />;
}
return (
<li className={styles.aggregationItem}><Link to={link}>{isSelected && <span className={styles.remove} >X</span>}<span className={styles.title}>{props.title}</span><span className={styles.count}>{props.count}</span></Link></li>
<li className={styles.aggregationItem}><Link to={link}>{icon}<span className={styles.title}>{props.title}</span><span className={styles.count}>{props.count}</span></Link></li>
);
};
export default ConcordanceAggregationItem;
export default ConcordanceAggregationItem;
@@ -9,16 +9,13 @@
color: $text-color;
display: flex;
text-decoration: none;
.remove {
margin-right: 0.8rem;
}
.title {
margin-left: 1.2rem;
flex-grow: 1;
margin-right: 0.8rem;
overflow: hidden;
text-overflow: clip;
}
}
}
}
@@ -18,7 +18,7 @@ const IndexTopBar = () => {
<div className={styles.menu}>
<div className='container-fluid'>
<div className='row align-items-center'>
<div className='col'><img src={logo} alt='Logo' /></div>
<div className='col'><a href="https://www.slovenscina.eu"><img src={logo} alt='Logo' /></a></div>
<div className={`col ${styles.links}`}><button type='button' onClick={languageClickHandler}><img src={langIcon} alt={changeLanguageTitle} />{changeLanguageCode}</button></div>
</div>
</div>
@@ -26,4 +26,4 @@ const IndexTopBar = () => {
);
};
export default IndexTopBar;
export default IndexTopBar;
@@ -22,7 +22,9 @@ const resources = {
'searchError': 'Server returned error when searching.',
'searchHelp': 'Help',
'searchHistory': 'Search history',
'searchRecords': 'Showing {{start}}-{{end}} from {{total}} records.'
'searchRecords': 'Showing {{start}}-{{end}} from {{total}} records.',
'select': 'Select',
'unselect': 'Remove selected'
},
concordance: {
'title': 'Search concordances',
@@ -63,7 +65,9 @@ const resources = {
'searchError': 'Strežnik je vrnil napako pri iskanju.',
'searchHelp': 'Pomoč',
'searchHistory': 'Zgodovina iskanj',
'searchRecords': 'Prikazujem {{start}}-{{end}} od {{total}} zapisov.'
'searchRecords': 'Prikazujem {{start}}-{{end}} od {{total}} zapisov.',
'select': 'Izberi',
'unselect': 'Odstrani izbrano'
},
concordance: {
'title': 'Iskanje po konkordancah',
@@ -96,4 +100,4 @@ i18n
}
});
export default i18n;
export default i18n;
@@ -49,7 +49,7 @@ const ResultsLayout = (props) => {
<div className='container-fluid'>
<div className='row align-items-center'>
<div className='col-sm-4 col-lg-2 col-xl-2'>
<Link to={search.rootUrl}><img src={logo} alt='Logo' /></Link>
<a href="https://www.slovenscina.eu"><img src={logo} alt='Logo' /></a>
<ul className={styles.areaButtons}>
<li className={`${styles.areaButton} ${isConcordance ? styles.active : null}`}><Link to={`/${search.corpusId}/concordance`}><img src={isConcordance ? concordanceActiveIcon : concordanceInactiveIcon} alt='Concordance' /></Link></li>
<li className={`${styles.areaButton} ${!isConcordance ? styles.active : null}`}><Link to={`/${search.corpusId}/list`}><img src={!isConcordance ? listActiveIcon : listInactiveIcon} alt='List' /></Link></li>
@@ -73,7 +73,7 @@ const ConcordanceResults = () => {
content = <p>{t('shared.noResults')}</p>
}
return (
<ResultsLayout source='concordance'>
<ResultsLayout source='concordance' help={<ConcordanceHelp />}>
<div className='row'>
<div className='col-lg-10 offset-lg-2'>
{content}
@@ -11,6 +11,7 @@ import ResultsHeader from '../../components/results/ResultsHeader';
import ResultsPager from '../../components/results/ResultsPager';
import ResultsExport from '../../components/results/ResultsExport';
import ListItems from '../../components/list/ListItems';
import ListHelp from '../../components/list/ListHelp';
import Spinner from '../../components/shared/Spinner';
const ListResults = () => {
@@ -48,7 +49,7 @@ const ListResults = () => {
if (!isLoading && isSuccess && data.items.length > 0) {
return (
<ResultsLayout source='list'>
<ResultsLayout source='list' help={<ListHelp />}>
<div className='row'>
<div className='col'>
<ResultsHeader query={body.query} onExport={() => setIsVisible(prevIsVisible => !prevIsVisible)} />
@@ -69,7 +70,7 @@ const ListResults = () => {
content = <p>{t('shared.noResults')}</p>
}
return (
<ResultsLayout source='list'>
<ResultsLayout source='list' help={<ListHelp />}>
<div className='row'>
<div className='col-lg-10 offset-lg-2'>
{content}
@@ -1,14 +1,11 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS base
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
# COPY LOCAL NUGET PACKAGES
WORKDIR /nuget
COPY local-packages/. .
# ADD LOCAL NUGET SOURCE
RUN dotnet nuget add source /nuget
# COPY SHARED MSBUILD CONFIGURATION
COPY src/Directory.Build.props /src/Directory.Build.props
COPY src/Directory.Packages.props /src/Directory.Packages.props
# RESTORE PACKAGES (with project files only)
WORKDIR /src/apps
@@ -44,4 +41,4 @@ RUN dotnet publish "Rsdo.Concordancer.SystemManager.csproj" -c Release -o /app/p
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
CMD tail -f /dev/null
CMD tail -f /dev/null
@@ -2,16 +2,11 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="Autofac" />
<PackageReference Include="CommandLineParser" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,9 @@
namespace Rsdo.Concordancer.Core.Extensions;
public static class StringExtensions
{
public static bool IsWildcardSearch(this string value)
{
return !string.IsNullOrEmpty(value) && (value.Contains('*') || value.Contains('?'));
}
}
@@ -1,16 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Rsdo.Concordancer.ServiceModel\Rsdo.Concordancer.ServiceModel.csproj" />
</ItemGroup>
@@ -14,6 +14,8 @@ public class VersionInfoTableMetadata : IVersionTableMetaData
public string UniqueIndexName => "uc_version";
public bool CreateWithPrimaryKey => false;
public object ApplicationContext { get; set; }
public bool OwnsSchema => true;
@@ -1,19 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="FluentMigrator" Version="3.3.2" />
<PackageReference Include="FluentMigrator.Runner" Version="3.3.2" />
<PackageReference Include="FluentMigrator.Runner.Postgres" Version="3.3.2" />
<PackageReference Include="Npgsql" Version="6.0.7" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="Autofac" />
<PackageReference Include="FluentMigrator" />
<PackageReference Include="FluentMigrator.Runner" />
<PackageReference Include="FluentMigrator.Runner.Postgres" />
<PackageReference Include="Npgsql" />
</ItemGroup>
<ItemGroup>
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenSearch.Client;
using Rsdo.Concordancer.Core.Extensions;
namespace Rsdo.Concordancer.Infrastructure.Extensions;
@@ -30,4 +32,36 @@ public static class ElasticQueryExtensions
},
};
}
public static QueryContainer ToQuery(this string value, string field)
{
if (value.IsWildcardSearch())
{
return new WildcardQuery()
{
Field = field,
Value = value,
};
}
return new TermQuery()
{
Field = field,
Value = value,
};
}
public static QueryContainer ToQuery(this List<string> values, string field)
{
if (values.Any(v => v.IsWildcardSearch()))
{
return values.Select(value => value.ToQuery(field)).ToList().ToBooleanOrQuery();
}
return new TermsQuery()
{
Field = field,
Terms = values,
};
}
}
@@ -1,18 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
<PackageReference Include="OpenSearch.Client" Version="1.1.0" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="Autofac" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="OpenSearch.Client" />
</ItemGroup>
<ItemGroup>
@@ -35,22 +35,12 @@ public class TermListQueryBuilder : IQueryBuilder<TermListQuery>
if (!string.IsNullOrEmpty(wordQuery.Form))
{
queries.Add(
new TermQuery()
{
Field = $"{tokenField}.formLower",
Value = wordQuery.Form.ToLower(),
});
queries.Add(wordQuery.Form.ToLower().ToQuery($"{tokenField}.formLower"));
}
if (!wordQuery.Lemmas.IsNullOrEmpty())
{
queries.Add(
new TermsQuery()
{
Field = $"{tokenField}.lemma",
Terms = wordQuery.Lemmas,
});
queries.Add(wordQuery.Lemmas.ToQuery($"{tokenField}.lemma"));
}
return queries.Count == 0 ? new MatchNoneQuery() : queries.ToBooleanAndQuery();
@@ -1,15 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" />
</ItemGroup>
</Project>
@@ -1,10 +1,14 @@
using System;
using System.Reflection;
using Autofac;
using Autofac.Core;
using FluentValidation;
using Microsoft.Extensions.Caching.Memory;
using Rsdo.Concordancer.Core.Interfaces;
using Rsdo.Concordancer.Core.Search.Queries.Concordances;
using Rsdo.Concordancer.Core.Search.Queries.TermLists;
using Rsdo.Concordancer.ServiceModel.Requests.Concordances;
using Rsdo.Concordancer.ServiceModel.Requests.TermLists;
using Rsdo.Concordancer.ServiceModel.Types;
using Rsdo.Concordancer.Services.Framework;
using Rsdo.Concordancer.Services.Framework.BulkLoaders;
@@ -15,6 +19,8 @@ using Rsdo.Concordancer.Services.Framework.Decorators;
using Rsdo.Concordancer.Services.Search.Aggregations;
using Rsdo.Concordancer.Services.Search.AlternateSearches;
using Rsdo.Concordancer.Services.Search.QueryFactories;
using Rsdo.Concordancer.Services.Search.QueryFactories.Concordances;
using Rsdo.Concordancer.Services.Search.QueryFactories.TermLists;
using Rsdo.Concordancer.Services.Services.InputQueryParser;
using Rsdo.Concordancer.Services.Services.LemmatizationService;
using Rsdo.Concordancer.Services.Services.ParagraphService;
@@ -83,7 +89,8 @@ public class ServicesModule : Module
builder.RegisterType<DefaultTokenizerService>().Keyed<ITokenizerService>(TokenizerType.Default).SingleInstance();
builder.RegisterType<InputQueryParser>().As<IInputQueryParser>().SingleInstance();
builder.RegisterType<ParagraphService>().As<IParagraphService>().InstancePerLifetimeScope();
builder.RegisterType<LemmatizationService>().As<ILemmatizationService>().InstancePerLifetimeScope();
builder.RegisterType<SimpleLemmatizationService>().Keyed<ILemmatizationService>(LemmatizationType.Simple).InstancePerLifetimeScope();
builder.RegisterType<WildcardLemmatizationService>().Keyed<ILemmatizationService>(LemmatizationType.Wildcard).InstancePerLifetimeScope();
builder.RegisterType<PartOfSpeechService>().As<IPartOfSpeechService>().InstancePerLifetimeScope();
}
@@ -103,7 +110,33 @@ public class ServicesModule : Module
private void RegisterSearch(ContainerBuilder builder)
{
// Query factories
builder.RegisterAssemblyTypes(ServicesAssembly).AsClosedTypesOf(typeof(IQueryFactory<,>)).AsImplementedInterfaces().InstancePerLifetimeScope();
var wildcardLemmatizationServiceParameter = new ResolvedParameter(
(p, ctx) => p.ParameterType == typeof(ILemmatizationService),
(p, ctx) => ctx.ResolveKeyed<ILemmatizationService>(LemmatizationType.Wildcard));
var simpleLemmatizationServiceParameter = new ResolvedParameter(
(p, ctx) => p.ParameterType == typeof(ILemmatizationService),
(p, ctx) => ctx.ResolveKeyed<ILemmatizationService>(LemmatizationType.Simple));
builder.RegisterType<ExportConcordancesQueryFactory>()
.As<IQueryFactory<ExportConcordances, ConcordancesQuery>>()
.WithParameter(simpleLemmatizationServiceParameter)
.InstancePerLifetimeScope();
builder.RegisterType<SearchConcordancesQueryFactory>()
.As<IQueryFactory<SearchConcordances, ConcordancesQuery>>()
.WithParameter(simpleLemmatizationServiceParameter)
.InstancePerLifetimeScope();
builder.RegisterType<ConcordanceDetailsQueryFactory>()
.As<IQueryFactory<ConcordanceDetails, ConcordancesQuery>>()
.WithParameter(simpleLemmatizationServiceParameter)
.InstancePerLifetimeScope();
builder.RegisterType<ExportTermListQueryFactory>()
.As<IQueryFactory<ExportTermList, TermListQuery>>()
.WithParameter(wildcardLemmatizationServiceParameter)
.InstancePerLifetimeScope();
builder.RegisterType<SearchTermListQueryFactory>()
.As<IQueryFactory<SearchTermList, TermListQuery>>()
.WithParameter(wildcardLemmatizationServiceParameter)
.InstancePerLifetimeScope();
// Aggregations
builder.RegisterType<AggregationProviderFactory>().As<IAggregationProviderFactory>().InstancePerLifetimeScope();
@@ -112,6 +145,7 @@ public class ServicesModule : Module
// Alternate searches
builder.RegisterType<LemmasAlternateSearchProvider>()
.As<IAlternateSearchProvider<SearchConcordances, SearchConcordancesResponse>>()
.WithParameter(simpleLemmatizationServiceParameter)
.InstancePerLifetimeScope();
}
}
@@ -1,25 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
<Version>1.0.4</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="EFCore.NamingConventions" Version="6.0.0" />
<PackageReference Include="FluentValidation" Version="11.2.2" />
<PackageReference Include="Hangfire.Core" Version="1.7.31" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Npgsql" Version="6.0.7" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="Autofac" />
<PackageReference Include="EFCore.NamingConventions" />
<PackageReference Include="FluentValidation" />
<PackageReference Include="Hangfire.Core" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Npgsql" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
</ItemGroup>
<ItemGroup>
@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac.Features.Indexed;
using Rsdo.Concordancer.Core.Extensions;
using Rsdo.Concordancer.ServiceModel.Requests.Concordances;
using Rsdo.Concordancer.ServiceModel.Types;
using Rsdo.Concordancer.Services.Services.TokenizerService;
@@ -21,6 +22,12 @@ public class InputQueryParser : IInputQueryParser
public async Task<IEnumerable<(TokenType type, string form)>> Tokenize(string query)
{
if (query.IsWildcardSearch())
{
// Classla tokenizer doesn't support wildcards, so use default tokenizer
return await tokenizers[TokenizerType.Default].Tokenize(query);
}
try
{
return await tokenizers[TokenizerType.Classla].Tokenize(query);
@@ -0,0 +1,7 @@
namespace Rsdo.Concordancer.Services.Services.LemmatizationService;
public enum LemmatizationType
{
Simple,
Wildcard,
}
@@ -7,17 +7,18 @@ using Rsdo.Concordancer.Services.Framework.DbContext;
namespace Rsdo.Concordancer.Services.Services.LemmatizationService;
public class LemmatizationService : ILemmatizationService
public class SimpleLemmatizationService : ILemmatizationService
{
private readonly MasterDbContext dbContext;
public LemmatizationService(MasterDbContext dbContext)
public SimpleLemmatizationService(MasterDbContext dbContext)
{
this.dbContext = dbContext;
}
public async Task<List<string>> GetLemmas(string form)
public virtual async Task<List<string>> GetLemmas(string form)
{
// Retrieving lemmas without support for wildcard search
var lemmas = await dbContext.LemmaFormPair.Where(f => f.Form.ToLower() == form.ToLower()).Select(f => f.Lemma).Distinct().ToListAsync();
if (lemmas.IsNullOrEmpty())
{
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Rsdo.Concordancer.Core.Extensions;
using Rsdo.Concordancer.Services.Framework.DbContext;
namespace Rsdo.Concordancer.Services.Services.LemmatizationService;
public class WildcardLemmatizationService : SimpleLemmatizationService
{
private readonly MasterDbContext dbContext;
public WildcardLemmatizationService(MasterDbContext dbContext)
: base(dbContext)
{
this.dbContext = dbContext;
}
public override async Task<List<string>> GetLemmas(string form)
{
if (!form.IsWildcardSearch())
{
return await base.GetLemmas(form);
}
var lemmas = await dbContext.LemmaFormPair.Where(f => EF.Functions.Like(f.Form.ToLower(), form.Replace("*", "%").Replace("?", "_").ToLower()))
.Select(f => f.Lemma)
.Distinct()
.ToListAsync();
if (lemmas.IsNullOrEmpty())
{
lemmas.Add(form);
}
return lemmas;
}
}
@@ -1,21 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
@@ -1,21 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Rsdo.StyleCop" Version="1.0.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>