Skip to content

Commit

Permalink
[kbss-cvut/termit-ui#553] Allow retrieving list of languages used in …
Browse files Browse the repository at this point in the history
…vocabulary.
  • Loading branch information
ledsoft committed Nov 7, 2024
1 parent 8c8a15f commit 4db5145
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 65 deletions.
162 changes: 98 additions & 64 deletions src/main/java/cz/cvut/kbss/termit/persistence/dao/VocabularyDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,13 @@ public Vocabulary update(Vocabulary entity) {
/**
* Forcefully removes the specified vocabulary.
* <p>
* This deletes the whole graph of the vocabulary, all terms in the vocabulary's glossary and then removes the vocabulary itself. Extreme caution
* should be exercised when using this method. All relevant data, including documents and files, will be dropped.
* This deletes the whole graph of the vocabulary, all terms in the vocabulary's glossary and then removes the
* vocabulary itself. Extreme caution should be exercised when using this method. All relevant data, including
* documents and files, will be dropped.
* <p>
* Publishes {@link VocabularyWillBeRemovedEvent} before the actual removal to allow other services to clean up related resources (e.g., delete the document).
* Publishes {@link VocabularyWillBeRemovedEvent} before the actual removal to allow other services to clean up
* related resources (e.g., delete the document).
*
* @param entity The vocabulary to delete
*/
@ModifiesData
Expand All @@ -236,9 +239,9 @@ public void remove(Vocabulary entity) {
* <p>
* Forcefully removes the specified vocabulary.
* <p>
* This deletes all terms in the vocabulary's glossary and then removes the vocabulary itself.
* Extreme caution should be exercised when using this method,
* as it does not check for any references or usage and just drops all the relevant data.
* This deletes all terms in the vocabulary's glossary and then removes the vocabulary itself. Extreme caution
* should be exercised when using this method, as it does not check for any references or usage and just drops all
* the relevant data.
* <p>
* The document is not removed.
*/
Expand All @@ -248,27 +251,27 @@ public void removeVocabularyKeepDocument(Vocabulary entity) {

/**
* <p>
* Does not publish the {@link VocabularyWillBeRemovedEvent}.<br>
* You should use {@link #remove(Vocabulary)} instead.
* Does not publish the {@link VocabularyWillBeRemovedEvent}.<br> You should use {@link #remove(Vocabulary)}
* instead.
* <p>
* Forcefully removes the specified vocabulary.
* <p>
* This deletes all terms in the vocabulary's glossary and then removes the vocabulary itself. Extreme caution
* should be exercised when using this method, as it does not check for any references or usage and just drops all
* the relevant data.
* @param entity The vocabulary to delete
* @param dropGraph if false,
* executes {@code src/main/resources/query/remove/removeGlossaryTerms.ru} removing terms,
* their relations, model, glossary and vocabulary itself, keeps the document.
* When true, the whole vocabulary graph is dropped.
*
* @param entity The vocabulary to delete
* @param dropGraph if false, executes {@code src/main/resources/query/remove/removeGlossaryTerms.ru} removing
* terms, their relations, model, glossary and vocabulary itself, keeps the document. When true,
* the whole vocabulary graph is dropped.
*/
private void removeVocabulary(Vocabulary entity, boolean dropGraph) {
Objects.requireNonNull(entity);
LOG.debug("Forcefully removing vocabulary {} and all its contents.", entity);
try {
final URI vocabularyContext = contextMapper.getVocabularyContext(entity.getUri());

if(dropGraph) {
if (dropGraph) {
// drops whole named graph
em.createNativeQuery("DROP GRAPH ?context")
.setParameter("context", vocabularyContext)
Expand Down Expand Up @@ -317,8 +320,8 @@ public Optional<Glossary> findGlossary(URI uri) {
}

/**
* Checks whether terms from the {@code subjectVocabulary} reference (as parent terms) any terms from the {@code
* targetVocabulary}.
* Checks whether terms from the {@code subjectVocabulary} reference (as parent terms) any terms from the
* {@code targetVocabulary}.
*
* @param subjectVocabulary Subject vocabulary identifier
* @param targetVocabulary Target vocabulary identifier
Expand Down Expand Up @@ -395,33 +398,35 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
* Gets content change records of the specified vocabulary.
*
* @param vocabulary Vocabulary whose content changes to get
* @param pageReq Specification of the size and number of the page to return
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
Objects.requireNonNull(vocabulary);
return createDetailedContentChangesQuery(vocabulary, pageReq).getResultList();
}

private TypedQuery<AbstractChangeRecord> createDetailedContentChangesQuery(Vocabulary vocabulary, Pageable pageReq) {
private TypedQuery<AbstractChangeRecord> createDetailedContentChangesQuery(Vocabulary vocabulary,
Pageable pageReq) {
return em.createNativeQuery("""
SELECT ?record WHERE {
?term ?inVocabulary ?vocabulary ;
a ?termType .
?record a ?changeRecord ;
?relatesTo ?term ;
?hasTime ?timestamp .
OPTIONAL { ?record ?hasChangedAttribute ?attribute . }
} ORDER BY DESC(?timestamp) ?attribute
""", AbstractChangeRecord.class)
SELECT ?record WHERE {
?term ?inVocabulary ?vocabulary ;
a ?termType .
?record a ?changeRecord ;
?relatesTo ?term ;
?hasTime ?timestamp .
OPTIONAL { ?record ?hasChangedAttribute ?attribute . }
} ORDER BY DESC(?timestamp) ?attribute
""", AbstractChangeRecord.class)
.setParameter("inVocabulary",
URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_je_pojmem_ze_slovniku))
URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_je_pojmem_ze_slovniku))
.setParameter("vocabulary", vocabulary)
.setParameter("termType", URI.create(SKOS.CONCEPT))
.setParameter("termType", URI.create(SKOS.CONCEPT))
.setParameter("changeRecord", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_c_zmena))
.setParameter("relatesTo", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_zmenenou_entitu))
.setParameter("hasTime", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_datum_a_cas_modifikace))
.setParameter("hasChangedAttribute", URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_zmeneny_atribut))
.setParameter("hasChangedAttribute",
URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_ma_zmeneny_atribut))
.setFirstResult((int) pageReq.getOffset())
.setMaxResults(pageReq.getPageSize());
}
Expand Down Expand Up @@ -576,16 +581,17 @@ public List<RdfsStatement> getVocabularyRelations(Vocabulary vocabulary, Collect

try {
return em.createNativeQuery("""
SELECT DISTINCT ?object ?relation ?subject {
?object a ?vocabularyType ;
?relation ?subject .
FILTER(?object != ?subject) .
FILTER(?relation NOT IN (?excluded)) .
} ORDER BY ?object ?relation
""", "RDFStatement")
SELECT DISTINCT ?object ?relation ?subject {
?object a ?vocabularyType ;
?relation ?subject .
FILTER(?object != ?subject) .
FILTER(?relation NOT IN (?excluded)) .
} ORDER BY ?object ?relation
""", "RDFStatement")
.setParameter("subject", vocabularyUri)
.setParameter("excluded", excludedRelations)
.setParameter("vocabularyType", URI.create(EntityToOwlClassMapper.getOwlClassForEntity(Vocabulary.class)))
.setParameter("excluded", excludedRelations)
.setParameter("vocabularyType",
URI.create(EntityToOwlClassMapper.getOwlClassForEntity(Vocabulary.class)))
.getResultList();
} catch (RuntimeException e) {
throw new PersistenceException(e);
Expand All @@ -603,31 +609,31 @@ public List<RdfsStatement> getTermRelations(Vocabulary vocabulary) {

try {
return em.createNativeQuery("""
SELECT DISTINCT ?object ?relation ?subject WHERE {
?term a ?termType;
?inVocabulary ?vocabulary .
{
?term ?relation ?secondTerm .
?secondTerm a ?termType;
?inVocabulary ?secondVocabulary .
BIND(?term as ?object)
BIND(?secondTerm as ?subject)
} UNION {
?secondTerm ?relation ?term .
?secondTerm a ?termType;
?inVocabulary ?secondVocabulary .
BIND(?secondTerm as ?object)
BIND(?term as ?subject)
}
FILTER(?relation IN (?deniedRelations))
FILTER(?object != ?subject)
FILTER(?secondVocabulary != ?vocabulary)
} ORDER by ?object ?relation ?subject
""", "RDFStatement"
SELECT DISTINCT ?object ?relation ?subject WHERE {
?term a ?termType;
?inVocabulary ?vocabulary .
{
?term ?relation ?secondTerm .
?secondTerm a ?termType;
?inVocabulary ?secondVocabulary .
BIND(?term as ?object)
BIND(?secondTerm as ?subject)
} UNION {
?secondTerm ?relation ?term .
?secondTerm a ?termType;
?inVocabulary ?secondVocabulary .
BIND(?secondTerm as ?object)
BIND(?term as ?subject)
}
FILTER(?relation IN (?deniedRelations))
FILTER(?object != ?subject)
FILTER(?secondVocabulary != ?vocabulary)
} ORDER by ?object ?relation ?subject
""", "RDFStatement"
).setMaxResults(DEFAULT_PAGE_SIZE)
.setParameter("termType", termType)
.setParameter("inVocabulary", inVocabulary)
Expand All @@ -638,4 +644,32 @@ public List<RdfsStatement> getTermRelations(Vocabulary vocabulary) {
throw new PersistenceException(e);
}
}

/**
* Returns the list of all distinct languages (language tags) used by terms in the specified vocabulary.
*
* @param vocabularyUri Vocabulary identifier
* @return List of distinct languages
*/
public List<String> getLanguages(URI vocabularyUri) {
Objects.requireNonNull(vocabularyUri);
try {
return em.createNativeQuery("""
SELECT DISTINCT ?lang WHERE {
?x a ?type ;
?inVocabulary ?vocabulary ;
?labelProp ?label .
BIND (LANG(?label) as ?lang)
}
""", String.class)
.setParameter("type", URI.create(SKOS.CONCEPT))
.setParameter("inVocabulary",
URI.create(cz.cvut.kbss.termit.util.Vocabulary.s_p_je_pojmem_ze_slovniku))
.setParameter("vocabulary", vocabularyUri)
.setParameter("labelProp", URI.create(SKOS.PREF_LABEL))
.getResultList();
} catch (RuntimeException e) {
throw new PersistenceException(e);
}
}
}
16 changes: 16 additions & 0 deletions src/main/java/cz/cvut/kbss/termit/rest/VocabularyController.java
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,22 @@ public List<AbstractChangeRecord> getDetailedHistoryOfContent(
return vocabularyService.getDetailedHistoryOfContent(vocabulary, pageReq);
}

@Operation(security = {@SecurityRequirement(name = "bearer-key")},
description = "Gets a list of languages used in the vocabulary.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "List of languages.")
})
@GetMapping(value = "/{localName}/languages", produces = {MediaType.APPLICATION_JSON_VALUE, JsonLd.MEDIA_TYPE})
public List<String> getLanguages(
@Parameter(description = ApiDoc.ID_LOCAL_NAME_DESCRIPTION,
example = ApiDoc.ID_LOCAL_NAME_EXAMPLE) @PathVariable String localName,
@Parameter(description = ApiDoc.ID_NAMESPACE_DESCRIPTION,
example = ApiDoc.ID_NAMESPACE_EXAMPLE) @RequestParam(name = QueryParams.NAMESPACE,
required = false) Optional<String> namespace) {
final URI vocabularyUri = resolveVocabularyUri(localName, namespace);
return vocabularyService.getLanguages(vocabularyUri);
}

@Operation(security = {@SecurityRequirement(name = "bearer-key")},
description = "Updates metadata of vocabulary with the specified identifier.")
@ApiResponses({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public List<AggregatedChangeInfo> getChangesOfContent(Vocabulary vocabulary) {
* Gets content change records of the specified vocabulary.
*
* @param vocabulary Vocabulary whose content changes to get
* @param pageReq Specification of the size and number of the page to return
* @param pageReq Specification of the size and number of the page to return
* @return List of change records, ordered by date in descending order
*/
public List<AbstractChangeRecord> getDetailedHistoryOfContent(Vocabulary vocabulary, Pageable pageReq) {
Expand Down Expand Up @@ -522,6 +522,17 @@ public AccessLevel getAccessLevel(Vocabulary vocabulary) {
return authorizationService.getAccessLevel(vocabulary);
}

/**
* Gets the list of languages used in the specified vocabulary.
*
* @param vocabularyUri Vocabulary identifier
* @return List of languages
*/
@PreAuthorize("@vocabularyAuthorizationService.canRead(#vocabularyUri)")
public List<String> getLanguages(URI vocabularyUri) {
return repositoryService.getLanguages(vocabularyUri);
}

@Override
public void setApplicationEventPublisher(@Nonnull ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,15 @@ public Vocabulary findVersionValidAt(Vocabulary vocabulary, Instant at) {
public PrefixDeclaration resolvePrefix(URI vocabularyUri) {
return vocabularyDao.resolvePrefix(vocabularyUri);
}

/**
* Returns the list of all distinct languages (language tags) used by terms in the specified vocabulary.
*
* @param vocabularyUri Vocabulary identifier
* @return List of distinct languages
*/
@Transactional(readOnly = true)
public List<String> getLanguages(URI vocabularyUri) {
return vocabularyDao.getLanguages(vocabularyUri);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import static cz.cvut.kbss.termit.environment.util.ContainsSameEntities.containsSameEntities;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
Expand Down Expand Up @@ -927,4 +928,23 @@ void getAnyExternalRelationsReturnsTermsWithBothRelations(URI termRelation) {
}
});
}

@Test
void getLanguagesReturnsDistinctLanguagesUsedByVocabularyTerms() {
final Vocabulary vocabulary = Generator.generateVocabularyWithId();
final Term term = Generator.generateTermWithId(vocabulary.getUri());
final Term term2 = Generator.generateTermWithId(vocabulary.getUri());
term2.getLabel().set("cs", "Název v češtině");
transactional(() -> {
em.persist(vocabulary, descriptorFor(vocabulary));
em.persist(term, descriptorFactory.termDescriptor(term));
em.persist(term2, descriptorFactory.termDescriptor(term2));
Generator.addTermInVocabularyRelationship(term, vocabulary.getUri(), em);
Generator.addTermInVocabularyRelationship(term2, vocabulary.getUri(), em);
});

final List<String> languages = sut.getLanguages(vocabulary.getUri());
assertEquals(2, languages.size());
assertThat(languages, hasItems(Environment.LANGUAGE, "cs"));
}
}
Loading

0 comments on commit 4db5145

Please sign in to comment.