Skip to content

Commit

Permalink
Merge pull request #224 from bounswe/wikidata_api
Browse files Browse the repository at this point in the history
Wikidata api
  • Loading branch information
alitpc25 authored May 12, 2023
2 parents 2fae5ee + 221865f commit 493e831
Show file tree
Hide file tree
Showing 9 changed files with 428 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.a1.disasterresponse.controller;

import com.a1.disasterresponse.model.EntityData;
import com.a1.disasterresponse.repository.EntityRepository;
import com.a1.disasterresponse.service.WikidataService;
import kotlin.Pair;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@RestController
public class CategoryController {

private final WikidataService wikidataService;
private final EntityRepository relationRepository;

public CategoryController(WikidataService wikidataService, EntityRepository relationRepository) {
this.wikidataService = wikidataService;
this.relationRepository = relationRepository;
}

@GetMapping("/category/findEntity")
public ResponseEntity<EntityData.EntityRecord> findEntity(@RequestParam String query) {
try {
String id = wikidataService.searchForWikidataEntity(query);
EntityData data = wikidataService.getWikidataEntityFromId(id);
return new ResponseEntity<>(data.toRecord(), HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@GetMapping("/category/getCategoriesOf")
public ResponseEntity<List<EntityData.EntityRecord>> getCategoriesOf(@RequestParam String id) {
try {
EntityData entityData = wikidataService.getWikidataEntityFromId(id);
List<EntityData.EntityRecord> categories = new ArrayList<>();
for (String category : entityData.categories()) {
categories.add(wikidataService.getWikidataEntityFromId(category).toRecord());
}
return new ResponseEntity<>(categories, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@PostMapping("/category/saveEntity")
public ResponseEntity<Void> saveEntity(@RequestParam String id) {
try {
EntityData entityData = wikidataService.getWikidataEntityFromId(id);
relationRepository.save(entityData.toRecord());
return new ResponseEntity<>(HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

@GetMapping("/category/getEntities")
public ResponseEntity<List<EntityData.EntityRecord>> getSavedEntities() {
return new ResponseEntity<>(relationRepository.findAll(), HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.a1.disasterresponse.model;

import jakarta.persistence.*;

import java.util.List;
import java.util.Map;


public record EntityData(String id, Map<String, String> labels,
List<String> categories) {

public EntityRecord toRecord() {
return new EntityRecord(id, labels.getOrDefault("en", id));
}

@Entity
public static class EntityRecord {
@Id
public String id;

public String name;

public EntityRecord() {
}

public EntityRecord(String id, String name) {
this.id = id;
this.name = name;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.a1.disasterresponse.repository;

import com.a1.disasterresponse.model.EntityData;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.util.Pair;

public interface EntityRepository extends JpaRepository<EntityData.EntityRecord, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.a1.disasterresponse.service;

import com.a1.disasterresponse.model.EntityData;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class WikidataService {

private static final String WIKIDATA_BASE_URL = "https://www.wikidata.org/w/api.php";

public static final String WIKIDATA_SEARCH = "?action=wbsearchentities&format=json&language=en&type=item&continue=0&search=";
public static final String WIKIDATA_GET_ENTITY = "?action=wbgetentities&format=json&languages=en|tr&props=labels|info|claims&ids=";
private static final OkHttpClient client = new OkHttpClient();
private static final ObjectMapper mapper = new ObjectMapper();

public String searchForWikidataEntity(String query) throws IOException {
JsonNode rootNode = executeWikidataQuery(WIKIDATA_SEARCH + query);

return rootNode.get("search").get(0).get("id").asText();
}

public EntityData getWikidataEntityFromId(String id) throws IOException {
JsonNode entity = executeWikidataQuery(WIKIDATA_GET_ENTITY + id).get("entities").get(id);

Map<String, String> labels = new HashMap<>();

entity.get("labels")
.fields()
.forEachRemaining(e -> labels.put(
e.getValue().get("language").textValue(),
e.getValue().get("value").textValue()
));

List<String> supercategories = new ArrayList<>();
if (entity.get("claims").has("P279")) {
entity.get("claims")
.get("P279")
.elements()
.forEachRemaining(e -> supercategories.add(e
.get("mainsnak")
.get("datavalue")
.get("value")
.get("id")
.asText()));
}

return new EntityData(id, labels, supercategories);
}

private JsonNode executeWikidataQuery(String query) throws IOException {
Request request = new Request.Builder()
.url(WIKIDATA_BASE_URL + query)
.get()
.build();

try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
throw new IOException("Unexpected code " + response);
}

String body = response.body().string();

return mapper.readTree(body);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package com.a1.disasterresponse.controller;

import com.a1.disasterresponse.model.EntityData;
import com.a1.disasterresponse.repository.EntityRepository;
import com.a1.disasterresponse.service.WikidataService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.*;

class CategoryControllerTest {

private WikidataService wikidataService;
private EntityRepository relationRepository;

private CategoryController categoryController;

@BeforeEach
void setUp() {
wikidataService = Mockito.mock(WikidataService.class);
relationRepository = Mockito.mock(EntityRepository.class);
categoryController = new CategoryController(wikidataService, relationRepository);
}

@Test
void testIdOf() throws IOException {
//Mock wikimedia api call
when(wikidataService.searchForWikidataEntity("blanket")).thenReturn("Q5852");

// Call api
ResponseEntity<EntityData.EntityRecord> resp = categoryController.findEntity("blanket");

// Verify the response
assertEquals(HttpStatus.OK, resp.getStatusCode());
assertNotNull(resp.getBody());
assertEquals("Q5852", resp.getBody().id);

verify(wikidataService, times(1)).searchForWikidataEntity("blanket");
}

@Test
void testGetCategoriesOf_nocats() throws IOException {
EntityData blanketData = new EntityData(
"Q5852",
Map.of("en", "blanket", "tr", "battaniye"),
List.of());

when(wikidataService.getWikidataEntityFromId("Q5852")).thenReturn(blanketData);


ResponseEntity<List<EntityData.EntityRecord>> resp = categoryController.getCategoriesOf("Q5852");

// Verify the response
assertEquals(HttpStatus.OK, resp.getStatusCode());
assertNotNull(resp.getBody());
assertEquals(0, resp.getBody().size());

verify(wikidataService, times(1)).getWikidataEntityFromId(anyString());
}

@Test
void testGetCategoriesOf_one_cat() throws IOException {
EntityData blanketData = new EntityData(
"Q5852",
Map.of("en", "blanket", "tr", "battaniye"),
List.of("Q31808206"));

EntityData coveringData = new EntityData(
"Q31808206",
Map.of("en", "covering"),
List.of("Q31807746"));

when(wikidataService.getWikidataEntityFromId("Q5852")).thenReturn(blanketData);
when(wikidataService.getWikidataEntityFromId("Q31808206")).thenReturn(coveringData);


ResponseEntity<List<EntityData.EntityRecord>> resp = categoryController.getCategoriesOf("Q5852");

// Verify the response
assertEquals(HttpStatus.OK, resp.getStatusCode());
assertNotNull(resp.getBody());
assertEquals(List.of(coveringData.toRecord()), resp.getBody());

verify(wikidataService, times(2)).getWikidataEntityFromId(anyString());
}


@Test
void testGetCategoriesOf_two_cats() throws IOException {
EntityData blanketData = new EntityData(
"Q5852",
Map.of("en", "blanket", "tr", "battaniye"),
List.of("Q31808206", "Q31807746"));

EntityData coveringData = new EntityData(
"Q31808206",
Map.of("en", "covering"),
List.of());

EntityData furnishingData = new EntityData(
"Q31807746",
Map.of("en", "furnishing"),
List.of("Q10273457", "Q17537576", "Q8205328"));


when(wikidataService.getWikidataEntityFromId("Q5852")).thenReturn(blanketData);
when(wikidataService.getWikidataEntityFromId("Q31808206")).thenReturn(coveringData);
when(wikidataService.getWikidataEntityFromId("Q31807746")).thenReturn(furnishingData);

ResponseEntity<List<EntityData.EntityRecord>> resp = categoryController.getCategoriesOf("Q5852");

// Verify the response
assertEquals(HttpStatus.OK, resp.getStatusCode());
assertNotNull(resp.getBody());
assertEquals(List.of(coveringData.toRecord(), furnishingData.toRecord()), resp.getBody());

verify(wikidataService, times(3)).getWikidataEntityFromId(anyString());
}
}
2 changes: 2 additions & 0 deletions practice-app/docker-compose_dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ services:
- POSTGRES_PASSWORD=compose-postgres
networks:
- app_network
ports:
- "5432:5432"

frontend_dev:
container_name: frontend_dev
Expand Down
4 changes: 3 additions & 1 deletion practice-app/frontend/disasterresponse/src/App.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { Navbar, Container, Nav } from 'react-bootstrap';
import { FaHome, FaMap, FaDirections, FaCloud, FaImdb, FaWifi, FaLanguage} from 'react-icons/fa';
import {FaHome, FaMap, FaDirections, FaCloud, FaImdb, FaWifi, FaLanguage, FaWikipediaW} from 'react-icons/fa';
import DirectionsPage from "./pages/DirectionsApi/DirectionsPage";
import WeatherApiPage from './pages/WeatherApi/WeatherPage';
import GoogleGeocodePage from './pages/GoogleGeocodeApi/GoogleGeocodePage';
import ImdbPage from './pages/ImdbApi/ImdbPage';
import GeoJsPage from './pages/GeoJsApi/GeoJsPage';
import GoogleTranslationPage from './pages/TranslationApi/GoogleTranslationPage';
import WikidataPage from "./pages/WikidataApi/WikidataPage";

const navLinks = [
{ path: '/googleDirectionsPage', label: ' Google Directions API', component: DirectionsPage, icon: <FaDirections /> },
Expand All @@ -16,6 +17,7 @@ const navLinks = [
{ path: '/ImdbPage', label: ' IMDB API', component: ImdbPage, icon: <FaImdb /> },
{ path: '/geoJsPage', label: ' GeoJs API', component: GeoJsPage, icon: <FaWifi /> },
{ path: '/googleTranslationPage', label: ' Google Translation API', component: GoogleTranslationPage, icon: <FaLanguage /> },
{ path: '/wikidataPage', label: ' Wikidata Api', component: WikidataPage, icon: <FaWikipediaW /> },
// add the rest of APIs
];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.directionsDiv {
margin-top: 20px;
}

.formDiv {
width: 40vw;
margin: 10px auto;
}

.directionsDiv {
width: 40vw;
margin: 10px auto;
}
Loading

0 comments on commit 493e831

Please sign in to comment.