diff --git a/src/Tree/Controllers/TreeController.cs b/src/Tree/Controllers/TreeController.cs index 8c74eef60fbae3d93781da917332028418f14883..9a8074d5ea1738bbb1a6578696d7155c4b3dfbef 100644 --- a/src/Tree/Controllers/TreeController.cs +++ b/src/Tree/Controllers/TreeController.cs @@ -37,10 +37,10 @@ namespace Coscine.Api.Tree.Controllers private readonly ProjectRoleModel _projectRoleModel; private readonly ProjectResourceModel _projectResourceModel; private readonly CoscineLogger _coscineLogger; - private readonly AnalyticsLogObject _analyticsLogObject; private readonly string _blobApiLink; private readonly string _prefix; private readonly IConfiguration _configuration; + private readonly string _shaclPropertyUrl = "http://www.w3.org/ns/shacl#property"; /// <summary> /// Tree controller constructor @@ -56,11 +56,10 @@ namespace Coscine.Api.Tree.Controllers _projectResourceModel = new ProjectResourceModel(); _coscineLogger = new CoscineLogger(logger); - _analyticsLogObject = new AnalyticsLogObject(); var rule = _configuration.GetStringAndWait("traefik/frontends/Coscine.Api.Blob/routes/Coscine.Api.Blob/rule"); - var host = rule.Substring("Host:".Length, rule.IndexOf(";") - "Host:".Length); - var path = rule.Substring(rule.IndexOf("/")); + var host = rule["Host:".Length..rule.IndexOf(";")]; + var path = rule[rule.IndexOf("/")..]; _blobApiLink = $"https://{host}{path}/blob/"; _prefix = _configuration.GetStringAndWait("coscine/global/epic/prefix"); } @@ -94,6 +93,11 @@ namespace Coscine.Api.Tree.Controllers var user = _authenticator.GetUser(); var check = CheckResourceIdAndPath(resourceId, path, out Resource resource); + if (resource.ApplicationProfile[^1] != '/') + { + resource.ApplicationProfile += '/'; + } + if (user == null || !_resourceModel.HasAccess(user, resource, UserRoles.Owner, UserRoles.Member)) { return Forbid("User has no Access to this resource."); @@ -115,13 +119,14 @@ namespace Coscine.Api.Tree.Controllers var infos = await resourceTypeDefinition.ListEntries(resourceId, path, resourceTypeOptions); var graphs = new List<JToken>(); - + int metadataCount = 0; foreach (var info in infos) { var id = GenerateId(resourceId, info.Key); if (_rdfStoreConnector.HasGraph(id.AbsoluteUri)) { var graph = _rdfStoreConnector.GetGraph(id); + metadataCount = graph.Triples.Count; graphs.Add(JToken.Parse(VDS.RDF.Writing.StringWriter.Write(graph, new RdfJsonWriter()))); } } @@ -133,10 +138,10 @@ namespace Coscine.Api.Tree.Controllers { var objectMetaInfo = new ObjectMetaInfo { - Name = x.Key.Substring(x.Key.LastIndexOf("/") + 1), + Name = x.Key[(x.Key.LastIndexOf("/") + 1)..], Path = x.Key, Size = (int)x.BodyBytes, - Kind = x.Key.Substring(x.Key.LastIndexOf(".") + 1), + Kind = x.Key[(x.Key.LastIndexOf(".") + 1)..], Provider = resource.Type.DisplayName }; var objectMetaInfoReturnObject = new ObjectMetaInfoReturnObject(objectMetaInfo, _blobApiLink, resource.Id.ToString()); @@ -170,10 +175,14 @@ namespace Coscine.Api.Tree.Controllers }; return result; }))) - )) - ); + )) + ); + + if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics) && path != "/") + { + LogAnalyticsViewMd(_projectResourceModel.GetProjectForResource(resource.Id).Value, resource.Id, path, user, GetMetadataCompleteness(metadataCount, resource)); + } - LogAnalytics("View MD", resourceId, path, user); return Json(jObject); } catch @@ -213,10 +222,11 @@ namespace Coscine.Api.Tree.Controllers return Forbid("User is no project member!"); } - if (resource.ApplicationProfile[resource.ApplicationProfile.Length - 1] != '/') + if (resource.ApplicationProfile[^1] != '/') { resource.ApplicationProfile += '/'; } + json[graphNameUri.AbsoluteUri]["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"] = new JArray { new JObject @@ -282,16 +292,25 @@ namespace Coscine.Api.Tree.Controllers { return BadRequest("Data has the wrong format!"); } + // store the data if (_rdfStoreConnector.HasGraph(graphNameUri)) { _rdfStoreConnector.ClearGraph(graphNameUri); - LogAnalytics("Update MD", resourceId, path, user); + + if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics)) + { + LogAnalyticsUpdateMd(_projectResourceModel.GetProjectForResource(resource.Id).Value, resource.Id, path, user, GetMetadataCompleteness(graph.Triples.Count, resource)); + } } else { _rdfStoreConnector.CreateNamedGraph(graphNameUri); - LogAnalytics("Upload MD", resourceId, path, user); + + if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics)) + { + LogAnalyticsUploadMd(_projectResourceModel.GetProjectForResource(resource.Id).Value, resource.Id, path, user, GetMetadataCompleteness(graph.Triples.Count, resource)); + } } // BaseUri must be set for the sparql query @@ -351,25 +370,67 @@ namespace Coscine.Api.Tree.Controllers return null; } - /// <summary> - /// Log analytics - /// </summary> - /// <param name="operation">Operation</param> - /// <param name="resourceId">Resource of the id</param> - /// <param name="filename">Name of the file</param> - /// <param name="user">User object</param> - private void LogAnalytics(string operation, string resourceId, string filename, User user) + private void LogAnalyticsViewMd(Guid projectId, Guid resourceId, string filename, User user, string metadataCompletness) { - if (CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Analytics)) + var analyticsLogObject = new AnalyticsLogObject { - _analyticsLogObject.Type = "Action"; - _analyticsLogObject.Operation = operation; - _analyticsLogObject.FileId = resourceId + "/" + HttpUtility.UrlDecode(filename).Substring(1); - _analyticsLogObject.ResourceId = resourceId; - _analyticsLogObject.ProjectId = _projectResourceModel.GetProjectForResource(new Guid(resourceId)).ToString(); - _analyticsLogObject.RoleId = _projectRoleModel.GetGetUserRoleForProject(new Guid(_analyticsLogObject.ProjectId), user.Id).ToString(); - _coscineLogger.AnalyticsLog(_analyticsLogObject); - } + Type = "Action", + Operation = "View MD", + RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(), + ProjectId = projectId.ToString(), + ResourceId = resourceId.ToString(), + FileId = resourceId.ToString() + "/" + HttpUtility.UrlDecode(filename)[1..], + ApplicationsProfile = _resourceModel.CreateReturnObjectFromDatabaseObject(_resourceModel.GetById(resourceId)).ApplicationProfile, + MetadataCompleteness = metadataCompletness, + }; + + _coscineLogger.AnalyticsLog(analyticsLogObject); + } + + private void LogAnalyticsUploadMd(Guid projectId, Guid resourceId, string filename, User user, string metadataCompletness) + { + var analyticsLogObject = new AnalyticsLogObject + { + Type = "Action", + Operation = "Upload MD", + RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(), + ProjectId = projectId.ToString(), + ResourceId = resourceId.ToString(), + FileId = resourceId.ToString() + "/" + HttpUtility.UrlDecode(filename)[1..], + ApplicationsProfile = _resourceModel.CreateReturnObjectFromDatabaseObject(_resourceModel.GetById(resourceId)).ApplicationProfile, + MetadataCompleteness = metadataCompletness, + }; + + _coscineLogger.AnalyticsLog(analyticsLogObject); + } + + private void LogAnalyticsUpdateMd(Guid projectId, Guid resourceId, string filename, User user, string metadataCompletness) + { + var analyticsLogObject = new AnalyticsLogObject + { + Type = "Action", + Operation = "Update MD", + RoleId = _projectRoleModel.GetGetUserRoleForProject(projectId, user.Id).ToString(), + ProjectId = projectId.ToString(), + ResourceId = resourceId.ToString(), + FileId = resourceId.ToString() + "/" + HttpUtility.UrlDecode(filename)[1..], + ApplicationsProfile = _resourceModel.CreateReturnObjectFromDatabaseObject(_resourceModel.GetById(resourceId)).ApplicationProfile, + MetadataCompleteness = metadataCompletness, + }; + + _coscineLogger.AnalyticsLog(analyticsLogObject); + } + + private string GetMetadataCompleteness(int metadataCount, Resource resource) + { + var shapesGraph = _rdfStoreConnector.GetGraph(resource.ApplicationProfile); + var nodeFactory = new NodeFactory(); + var uriNode = (UriNode)nodeFactory.CreateUriNode(new Uri(_shaclPropertyUrl)); + + var total = shapesGraph.GetTriplesWithPredicate(uriNode).Count(); + var present = metadataCount; + + return $"{present}/{total}"; } } } diff --git a/src/Tree/Tree.csproj b/src/Tree/Tree.csproj index b475ad4e81f624c54384bbfb20dbea10b236c142..3580436b75f0a683f7575d633d9d56057d7cf6db 100644 --- a/src/Tree/Tree.csproj +++ b/src/Tree/Tree.csproj @@ -18,6 +18,8 @@ </PropertyGroup> <ItemGroup> <PackageReference Include="Coscine.ApiCommons" Version="2.*-*" /> + <PackageReference Include="Coscine.Database" Version="2.*-*" /> + <PackageReference Include="Coscine.Logging" Version="2.*-*" /> <PackageReference Include="Coscine.Metadata" Version="2.*-*" /> <PackageReference Include="Coscine.ResourceLoader" Version="2.*-*" /> <PackageReference Include="Coscine.WaterbutlerHelper" Version="2.*-*" />