Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create statistics page #933

Merged
merged 6 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Example properties:

tree:fragmentationStrategy [
a tree:GeospatialFragmentation ;
tree:maxZoom "15" ;
tree:maxZoom 15 ;
tree:fragmentationPath <http://www.opengis.net/ont/geosparql#asWKT> ;
] .
```
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.statistics.service;

public class StatisticsConstants {
private StatisticsConstants() {
}

public static final String DCT = "http://purl.org/dc/terms/";
public static final String PROP_SERVERNAME = DCT + "title";

public static final String SERVERNAME = "server name";

public static final String INGESTED_COUNT = "member count";

public static final String CURRENT_COUNT = "currently retained member count";

public static final String LDESES = "LDESes";

public static final String NAME = "name";

public static final String VIEWS = "views";

public static final String FRAGMENTATIONS = "fragmentations";

public static final String FRAGMENT_PROGRESS = "fragmentation progress";

public static final String PROPERTIES = "properties";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.statistics.service;

import org.apache.jena.atlas.json.JsonObject;

public interface StatisticsService {
JsonObject getMetrics();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.statistics.service;

import be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.dcat.dcatserver.repositories.DcatServerRepository;
import be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.eventstream.repository.EventStreamRepository;
import be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.view.repository.ViewRepository;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.model.EventStream;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.model.FragmentationConfig;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.model.ViewName;
import be.vlaanderen.informatievlaanderen.ldes.server.domain.model.ViewSpecification;
import be.vlaanderen.informatievlaanderen.ldes.server.fragmentation.entities.FragmentSequence;
import be.vlaanderen.informatievlaanderen.ldes.server.fragmentation.repository.FragmentSequenceRepository;
import be.vlaanderen.informatievlaanderen.ldes.server.ingest.repositories.MemberRepository;
import org.apache.jena.atlas.json.JsonArray;
import org.apache.jena.atlas.json.JsonObject;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Statement;
import org.springframework.stereotype.Service;

import java.util.Objects;

import static be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.statistics.service.StatisticsConstants.*;
import static org.apache.jena.rdf.model.ResourceFactory.createProperty;

@Service
public class StatisticsServiceImpl implements StatisticsService {

private final DcatServerRepository dcatServerRepository;
private final MemberRepository memberRepository;
private final EventStreamRepository eventStreamRepository;
private final ViewRepository viewRepository;
private final FragmentSequenceRepository fragmentSequenceRepository;

public StatisticsServiceImpl(DcatServerRepository dcatServerRepository, MemberRepository memberRepository,
EventStreamRepository eventStreamRepository, ViewRepository viewRepository,
FragmentSequenceRepository fragmentSequenceRepository) {
this.dcatServerRepository = dcatServerRepository;
this.memberRepository = memberRepository;
this.eventStreamRepository = eventStreamRepository;
this.viewRepository = viewRepository;
this.fragmentSequenceRepository = fragmentSequenceRepository;
}

@Override
public JsonObject getMetrics() {
JsonObject json = new JsonObject();
dcatServerRepository.findSingleDcatServer()
.ifPresent(dcatServer -> dcatServer.getDcat()
.listStatements(null, createProperty(PROP_SERVERNAME), (RDFNode) null)
.forEach(statement -> json.put(SERVERNAME + getLanguage(statement), statement.getString())));
json.put(INGESTED_COUNT, memberRepository.getTotalSequence());
json.put(CURRENT_COUNT, memberRepository.getMemberCount());

JsonArray eventStreamJsons = new JsonArray();
eventStreamRepository.retrieveAllEventStreams().stream().map(this::getLdesJson).forEach(eventStreamJsons::add);
json.put(LDESES, eventStreamJsons);
return json;
}

private String getLanguage(Statement statement) {
return Objects.equals(statement.getLanguage(), "") ? "" : " " + statement.getLanguage();
}

private JsonObject getLdesJson(EventStream eventStream) {
JsonObject ldesJson = new JsonObject();
ldesJson.put(NAME, eventStream.getCollection());

long ingestedMembers = memberRepository.getSequenceForCollection(eventStream.getCollection());
ldesJson.put(INGESTED_COUNT, ingestedMembers);
ldesJson.put(CURRENT_COUNT, memberRepository.getMemberCountOfCollection(eventStream.getCollection()));

JsonArray viewJsons = new JsonArray();
viewRepository.retrieveAllViewsOfCollection(eventStream.getCollection()).stream()
.map(view -> getViewJson(view, ingestedMembers)).forEach(viewJsons::add);
ldesJson.put(VIEWS, viewJsons);
return ldesJson;
}

private JsonObject getViewJson(ViewSpecification view, long ingestedMembers) {
JsonObject viewJson = new JsonObject();
viewJson.put(NAME, view.getName().asString());

JsonArray fragmentationJsons = new JsonArray();
view.getFragmentations().stream().map(this::fragmentationConfigToJson).forEach(fragmentationJsons::add);
viewJson.put(FRAGMENTATIONS, fragmentationJsons);

viewJson.put(FRAGMENT_PROGRESS, calculateFragmentationProgress(view.getName(), ingestedMembers));
return viewJson;
}

private long calculateFragmentationProgress(ViewName viewName, long ingestedMembers) {
long lastProcessed = fragmentSequenceRepository.findLastProcessedSequence(viewName)
.map(FragmentSequence::sequenceNr).orElse(0L);
return ingestedMembers == 0 ? 0
: Math.round((1 - (ingestedMembers - (double) lastProcessed) / ingestedMembers) * 100);
}

private JsonObject fragmentationConfigToJson(FragmentationConfig config) {
JsonObject fragmentationJson = new JsonObject();
fragmentationJson.put(NAME, config.getName());

JsonObject propJsons = new JsonObject();
config.getConfig().forEach(propJsons::put);
fragmentationJson.put(PROPERTIES, propJsons);
return fragmentationJson;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package be.vlaanderen.informatievlaanderen.ldes.server.admin.rest.controllers;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;

@SuppressWarnings("java:S2479") // whitespace needed for examples
@Tag(name = "Statistics")
public interface OpenApiStatisticsController {

@Operation(summary = "Retrieve json with statistics of the LDES server")
@ApiResponse(responseCode = "200", content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = String.class), examples = @ExampleObject(value = """
{
"ingested member count": 4018,
"current member count": 4018,
"LDESes": [
{
"name": "mobility-hindrances",
"ingested member count": 4018,
"current member count": 4018,
Comment on lines +23 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frankly, to an outsider, this might be quite confusing..
The difference between Ingested members and current member count is only something for internal purposes, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, ingested might be beter as historical member count? or something like that

"views": [
{
"name": "mobility-hindrances/timebased",
"fragmentations": [
{
"name": "HierarchicalTimeBasedFragmentation",
"properties": {
"maxGranularity": "minute",
"fragmentationPath": "http://www.w3.org/ns/prov#generatedAtTime"
}
}
],
"fragmentation progress": 100
}
]
}
]
}
""")),
})
String getStatistics();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package be.vlaanderen.informatievlaanderen.ldes.server.admin.rest.controllers;

import be.vlaanderen.informatievlaanderen.ldes.server.admin.domain.statistics.service.StatisticsService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/admin/api/v1/statistics")
public class StatisticsController implements OpenApiStatisticsController {

private final StatisticsService statisticsService;

public StatisticsController(StatisticsService statisticsService) {
this.statisticsService = statisticsService;
}

@GetMapping(produces = { "application/json" })
public String getStatistics() {
return statisticsService.getMetrics().toString();
}
}
2 changes: 2 additions & 0 deletions ldes-server-admin/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

// LDES dependencies
requires ldes.domain;
requires ldes.ingest.domain;
requires ldes.fragmentation.domain;

// external dependencies
requires spring.boot;
Expand Down
Loading
Loading