Revize 440997e0
Přidáno uživatelem Vojtěch Bartička před téměř 3 roky(ů)
Backend/Backend/Controllers/DocumentController.cs | ||
---|---|---|
2 | 2 |
using Microsoft.AspNetCore.Mvc; |
3 | 3 |
using RestAPI.Utils; |
4 | 4 |
using System.Net; |
5 |
using Serilog; |
|
6 | 5 |
using ILogger = Serilog.ILogger; |
7 | 6 |
using Models.Documents; |
8 | 7 |
using RestAPI.Exceptions; |
9 | 8 |
using RestAPI.Authentication; |
9 |
using Models.Annotations; |
|
10 |
using Core.Services.AnnotationService; |
|
10 | 11 |
|
11 | 12 |
namespace RestAPI.Controllers; |
12 | 13 |
|
... | ... | |
16 | 17 |
{ |
17 | 18 |
private readonly IDocumentService documentService; |
18 | 19 |
private readonly ILogger logger; |
20 |
private readonly IAnnotationService annotationService; |
|
19 | 21 |
|
20 |
|
|
21 |
public DocumentController(IDocumentService documentService, ILogger logger) |
|
22 |
public DocumentController(IDocumentService documentService, IAnnotationService annotationService, ILogger logger) |
|
22 | 23 |
{ |
23 | 24 |
this.documentService = documentService; |
24 | 25 |
this.logger = logger; |
26 |
this.annotationService = annotationService; |
|
25 | 27 |
} |
26 | 28 |
|
27 | 29 |
[HttpGet("/documents")] |
... | ... | |
180 | 182 |
} |
181 | 183 |
} |
182 | 184 |
|
185 |
[HttpPost("/document/{documentId}/final")] |
|
186 |
[Authorize(Models.Enums.ERole.ADMINISTRATOR)] |
|
187 |
[ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(CreateFinalAnnotationResponse))] |
|
188 |
[ProducesResponseType((int)HttpStatusCode.Forbidden)] |
|
189 |
public ActionResult<CreateFinalAnnotationResponse> CreateFinalAnnotation([FromServices] ClientInfo clientInfo, Guid documentId) |
|
190 |
{ |
|
191 |
if (clientInfo.LoggedUser == null) |
|
192 |
{ |
|
193 |
logger.Warning("ClientInfo has null LoggerUser in [Authorized] controller"); |
|
194 |
return Problem(); |
|
195 |
} |
|
196 |
|
|
197 |
try |
|
198 |
{ |
|
199 |
var guid = annotationService.CreateFinalAnnotation(documentId, clientInfo.LoggedUser.Id); |
|
200 |
|
|
201 |
return Ok(new CreateFinalAnnotationResponse() |
|
202 |
{ |
|
203 |
FinalAnnotationId = guid |
|
204 |
}); |
|
205 |
} |
|
206 |
catch (InvalidOperationException e) |
|
207 |
{ |
|
208 |
throw new BadRequestException(e.Message); |
|
209 |
} |
|
210 |
} |
|
183 | 211 |
} |
184 | 212 |
|
Backend/Backend/appsettings.Development.json | ||
---|---|---|
6 | 6 |
} |
7 | 7 |
}, |
8 | 8 |
"AllowedHosts": "*", |
9 |
"ConnectionString": "Host=localhost:5432;Database=dbo;Username=myuser;Password=password", |
|
9 |
"ConnectionString": "Host=localhost:5432;Database=dbo;Username=myuser;Password=password;Include Error Detail=true;",
|
|
10 | 10 |
"JwtConfig": { |
11 | 11 |
"Secret": "CJgvc9BtwJjnCExzKLB2ndtxERW3YMhqMMPNrBZuTJaskPqRVfZQHRUnbRHH9Ekp5zsaTSeM73vQSKvq48w2u8jTnKUn7Uhk2zBeD9wLz48ssKMppNC9HDTXSk6RWP5PtXfCayJ67mbUWF6aknnteLg4sDKzqYeNYjyw8Jk6", |
12 | 12 |
"Issuer": "0x00.server" |
Backend/Core/MapperProfiles/TagProfileEF.cs | ||
---|---|---|
18 | 18 |
CreateMap<SubTag, SubTagInfo>(); |
19 | 19 |
CreateMap<AnnotationTag, TagInstanceInfo>() |
20 | 20 |
.ForMember(ti => ti.OccurenceId, opt => opt.MapFrom(at => at.Id)); |
21 |
|
|
22 |
// Ignore ID and Annotation |
|
23 |
CreateMap<AnnotationTag, FinalAnnotationTag>() |
|
24 |
.ForMember(fat => fat.Id, opt => opt.Ignore()) |
|
25 |
.ForMember(fat => fat.Annotation, opt => opt.Ignore()); |
|
21 | 26 |
} |
22 | 27 |
} |
23 | 28 |
} |
Backend/Core/Services/AnnotationService/AnnotationServiceEF.cs | ||
---|---|---|
258 | 258 |
for (; i < descCount; i++) |
259 | 259 |
{ |
260 | 260 |
var node = descendantsToEdit.ElementAt(i); |
261 |
if (!node.Name.Contains("#text") || addedForSelection.Contains(node) || addedForSelection.Contains(node.ParentNode) || |
|
262 |
node.ParentNode.Name == "style") |
|
261 |
if (!node.Name.Contains("#text") || addedForSelection.Contains(node) || addedForSelection.Contains(node.ParentNode) || node.ParentNode.Name == "style") |
|
263 | 262 |
{ |
264 | 263 |
continue; |
265 | 264 |
} |
... | ... | |
959 | 958 |
annotation.State = done ? EState.DONE : EState.IN_PROGRESS; |
960 | 959 |
context.SaveChanges(); |
961 | 960 |
} |
961 |
|
|
962 |
public Guid CreateFinalAnnotation(Guid documentId, Guid userId) |
|
963 |
{ |
|
964 |
var document = context.Documents.Single(d => d.Id == documentId); |
|
965 |
var user = context.Users.Single(u => u.Id == userId); |
|
966 |
|
|
967 |
// Remove existing |
|
968 |
if (context.FinalAnnotations.Any(fa => fa.Document == document)) |
|
969 |
{ |
|
970 |
var finalAnnotationOld = context.FinalAnnotations.Single(fa => fa.Document == document); |
|
971 |
context.FinalAnnotations.Remove(finalAnnotationOld); |
|
972 |
} |
|
973 |
|
|
974 |
var annotations = context.Annotations |
|
975 |
.Include(a => a.Document) |
|
976 |
.Where(a => a.Document == document && a.State == EState.DONE) |
|
977 |
.ToList(); |
|
978 |
|
|
979 |
var finalAnnotation = new FinalAnnotation() |
|
980 |
{ |
|
981 |
Id = Guid.NewGuid(), |
|
982 |
DateAssigned = DateTime.Now, |
|
983 |
DateLastChanged = DateTime.Now, |
|
984 |
Document = document, |
|
985 |
User = user, |
|
986 |
UserAssigned = user, |
|
987 |
Annotations = annotations, |
|
988 |
State = EState.NEW |
|
989 |
}; |
|
990 |
|
|
991 |
List<AnnotationTag> annotationTagsAll = new(); |
|
992 |
foreach (var annotation in annotations) |
|
993 |
{ |
|
994 |
annotationTagsAll.AddRange(context.AnnotationTags |
|
995 |
.Where(at => at.Annotation == annotation) |
|
996 |
.Include(at => at.Tag) |
|
997 |
.Include(at => at.SubTag) |
|
998 |
.Include(at => at.Annotation) |
|
999 |
.ThenInclude(a => a.User) |
|
1000 |
.ToList()); |
|
1001 |
} |
|
1002 |
annotationTagsAll = annotationTagsAll.OrderBy(at => at.Position).ToList(); |
|
1003 |
|
|
1004 |
Dictionary<Guid, List<AnnotationTag>> occurenceDict = new(); |
|
1005 |
foreach (var annotationTag in annotationTagsAll) |
|
1006 |
{ |
|
1007 |
if (occurenceDict.ContainsKey(annotationTag.Instance)) |
|
1008 |
{ |
|
1009 |
occurenceDict[annotationTag.Instance].Add(annotationTag); |
|
1010 |
} |
|
1011 |
else |
|
1012 |
{ |
|
1013 |
occurenceDict[annotationTag.Instance] = new(); |
|
1014 |
occurenceDict[annotationTag.Instance].Add(annotationTag); |
|
1015 |
} |
|
1016 |
} |
|
1017 |
|
|
1018 |
List<List<AnnotationTag>> occurenceLists = new(); |
|
1019 |
foreach (var key in occurenceDict.Keys) |
|
1020 |
{ |
|
1021 |
occurenceLists.Add(occurenceDict[key]); |
|
1022 |
} |
|
1023 |
|
|
1024 |
List<List<AnnotationTag>> annotationTagsProcessed = new(); |
|
1025 |
List<FinalAnnotationTag> finalAnnotationTags = new(); |
|
1026 |
for (int i = 0; i < occurenceLists.Count; i++) |
|
1027 |
{ |
|
1028 |
var occurrenceList1 = occurenceLists[i]; |
|
1029 |
List<List<AnnotationTag>> sameLists = new(); |
|
1030 |
|
|
1031 |
for (int j = 0; j < occurenceLists.Count; j++) |
|
1032 |
{ |
|
1033 |
var occurrenceList2 = occurenceLists[j]; |
|
1034 |
|
|
1035 |
if (annotationTagsProcessed.Contains(occurrenceList2)) |
|
1036 |
{ |
|
1037 |
continue; |
|
1038 |
} |
|
1039 |
|
|
1040 |
if (SelectionsAreSame(occurrenceList1, occurrenceList2)) |
|
1041 |
{ |
|
1042 |
sameLists.Add(occurrenceList2); |
|
1043 |
} |
|
1044 |
} |
|
1045 |
|
|
1046 |
// This means that this occurrence ahs already been processed as matching a previous tag |
|
1047 |
if (sameLists.Count() == 0) |
|
1048 |
{ |
|
1049 |
continue; |
|
1050 |
} |
|
1051 |
|
|
1052 |
List<User> relatedUsers = new(); |
|
1053 |
foreach (var list in sameLists) |
|
1054 |
{ |
|
1055 |
relatedUsers.Add(list[0].Annotation.User); |
|
1056 |
} |
|
1057 |
|
|
1058 |
foreach (var tag in sameLists[0]) |
|
1059 |
{ |
|
1060 |
finalAnnotationTags.Add(new() |
|
1061 |
{ |
|
1062 |
Id = Guid.NewGuid(), |
|
1063 |
Tag = tag.Tag, |
|
1064 |
SubTag = tag.SubTag, |
|
1065 |
Annotation = finalAnnotation, |
|
1066 |
SelectedText = tag.SelectedText, |
|
1067 |
Sentiment = tag.Sentiment, |
|
1068 |
Instance = tag.Instance, |
|
1069 |
IsFinal = sameLists.Count == annotations.Count, |
|
1070 |
Length = tag.Length, |
|
1071 |
Position = tag.Position, |
|
1072 |
Note = "", |
|
1073 |
Users = relatedUsers |
|
1074 |
}); |
|
1075 |
} |
|
1076 |
|
|
1077 |
annotationTagsProcessed.AddRange(sameLists); |
|
1078 |
} |
|
1079 |
|
|
1080 |
context.FinalAnnotations.Add(finalAnnotation); |
|
1081 |
context.SaveChanges(); |
|
1082 |
context.FinalAnnotationTags.AddRange(finalAnnotationTags); |
|
1083 |
|
|
1084 |
context.SaveChanges(); |
|
1085 |
return finalAnnotation.Id; |
|
1086 |
} |
|
1087 |
|
|
1088 |
private bool SelectionsAreSame(List<AnnotationTag> list1, List<AnnotationTag> list2) |
|
1089 |
{ |
|
1090 |
if (list1.Count != list2.Count) |
|
1091 |
{ |
|
1092 |
return false; |
|
1093 |
} |
|
1094 |
|
|
1095 |
bool sentimentEnabled = list1[0].Tag.SentimentEnabled; |
|
1096 |
|
|
1097 |
if (sentimentEnabled) |
|
1098 |
{ |
|
1099 |
for (int i = 0; i < list1.Count; i++) |
|
1100 |
{ |
|
1101 |
var tag1 = list1[i]; |
|
1102 |
var tag2 = list2[i]; |
|
1103 |
if (tag1.Position == tag2.Position && |
|
1104 |
tag1.Length == tag2.Length && |
|
1105 |
tag1.Sentiment == tag2.Sentiment && |
|
1106 |
tag1.Tag == tag2.Tag && |
|
1107 |
tag1.SubTag == tag2.SubTag) |
|
1108 |
{ |
|
1109 |
continue; |
|
1110 |
} |
|
1111 |
else |
|
1112 |
{ |
|
1113 |
return false; |
|
1114 |
} |
|
1115 |
} |
|
1116 |
} |
|
1117 |
else |
|
1118 |
{ |
|
1119 |
for (int i = 0; i < list1.Count; i++) |
|
1120 |
{ |
|
1121 |
var tag1 = list1[i]; |
|
1122 |
var tag2 = list2[i]; |
|
1123 |
if (tag1.Position == tag2.Position && |
|
1124 |
tag1.Length == tag2.Length && |
|
1125 |
tag1.Tag == tag2.Tag && |
|
1126 |
tag1.SubTag == tag2.SubTag) |
|
1127 |
{ |
|
1128 |
continue; |
|
1129 |
} |
|
1130 |
else |
|
1131 |
{ |
|
1132 |
return false; |
|
1133 |
} |
|
1134 |
} |
|
1135 |
} |
|
1136 |
|
|
1137 |
return true; |
|
1138 |
} |
|
962 | 1139 |
} |
963 | 1140 |
} |
Backend/Core/Services/AnnotationService/IAnnotationService.cs | ||
---|---|---|
18 | 18 |
public void AddNoteToAnnotation(Guid annotationId, Guid userId, ERole userRole, AddNoteToAnnotationRequest request); |
19 | 19 |
public void SetTagInstanceSentiment(Guid annotationId, Guid instanceId, Guid userId, ERole userRole, ETagSentiment sentiment); |
20 | 20 |
public void MarkAnnotationAsDone(Guid annotationId, Guid userId, ERole userRole, bool done); |
21 |
public Guid CreateFinalAnnotation(Guid documentId, Guid userId); |
|
21 | 22 |
} |
22 | 23 |
} |
Backend/Models/Annotations/CreateFinalAnnotationResponse.cs | ||
---|---|---|
1 |
using System; |
|
2 |
using System.Collections.Generic; |
|
3 |
using System.Linq; |
|
4 |
using System.Text; |
|
5 |
using System.Threading.Tasks; |
|
6 |
|
|
7 |
namespace Models.Annotations |
|
8 |
{ |
|
9 |
public class CreateFinalAnnotationResponse |
|
10 |
{ |
|
11 |
public Guid FinalAnnotationId { get; set; } |
|
12 |
} |
|
13 |
} |
Také k dispozici: Unified diff
Tag merging for final annotation beta