Skip to content

Commit

Permalink
Merge branch 'master' into remove-partial-publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
gunnarvelle committed Jun 26, 2024
2 parents 243b1d2 + 4e8516a commit 838967f
Show file tree
Hide file tree
Showing 29 changed files with 801 additions and 37 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,12 @@
</excludeClasses>
<customTypeMappings>
<mapping>java.net.URI:string</mapping>
<mapping>no.ndla.taxonomy.domain.UpdateOrDelete[T]:T</mapping>
</customTypeMappings>
<nullabilityDefinition>nullAndUndefinedInlineUnion</nullabilityDefinition>
<nullableAnnotations>
<annotation>no.ndla.taxonomy.domain.NullOrUndefined</annotation>
</nullableAnnotations>
<mapDate>asString</mapDate>
<mapMap>asRecord</mapMap>
<noEslintDisable>true</noEslintDisable>
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/no/ndla/taxonomy/domain/Grade.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Part of NDLA taxonomy-api
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

package no.ndla.taxonomy.domain;

import com.fasterxml.jackson.annotation.JsonValue;

public enum Grade {
One(1),
Two(2),
Three(3),
Four(4),
Five(5);

private final int value;

Grade(int value) {
this.value = value;
}

@JsonValue
public int toInt() {
return value;
}

public static Grade fromInt(Integer value) {
return switch (value) {
case 1 -> One;
case 2 -> Two;
case 3 -> Three;
case 4 -> Four;
case 5 -> Five;
default -> throw new IllegalArgumentException("Unexpected grade value: " + value + ". Must be 1-5.");
};
}
}
36 changes: 36 additions & 0 deletions src/main/java/no/ndla/taxonomy/domain/GradeAverage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Part of NDLA taxonomy-api
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

package no.ndla.taxonomy.domain;

import java.util.Collection;
import java.util.Optional;

public class GradeAverage {
public GradeAverage(double averageValue, int count) {
this.averageValue = averageValue;
this.count = count;
}

double averageValue;
int count;

public static GradeAverage fromGrades(Collection<Optional<Grade>> grades) {
var existing = grades.stream().flatMap(Optional::stream).toList();
var count = existing.size();
var avg = existing.stream().mapToInt(Grade::toInt).average().orElse(0.0);
return new GradeAverage(avg, count);
}

public double getAverageValue() {
return averageValue;
}

public int getCount() {
return count;
}
}
24 changes: 24 additions & 0 deletions src/main/java/no/ndla/taxonomy/domain/GradeConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Part of NDLA taxonomy-api
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

package no.ndla.taxonomy.domain;

import jakarta.persistence.AttributeConverter;

public class GradeConverter implements AttributeConverter<Grade, Integer> {
@Override
public Integer convertToDatabaseColumn(Grade grade) {
if (grade == null) return null;
return grade.toInt();
}

@Override
public Grade convertToEntityAttribute(Integer integer) {
if (integer == null) return null;
return Grade.fromInt(integer);
}
}
97 changes: 97 additions & 0 deletions src/main/java/no/ndla/taxonomy/domain/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.transaction.annotation.Transactional;

