Projekt

Obecné

Profil

Stáhnout (28.2 KB) Statistiky
| Větev: | Tag: | Revize:
1
using Core.Contexts;
2
using Core.Entities;
3
using Models.Annotations;
4
using Models.Enums;
5
using Serilog;
6
using System;
7
using System.Collections.Generic;
8
using System.Linq;
9
using System.Text;
10
using System.Threading.Tasks;
11
using Microsoft.EntityFrameworkCore;
12
using AutoMapper;
13
using Models.Tags;
14
using Ganss.XSS;
15
using HtmlAgilityPack;
16
using System.Text.RegularExpressions;
17
using Newtonsoft.Json;
18
using Core.GraphUtils;
19

    
20
namespace Core.Services.AnnotationService
21
{
22
    public class AnnotationServiceEF : IAnnotationService
23
    {
24
        private readonly DatabaseContext context;
25
        private readonly ILogger logger;
26
        private readonly IMapper mapper;
27
        private readonly IHTMLService htmlService;
28

    
29

    
30
        public AnnotationServiceEF(DatabaseContext context, ILogger logger, IMapper mapper, IHTMLService htmlService)
31
        {
32
            this.context = context;
33
            this.logger = logger;
34
            this.mapper = mapper;
35
            this.htmlService = htmlService;
36
        }
37

    
38
        public void CreateDocumentAnnotations(AnnotationsAddRequest request, Guid clientUserId)
39
        {
40
            User addingUser = context.Users.Single(u => u.Id == clientUserId);
41

    
42
            // Check the documents exist
43
            var documents = context.Documents.Where(d => request.DocumentIdList.Contains(d.Id)).ToList();
44
            if (documents.Count() != request.DocumentIdList.Count)
45
            {
46
                logger.Information($"Received a non-existent Document ID when assigning documents to users");
47
                throw new InvalidOperationException($"{request.DocumentIdList.Count - documents.Count()} of the received documents do not exist");
48
            }
49

    
50
            var users = context.Users.Where(u => request.UserIdList.Contains(u.Id)).ToList();
51
            foreach (var user in users)
52
            {
53
                var userAnnotatedDocuments = context.Annotations
54
                    .Where(a => !(a is FinalAnnotation))
55
                    .Where(a => a.User == user)
56
                    .Select(a => a.Document)
57
                    .ToList();
58
                foreach (var doc in documents)
59
                {
60
                    if (userAnnotatedDocuments.Contains(doc))
61
                    {
62
                        logger.Information($"User {user.Username} has already been assigned the document {doc.Id}, ignoring");
63
                        continue;
64
                    }
65

    
66
                    context.Annotations.Add(new Annotation()
67
                    {
68
                        User = user,
69
                        UserAssigned = addingUser,
70
                        DateAssigned = DateTime.Now,
71
                        DateLastChanged = DateTime.Now,
72
                        Document = doc,
73
                        State = EState.NEW,
74
                        Note = ""
75
                    });
76
                }
77
            }
78

    
79
            context.SaveChanges();
80
        }
81

    
82
        public AnnotationListResponse GetUserAnnotations(Guid userId)
83
        {
84
            var annotations = context.Annotations
85
                .Where(a => !(a is FinalAnnotation))
86
                .Where(a => a.User.Id == userId)
87
                .Include(a => a.Document)
88
                .ToList();
89

    
90
            var infos = new List<AnnotationListInfo>();
91
            foreach (var annotation in annotations)
92
            {
93
                infos.Add(new AnnotationListInfo()
94
                {
95
                    AnnotationId = annotation.Id,
96
                    DocumentName = annotation.Document.Name,
97
                    State = annotation.State
98
                });
99
            }
100

    
101
            return new AnnotationListResponse()
102
            {
103
                Annotations = infos
104
            };
105
        }
106

    
107
        public void AddNoteToAnnotation(Guid annotationId, Guid userId, ERole userRole, AddNoteToAnnotationRequest request, bool isFinal)
108
        {
109
            Annotation annotation = null;
110
            try
111
            {
112
                if (!isFinal)
113
                {
114
                    annotation = context.Annotations.Include(a => a.User).First(a => a.Id == annotationId);
115
                }
116
                else
117
                {
118
                    annotation = context.FinalAnnotations.Include(a => a.User).First(a => a.Id == annotationId);
119
                }
120
            }
121
            catch (Exception)
122
            {
123
                throw new InvalidOperationException("Annotation not found");
124
            }
125

    
126
            if (userRole < ERole.ADMINISTRATOR && annotation.User.Id != userId)
127
            {
128
                throw new UnauthorizedAccessException("User does not have access to this annotation");
129
            }
130

    
131
            annotation.Note = request.Note;
132
            context.SaveChanges();
133
        }
134

    
135

    
136
        public AnnotationInfo GetAnnotation(Guid annotationId, Guid userId, ERole userRole, bool isFinal)
137
        {
138
            Annotation annotation = new();
139
            if (!isFinal)
140
            {
141
                annotation = context.Annotations
142
                .Where(a => !(a is FinalAnnotation))
143
                .Where(a => a.Id == annotationId)
144
                .Include(a => a.User)
145
                .Include(a => a.Document).ThenInclude(d => d.Content)
146
                .First();
147
            }
148
            else
149
            {
150
                annotation = context.FinalAnnotations
151
                .Where(a => a.Id == annotationId)
152
                .Include(a => a.User)
153
                .Include(a => a.Document).ThenInclude(d => d.Content)
154
                .First();
155
            }
156

    
157

    
158
            if (userRole < ERole.ADMINISTRATOR)
159
            {
160
                if (annotation.User.Id != userId)
161
                {
162
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
163
                }
164
            }
165

    
166
            var documentContent = context.Documents.Where(d => d.Id == annotation.Document.Id).Select(d => d.Content).First();
167

    
168
            IQueryable<AnnotationTagGeneric> tagsQuery;
169
            List<AnnotationTagGeneric> tags;
170
            if (!isFinal)
171
            {
172
                tagsQuery = context.AnnotationTags.Where(at => at.Annotation.Id == annotationId);
173
            }
174
            else
175
            {
176
                tagsQuery = context.FinalAnnotationTags.Where(at => at.Annotation.Id == annotationId);
177
            }
178

    
179
            tags = tagsQuery.Include(at => at.Tag)
180
                .ThenInclude(t => t.Category)
181
                .Include(at => at.SubTag)
182
                .OrderBy(at => at.Position)
183
                .Select(at => at)
184
                .ToList();
185

    
186
            List<TagInstanceInfo> tagInstanceInfos = new();
187
            foreach (var tag in tags)
188
            {
189
                var tagInstance = mapper.Map<TagInstanceInfo>(tag);
190
                tagInstanceInfos.Add(tagInstance);
191
            }
192

    
193
            var docToRender = "";
194
            HTMLService.CachedInfo cachedInfoToReturn = new();
195
            if (annotation.CachedDocumentHTML == "")
196
            {
197
                var result = htmlService.FullPreprocessHTML(documentContent.Content, tags);
198
                cachedInfoToReturn = result.Item2;
199
                docToRender = result.Item1;
200

    
201
                annotation.CachedStartPositions = JsonConvert.SerializeObject(cachedInfoToReturn.TagStartPositions);
202
                annotation.CachedLengths = JsonConvert.SerializeObject(cachedInfoToReturn.TagStartLengths);
203
                annotation.CachedClosingPositions = JsonConvert.SerializeObject(cachedInfoToReturn.TagClosingPositions);
204
                annotation.CachedClosingLengths = JsonConvert.SerializeObject(cachedInfoToReturn.TagClosingLengths);
205
                annotation.CachedCSS = JsonConvert.SerializeObject(cachedInfoToReturn.TagInstanceCSS);
206
                annotation.ModifiedType = EModified.NONE;
207
                annotation.CachedDocumentHTML = docToRender;
208
                context.SaveChanges();
209
            }
210
            else
211
            {
212
                docToRender = annotation.CachedDocumentHTML;
213
                cachedInfoToReturn = new()
214
                {
215
                    TagStartPositions = JsonConvert.DeserializeObject<List<int>>(annotation.CachedStartPositions),
216
                    TagStartLengths = JsonConvert.DeserializeObject<List<int>>(annotation.CachedLengths),
217
                    TagClosingPositions = JsonConvert.DeserializeObject<List<int>>(annotation.CachedClosingPositions),
218
                    TagClosingLengths = JsonConvert.DeserializeObject<List<int>>(annotation.CachedClosingLengths),
219
                    TagInstanceCSS = JsonConvert.DeserializeObject<List<TagInstanceCSSInfo>>(annotation.CachedCSS)
220
                };
221

    
222
                // The annotation has been modified and we need to either add the new tag or remove the tag
223
                if (annotation.ModifiedType != EModified.NONE)
224
                {
225
                    if (annotation.ModifiedType == EModified.ADDED)
226
                    {
227
                        var lastModifiedTag = context.AnnotationTags.Where(at => at.Id == annotation.LastModifiedTagId).First();
228
                        var result = htmlService.PartialPreprocessHTMLAddTag(docToRender, documentContent.Content, lastModifiedTag, tags, cachedInfoToReturn);
229
                        docToRender = result.Item1;
230
                        cachedInfoToReturn = result.Item2;
231
                    }
232
                    else if (annotation.ModifiedType == EModified.REMOVED)
233
                    {
234
                        var result = htmlService.PartialPreprocessHTMLRemoveTag(docToRender, documentContent.Content, new AnnotationTag() { Id = annotation.LastModifiedTagId.Value }, tags, cachedInfoToReturn);
235
                        docToRender = result.Item1;
236
                        cachedInfoToReturn = result.Item2;
237
                    }
238

    
239
                    annotation.ModifiedType = EModified.NONE;
240
                    annotation.CachedStartPositions = JsonConvert.SerializeObject(cachedInfoToReturn.TagStartPositions);
241
                    annotation.CachedLengths = JsonConvert.SerializeObject(cachedInfoToReturn.TagStartLengths);
242
                    annotation.CachedClosingPositions = JsonConvert.SerializeObject(cachedInfoToReturn.TagClosingPositions);
243
                    annotation.CachedClosingLengths = JsonConvert.SerializeObject(cachedInfoToReturn.TagClosingLengths);
244
                    annotation.CachedDocumentHTML = docToRender;
245
                    annotation.CachedCSS = JsonConvert.SerializeObject(cachedInfoToReturn.TagInstanceCSS);
246
                    context.SaveChanges();
247
                }
248
            }
249

    
250
            // We probably cannot use AutoMapper since we are dealing with too many different entities
251
            AnnotationInfo annotationInfo = new()
252
            {
253
                SourceDocumentContent = documentContent.Content,
254
                DocumentToRender = docToRender,
255
                TagStartPositions = cachedInfoToReturn.TagStartPositions.ToArray(),
256
                TagLengths = cachedInfoToReturn.TagStartLengths.ToArray(),
257
                Note = annotation.Note,
258
                State = annotation.State,
259
                Type = IsHtml(documentContent.Content) ? EDocumentType.HTML : EDocumentType.TEXT,
260
                TagInstances = tagInstanceInfos,
261
                CSSInfo = cachedInfoToReturn.TagInstanceCSS
262
            };
263

    
264
            return annotationInfo;
265
        }
266

    
267
        // TODO temporary
268
        private bool IsHtml(string text)
269
        {
270
            return text.Contains("<html>");
271
        }
272

    
273
        public void AddAnnotationInstance(Guid annotationId, Guid userId, ERole userRole, AnnotationInstanceAddRequest request, bool isFinal)
274
        {
275
            Annotation annotation = new();
276
            if (!isFinal)
277
            {
278
                annotation = context.Annotations
279
                    .Where(a => !(a is FinalAnnotation))
280
                    .Where(a => a.Id == annotationId)
281
                    .Include(a => a.User)
282
                    .Include(a => a.Document).ThenInclude(d => d.Content)
283
                    .First();
284
            }
285
            else
286
            {
287
                annotation = context.FinalAnnotations
288
                    .Where(a => a.Id == annotationId)
289
                    .Include(a => a.User)
290
                    .Include(a => a.Document).ThenInclude(d => d.Content)
291
                    .First();
292
            }
293

    
294
            if (userRole < ERole.ADMINISTRATOR)
295
            {
296
                if (annotation.User.Id != userId)
297
                {
298
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
299
                }
300
            }
301

    
302
            if (annotation.State == EState.NEW)
303
            {
304
                annotation.State = EState.IN_PROGRESS;
305
            }
306

    
307
            AnnotationTagGeneric annotationTag = new();
308
            if (isFinal)
309
            {
310
                annotationTag = new FinalAnnotationTag()
311
                {
312
                    Id = Guid.NewGuid(),
313
                    Annotation = annotation as FinalAnnotation,
314
                    Instance = request.InstanceId == null ? Guid.NewGuid() : request.InstanceId.Value,
315
                    Length = request.Length,
316
                    Position = request.Position,
317
                    SelectedText = request.SelectedText,
318
                    Note = ""
319
                };
320
            }
321
            else
322
            {
323
                annotationTag = new AnnotationTag()
324
                {
325
                    Id = Guid.NewGuid(),
326
                    Annotation = annotation as Annotation,
327
                    Instance = request.InstanceId == null ? Guid.NewGuid() : request.InstanceId.Value,
328
                    Length = request.Length,
329
                    Position = request.Position,
330
                    SelectedText = request.SelectedText,
331
                    Note = ""
332
                };
333
            }
334

    
335
            if (request.Type == ETagType.TAG)
336
            {
337
                annotationTag.Tag = context.Tags.Where(t => t.Id == request.Id).Single();
338
                annotationTag.SubTag = null;
339

    
340
                if (annotationTag.Tag.SentimentEnabled)
341
                {
342
                    annotationTag.Sentiment = ETagSentiment.NEUTRAL;
343
                }
344

    
345
                // If for the same annotation exists a tag with same position and length and of the same type, ignore
346
                if (context.AnnotationTags.Any(at =>
347
                at.Position == annotationTag.Position &&
348
                at.Length == annotationTag.Length &&
349
                at.Annotation == annotation &&
350
                at.Tag == annotationTag.Tag))
351
                {
352
                    throw new InvalidOperationException("Duplicate tag");
353
                }
354

    
355
            }
356
            else if (request.Type == ETagType.SUBTAG)
357
            {
358
                var subTag = context.SubTags.Where(st => st.Id == request.Id).Include(st => st.Tag).Single();
359
                annotationTag.SubTag = subTag;
360
                annotationTag.Tag = subTag.Tag;
361

    
362
                if (annotationTag.SubTag.SentimentEnabled)
363
                {
364
                    annotationTag.Sentiment = ETagSentiment.NEUTRAL;
365
                }
366

    
367
                if (context.AnnotationTags.Any(at =>
368
                at.Position == annotationTag.Position &&
369
                at.Length == annotationTag.Length &&
370
                at.Annotation == annotation &&
371
                at.Tag == annotationTag.Tag &&
372
                at.SubTag == annotationTag.SubTag))
373
                {
374
                    throw new InvalidOperationException("Duplicate tag");
375
                }
376
            }
377
            else
378
            {
379
                throw new ArgumentException($"Unknown tag type {request.Type}");
380
            }
381

    
382
            if (annotation.State == EState.NEW)
383
            {
384
                annotation.State = EState.IN_PROGRESS;
385
            }
386

    
387
            annotation.LastModifiedTagId = annotationTag.Id;
388
            annotation.ModifiedType = EModified.ADDED;
389

    
390
            if (isFinal)
391
            {
392
                context.FinalAnnotationTags.Add(annotationTag as FinalAnnotationTag);
393
            }
394
            else
395
            {
396
                context.AnnotationTags.Add(annotationTag as AnnotationTag);
397
            }
398

    
399
            context.SaveChanges();
400
        }
401

    
402
        public void DeleteAnnotationInstance(Guid annotationId, Guid tagInstanceId, Guid loggedUserId, ERole userRole, bool isFinal)
403
        {
404
            Annotation annotation = null;
405
            try
406
            {
407
                if (!isFinal)
408
                {
409
                    annotation = context.Annotations
410
                    .Where(a => !(a is FinalAnnotation))
411
                    .Where(a => a.Id == annotationId)
412
                    .Include(a => a.User)
413
                    .Include(a => a.Document).ThenInclude(d => d.Content)
414
                    .First();
415
                }
416
                else
417
                {
418
                    annotation = context.FinalAnnotations
419
                    .Where(a => a.Id == annotationId)
420
                    .Include(a => a.User)
421
                    .Include(a => a.Document).ThenInclude(d => d.Content)
422
                    .First();
423
                }
424

    
425

    
426
            }
427
            catch (Exception ex)
428
            {
429
                throw new InvalidOperationException("Could not find annotation");
430
            }
431

    
432

    
433
            if (userRole < ERole.ADMINISTRATOR)
434
            {
435
                if (annotation.User.Id != loggedUserId)
436
                {
437
                    throw new UnauthorizedAccessException($"User {loggedUserId} does not have assigned annotation {annotationId}");
438
                }
439
            }
440

    
441
            AnnotationTagGeneric annotationTag = new();
442

    
443
            if (isFinal)
444
            {
445
                if (!context.FinalAnnotationTags.Any(at => at.Id == tagInstanceId))
446
                {
447
                    throw new InvalidOperationException("Could not find tag instance");
448
                }
449
                annotationTag = context.FinalAnnotationTags.First(at => at.Id == tagInstanceId);
450

    
451
            }
452
            else
453
            {
454
                if (!context.AnnotationTags.Any(at => at.Id == tagInstanceId))
455
                {
456
                    throw new InvalidOperationException("Could not find tag instance");
457
                }
458
                annotationTag = context.AnnotationTags.First(at => at.Id == tagInstanceId);
459
            }
460

    
461
            annotation.LastModifiedTagId = annotationTag.Id;
462
            annotation.ModifiedType = EModified.REMOVED;
463

    
464
            if (isFinal)
465
            {
466
                context.FinalAnnotationTags.Remove(annotationTag as FinalAnnotationTag);
467
            }
468
            else
469
            {
470
                context.AnnotationTags.Remove(annotationTag as AnnotationTag);
471
            }
472

    
473
            context.SaveChanges();
474
        }
475

    
476
        public void SetTagInstanceSentiment(Guid annotationId, Guid instanceId, Guid userId, ERole userRole, ETagSentiment sentiment, bool isFinal)
477
        {
478
            Annotation annotation = null;
479
            try
480
            {
481
                if (!isFinal)
482
                {
483
                    annotation = context.Annotations
484
                    .Where(a => !(a is FinalAnnotation))
485
                    .Where(a => a.Id == annotationId)
486
                    .Include(a => a.User)
487
                    .Include(a => a.Document).ThenInclude(d => d.Content)
488
                    .First();
489
                }
490
                else
491
                {
492
                    annotation = context.FinalAnnotations
493
                    .Where(a => a.Id == annotationId)
494
                    .Include(a => a.User)
495
                    .Include(a => a.Document).ThenInclude(d => d.Content)
496
                    .First();
497
                }
498
            }
499
            catch (Exception ex)
500
            {
501
                throw new InvalidOperationException("Could not find annotation");
502
            }
503

    
504

    
505
            if (userRole < ERole.ADMINISTRATOR)
506
            {
507
                if (annotation.User.Id != userId)
508
                {
509
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
510
                }
511
            }
512

    
513

    
514
            IEnumerable<AnnotationTagGeneric> tagInstances = null;
515

    
516
            if (isFinal)
517
            {
518
                tagInstances = context.FinalAnnotationTags.Where(at => at.Instance == instanceId).ToList();
519
            }
520
            else
521
            {
522
                tagInstances = context.AnnotationTags.Where(at => at.Instance == instanceId).ToList();
523
            }
524

    
525
            if (tagInstances.Count() == 0)
526
            {
527
                throw new InvalidOperationException("No such instance found");
528
            }
529

    
530
            foreach (var tagInstance in tagInstances)
531
            {
532
                tagInstance.Sentiment = sentiment;
533
            }
534

    
535
            context.SaveChanges();
536
        }
537

    
538
        public void MarkAnnotationAsDone(Guid annotationId, Guid userId, ERole userRole, bool done, bool isFinal)
539
        {
540

    
541
            Annotation annotation = null;
542
            try
543
            {
544
                if (!isFinal)
545
                {
546
                    annotation = context.Annotations
547
                    .Where(a => !(a is FinalAnnotation))
548
                   .Where(a => a.Id == annotationId)
549
                   .Include(a => a.User)
550
                   .Include(a => a.Document).ThenInclude(d => d.Content)
551
                   .First();
552
                }
553
                else
554
                {
555
                    annotation = context.FinalAnnotations
556
                   .Where(a => a.Id == annotationId)
557
                   .Include(a => a.User)
558
                   .Include(a => a.Document).ThenInclude(d => d.Content)
559
                   .First();
560
                }
561
            }
562
            catch (Exception ex)
563
            {
564
                throw new InvalidOperationException("Could not find annotation");
565
            }
566

    
567
            if (userRole < ERole.ADMINISTRATOR)
568
            {
569
                if (annotation.User.Id != userId)
570
                {
571
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
572
                }
573
            }
574

    
575
            annotation.State = done ? EState.DONE : EState.IN_PROGRESS;
576
            context.SaveChanges();
577
        }
578

    
579
        public Guid CreateFinalAnnotation(Guid documentId, Guid userId)
580
        {
581
            var document = context.Documents.Single(d => d.Id == documentId);
582
            var user = context.Users.Single(u => u.Id == userId);
583

    
584
            // Remove existing 
585
            if (context.FinalAnnotations.Any(fa => fa.Document == document))
586
            {
587
                var finalAnnotationOld = context.FinalAnnotations.Single(fa => fa.Document == document);
588
                context.FinalAnnotations.Remove(finalAnnotationOld);
589
            }
590

    
591
            var annotations = context.Annotations
592
                .Where(a => !(a is FinalAnnotation))
593
                .Include(a => a.Document)
594
                .Where(a => a.Document == document && a.State == EState.DONE)
595
                .ToList();
596

    
597
            var finalAnnotation = new FinalAnnotation()
598
            {
599
                Id = Guid.NewGuid(),
600
                DateAssigned = DateTime.Now,
601
                DateLastChanged = DateTime.Now,
602
                Document = document,
603
                User = user,
604
                UserAssigned = user,
605
                Annotations = annotations,
606
                State = EState.NEW
607
            };
608

    
609
            List<AnnotationTag> annotationTagsAll = new();
610
            foreach (var annotation in annotations)
611
            {
612
                annotationTagsAll.AddRange(context.AnnotationTags
613
                    .Where(at => at.Annotation == annotation)
614
                    .Include(at => at.Tag)
615
                    .Include(at => at.SubTag)
616
                    .Include(at => at.Annotation)
617
                    .ThenInclude(a => a.User)
618
                    .ToList());
619
            }
620
            annotationTagsAll = annotationTagsAll.OrderBy(at => at.Position).ToList();
621

    
622
            Dictionary<Guid, List<AnnotationTag>> occurenceDict = new();
623
            foreach (var annotationTag in annotationTagsAll)
624
            {
625
                if (occurenceDict.ContainsKey(annotationTag.Instance))
626
                {
627
                    occurenceDict[annotationTag.Instance].Add(annotationTag);
628
                }
629
                else
630
                {
631
                    occurenceDict[annotationTag.Instance] = new();
632
                    occurenceDict[annotationTag.Instance].Add(annotationTag);
633
                }
634
            }
635

    
636
            List<List<AnnotationTag>> occurenceLists = new();
637
            foreach (var key in occurenceDict.Keys)
638
            {
639
                occurenceLists.Add(occurenceDict[key]);
640
            }
641

    
642
            List<List<AnnotationTag>> annotationTagsProcessed = new();
643
            List<FinalAnnotationTag> finalAnnotationTags = new();
644
            for (int i = 0; i < occurenceLists.Count; i++)
645
            {
646
                var occurrenceList1 = occurenceLists[i];
647
                List<List<AnnotationTag>> sameLists = new();
648

    
649
                for (int j = 0; j < occurenceLists.Count; j++)
650
                {
651
                    var occurrenceList2 = occurenceLists[j];
652

    
653
                    if (annotationTagsProcessed.Contains(occurrenceList2))
654
                    {
655
                        continue;
656
                    }
657

    
658
                    if (SelectionsAreSame(occurrenceList1, occurrenceList2))
659
                    {
660
                        sameLists.Add(occurrenceList2);
661
                    }
662
                }
663

    
664
                // This means that this occurrence ahs already been processed as matching a previous tag
665
                if (sameLists.Count() == 0)
666
                {
667
                    continue;
668
                }
669

    
670
                List<User> relatedUsers = new();
671
                foreach (var list in sameLists)
672
                {
673
                    relatedUsers.Add(list[0].Annotation.User);
674
                }
675

    
676
                foreach (var tag in sameLists[0])
677
                {
678
                    finalAnnotationTags.Add(new()
679
                    {
680
                        Id = Guid.NewGuid(),
681
                        Tag = tag.Tag,
682
                        SubTag = tag.SubTag,
683
                        Annotation = finalAnnotation,
684
                        SelectedText = tag.SelectedText,
685
                        Sentiment = tag.Sentiment,
686
                        Instance = tag.Instance,
687
                        IsFinal = sameLists.Count == annotations.Count,
688
                        Length = tag.Length,
689
                        Position = tag.Position,
690
                        Note = "",
691
                        Users = relatedUsers
692
                    });
693
                }
694

    
695
                annotationTagsProcessed.AddRange(sameLists);
696
            }
697

    
698
            context.FinalAnnotations.Add(finalAnnotation);
699
            context.SaveChanges();
700
            context.FinalAnnotationTags.AddRange(finalAnnotationTags);
701

    
702
            context.SaveChanges();
703
            return finalAnnotation.Id;
704
        }
705

    
706
        private bool SelectionsAreSame(List<AnnotationTag> list1, List<AnnotationTag> list2)
707
        {
708
            if (list1.Count != list2.Count)
709
            {
710
                return false;
711
            }
712

    
713
            bool sentimentEnabled = list1[0].Tag.SentimentEnabled;
714

    
715
            if (sentimentEnabled)
716
            {
717
                for (int i = 0; i < list1.Count; i++)
718
                {
719
                    var tag1 = list1[i];
720
                    var tag2 = list2[i];
721
                    if (tag1.Position == tag2.Position &&
722
                        tag1.Length == tag2.Length &&
723
                        tag1.Sentiment == tag2.Sentiment &&
724
                        tag1.Tag == tag2.Tag &&
725
                        tag1.SubTag == tag2.SubTag)
726
                    {
727
                        continue;
728
                    }
729
                    else
730
                    {
731
                        return false;
732
                    }
733
                }
734
            }
735
            else
736
            {
737
                for (int i = 0; i < list1.Count; i++)
738
                {
739
                    var tag1 = list1[i];
740
                    var tag2 = list2[i];
741
                    if (tag1.Position == tag2.Position &&
742
                        tag1.Length == tag2.Length &&
743
                        tag1.Tag == tag2.Tag &&
744
                        tag1.SubTag == tag2.SubTag)
745
                    {
746
                        continue;
747
                    }
748
                    else
749
                    {
750
                        return false;
751
                    }
752
                }
753
            }
754

    
755
            return true;
756
        }
757
    }
758
}
(1-1/2)