Projekt

Obecné

Profil

Stáhnout (7.78 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

    
18
namespace Core.Services.AnnotationService
19
{
20
    public class AnnotationServiceEF : IAnnotationService
21
    {
22
        private readonly DatabaseContext context;
23
        private readonly ILogger logger;
24
        private readonly IMapper mapper;
25

    
26
        public AnnotationServiceEF(DatabaseContext context, ILogger logger, IMapper mapper)
27
        {
28
            this.context = context;
29
            this.logger = logger;
30
            this.mapper = mapper;
31
        }
32

    
33
        public void CreateDocumentAnnotations(AnnotationsAddRequest request, Guid clientUserId)
34
        {
35
            User addingUser = context.Users.Single(u => u.Id == clientUserId);
36

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

    
45
            var users = context.Users.Where(u => request.UserIdList.Contains(u.Id)).ToList();
46
            foreach (var user in users)
47
            {
48
                var userAnnotatedDocuments = context.Annotations.Where(a => a.User == user).Select(a => a.Document).ToList();
49
                foreach (var doc in documents)
50
                {
51
                    if (userAnnotatedDocuments.Contains(doc))
52
                    {
53
                        logger.Information($"User {user.Username} has already been assigned the document {doc.Id}, ignoring");
54
                        continue;
55
                    }
56

    
57
                    context.Annotations.Add(new Annotation()
58
                    {
59
                        User = user,
60
                        UserAssigned = addingUser,
61
                        DateAssigned = DateTime.Now,
62
                        DateLastChanged = DateTime.Now,
63
                        Document = doc,
64
                        State = EState.NEW,
65
                        Note = ""
66
                    });
67
                }
68
            }
69

    
70
            context.SaveChanges();
71
        }
72

    
73
        public AnnotationListResponse GetUserAnnotations(Guid userId)
74
        {
75
            var annotations = context.Annotations.Where(a => a.User.Id == userId).Include(a => a.Document).ToList();
76
            var documentIds = annotations.Select(a => a.Document.Id).ToList();
77
            var documents = context.Documents.Where(d => documentIds.Contains(d.Id));
78
            var infos = new List<AnnotationListInfo>();
79

    
80
            var annotationsDocuments = annotations.Zip(documents, (a, d) => new { Annotation = a, Document = d });
81
            foreach (var ad in annotationsDocuments)
82
            {
83
                infos.Add(new AnnotationListInfo()
84
                {
85
                    AnnotationId = ad.Annotation.Id,
86
                    DocumentName = ad.Document.Name,
87
                    State = ad.Annotation.State
88
                });
89
            }
90

    
91
            return new AnnotationListResponse()
92
            {
93
                Annotations = infos
94
            };
95
        }
96

    
97
        public AnnotationInfo GetAnnotation(Guid annotationId, Guid userId, ERole userRole)
98
        {
99
            var annotation = context.Annotations
100
                .Where(a => a.Id == annotationId)
101
                .Include(a => a.User)
102
                .Include(a => a.Document).ThenInclude(d => d.Content)
103
                .First();
104

    
105
            if (userRole < ERole.ADMINISTRATOR)
106
            {
107
                if (annotation.User.Id != userId)
108
                {
109
                    throw new UnauthorizedAccessException($"User {userId} does not have assigned annotation {annotationId}");
110
                }
111
            }
112

    
113
            var documentContent = context.Documents.Where(d => d.Id == annotation.Document.Id).Select(d => d.Content).First();
114
            var preprocessingResult = PreprocessHTML(documentContent.Content);
115

    
116
            // We probably cannot use AutoMapper since we are dealing with too many different entities
117
            AnnotationInfo annotationInfo = new()
118
            {
119
                SourceDocumentContent = documentContent.Content,
120
                DocumentToRender = preprocessingResult.docToRender,
121
                TagStartPositions = preprocessingResult.openingTagPositions,
122
                TagLengths = preprocessingResult.openingTagLengths,
123
                Note = annotation.Note,
124
                State = annotation.State,
125
                Type = IsHtml(documentContent.Content) ? EDocumentType.HTML : EDocumentType.TEXT
126
            };
127

    
128
            var tags = context.AnnotationTags.Where(at => at.Annotation.Id == annotationId)
129
                .Include(at => at.Tag).ThenInclude(t => t.Category)
130
                .Include(at => at.SubTag)
131
                .ToList();
132

    
133
            foreach (var tag in tags)
134
            {
135
                var tagInstance = mapper.Map<TagInstanceInfo>(tag);
136
                annotationInfo.TagInstances.Add(tagInstance);
137
            }
138

    
139
            return annotationInfo;
140
        }
141

    
142
        private (string docToRender, int[] openingTagPositions, int[] openingTagLengths, int[] closingTagPositions, int[] closingTagLengths) PreprocessHTML(string htmlSource)
143
        {
144
            var docOriginal = new HtmlDocument();
145
            docOriginal.LoadHtml(htmlSource);
146
            var docToEdit = new HtmlDocument();
147
            docToEdit.LoadHtml(htmlSource);
148

    
149
            string idAttributeName = "aswi-tag-id";
150

    
151
            var descendantsOriginal = docOriginal.DocumentNode.DescendantsAndSelf();
152
            var descendantsToEdit = docToEdit.DocumentNode.DescendantsAndSelf();
153

    
154
            List<int> tagStartPositions = new();
155
            List<int> tagStartLengths = new();
156

    
157

    
158
            List<int> tagClosingPositions = new();
159
            List<int> tagClosingLengths = new();
160

    
161
            int currentId = 0;
162

    
163
            var zipped = descendantsOriginal.Zip(descendantsToEdit, (orig, toEdit) => new { Original = orig, ToEdit = toEdit });
164
            foreach (var node in zipped)
165
            {
166
                var originalNode = node.Original;
167
                var toEditNode = node.ToEdit;
168

    
169
                if (originalNode.Name.Contains("#"))
170
                {
171
                    continue;
172
                }
173

    
174
                tagStartPositions.Add(originalNode.OuterStartIndex);
175
                tagStartLengths.Add(originalNode.OuterStartIndex - originalNode.InnerStartIndex);
176
                currentId = tagStartPositions.Count - 1;
177
                toEditNode.Attributes.Add(idAttributeName, currentId.ToString());
178

    
179
                tagClosingPositions.Add(originalNode.InnerStartIndex + originalNode.InnerLength);
180
                tagClosingLengths.Add((originalNode.OuterStartIndex + originalNode.OuterLength) - (originalNode.InnerStartIndex + originalNode.InnerLength));
181
            }
182

    
183
            string docToRender = docToEdit.DocumentNode.OuterHtml;
184
            HtmlSanitizer sanitizer = new HtmlSanitizer();
185
            sanitizer.AllowedAttributes.Clear();
186
            sanitizer.AllowedAttributes.Add(idAttributeName);
187

    
188
            docToRender = sanitizer.Sanitize(docToRender);
189

    
190
            return (docToRender, tagStartPositions.ToArray(), tagStartLengths.ToArray(), tagClosingPositions.ToArray(), tagClosingLengths.ToArray());
191
        }
192

    
193
        // TODO temporary
194
        private bool IsHtml(string text)
195
        {
196
            return text.Contains("<!DOCTYPE html>");
197
        }
198
    }
199
}
(1-1/2)