@Entity
public class Node extends DomainObject implements EntityWithMetadata {
Expand Down Expand Up @@ -70,6 +71,19 @@ public class Node extends DomainObject implements EntityWithMetadata {
@Column(name = "contexts", columnDefinition = "jsonb")
private Set<TaxonomyContext> contexts = new HashSet<>();

@Column(name = "quality_evaluation")
@Convert(converter = GradeConverter.class)
private Grade qualityEvaluation;

@Column(name = "quality_evaluation_comment")
private String qualityEvaluationComment;

@Column(name = "child_quality_evaluation_average")
private Double childQualityEvaluationAverage;

@Column(name = "child_quality_evaluation_count")
private int childQualityEvaluationCount;

// Needed for hibernate
public Node() {}

Expand All @@ -88,6 +102,8 @@ public Node(Node node, boolean keepPublicId) {
this.nodeType = node.getNodeType();
this.ident = node.getIdent();
this.context = node.isContext();
this.qualityEvaluation = node.getQualityEvaluationGrade().orElse(null);
this.qualityEvaluationComment = node.getQualityEvaluationComment().orElse(null);

if (keepPublicId) {
setPublicId(node.getPublicId());
Expand All @@ -113,6 +129,87 @@ public Node(Node node, boolean keepPublicId) {
setName(node.getName());
}

public Optional<Grade> getQualityEvaluationGrade() {
return Optional.ofNullable(qualityEvaluation);
}

public Optional<String> getQualityEvaluationComment() {
return Optional.ofNullable(qualityEvaluationComment);
}

public Optional<String> getQualityEvaluationNote() {
return Optional.ofNullable(qualityEvaluationComment);
}

public Optional<GradeAverage> getChildQualityEvaluationAverage() {
return Optional.ofNullable(this.childQualityEvaluationAverage)
.map(avg -> new GradeAverage(avg, this.childQualityEvaluationCount));
}

public void updateChildQualityEvaluationAverage(Optional<Grade> previousGrade, Optional<Grade> newGrade) {
var childAvg = getChildQualityEvaluationAverage();
if (childAvg.isEmpty()) {
newGrade.ifPresent(ng -> {
this.childQualityEvaluationAverage = (double) ng.toInt();
this.childQualityEvaluationCount = 1;
});
return;
}

var avg = childAvg.get();
if (previousGrade.isEmpty() && newGrade.isEmpty()) return;
else if (previousGrade.isEmpty()) { // New grade is present
var newCount = avg.getCount() + 1;
var newSum = ((avg.averageValue * avg.getCount()) + newGrade.get().toInt());
var newAverage = newSum / newCount;
this.childQualityEvaluationCount = newCount;
this.childQualityEvaluationAverage = newAverage;
} else if (newGrade.isEmpty()) { // Previous grade is present
var newCount = avg.getCount() - 1;
var oldSum = (avg.averageValue * avg.getCount());
var newSum = oldSum - previousGrade.get().toInt();
var newAverage = newSum / newCount;
this.childQualityEvaluationCount = newCount;
this.childQualityEvaluationAverage = newAverage;
} else { // Both grades are present
var oldSum = avg.averageValue * avg.getCount();
var newSum = oldSum - previousGrade.get().toInt() + newGrade.get().toInt();
var newAverage = newSum / avg.getCount();
this.childQualityEvaluationCount = avg.getCount();
this.childQualityEvaluationAverage = newAverage;
}
}

@Transactional
public void updateEntireAverageTree() {
var allChildGrades = getChildGradesRecursively();
var gradeAverage = GradeAverage.fromGrades(allChildGrades);

if (gradeAverage.count > 0) {
this.childQualityEvaluationAverage = gradeAverage.averageValue;
this.childQualityEvaluationCount = gradeAverage.count;
}
}

public List<Optional<Grade>> getChildGradesRecursively() {
var children = getChildNodes();
return children.stream()
.flatMap(child -> {
ArrayList<Optional<Grade>> childGrades = new ArrayList<>(child.getChildGradesRecursively());
childGrades.add(child.getQualityEvaluationGrade());
return childGrades.stream();
})
.toList();
}

public void setQualityEvaluation(Grade qualityEvaluation) {
this.qualityEvaluation = qualityEvaluation;
}

public void setQualityEvaluationComment(Optional<String> qualityEvaluationComment) {
this.qualityEvaluationComment = qualityEvaluationComment.orElse(null);
}

public String getPathPart() {
return "/" + getPublicId().getSchemeSpecificPart();
}
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/no/ndla/taxonomy/domain/NullOrUndefined.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Part of NDLA taxonomy-api
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

package no.ndla.taxonomy.domain;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** An annotation to indicate that a field can be null or undefined in the generated typescript. */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
public @interface NullOrUndefined {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Part of NDLA taxonomy-api
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

package no.ndla.taxonomy.domain;

import com.fasterxml.jackson.databind.JsonNode;
import java.util.Optional;
import no.ndla.taxonomy.service.dtos.QualityEvaluationDTO;

public class QualityEvaluationDTODeserializer extends UpdateOrDelete.Deserializer<QualityEvaluationDTO> {
private Optional<String> getNote(JsonNode node) {
var hasNote = node.has("note");
var noteNode = node.get("note");
if (hasNote && noteNode.isTextual()) {
return Optional.of(node.get("note").asText());
}
return Optional.empty();
}

@Override
protected QualityEvaluationDTO deserializeInner(JsonNode node) {
var gradeInt = node.get("grade").asInt();
var grade = Grade.fromInt(gradeInt);
var note = getNote(node);
return new QualityEvaluationDTO(grade, note);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Part of NDLA taxonomy-api
* Copyright (C) 2024 NDLA
*
* See LICENSE
*/

package no.ndla.taxonomy.domain;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import no.ndla.taxonomy.service.dtos.QualityEvaluationDTO;

public class QualityEvaluationDTOSerializer extends UpdateOrDelete.Serializer<QualityEvaluationDTO> {
@Override
protected void serializeInner(
QualityEvaluationDTO value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeObject(value);
}
}
Loading

0 comments on commit 838967f

Please sign in to comment.