Merge branch '22-integrate-api-and-frontend' into 7-add-page-to-view-dataset-api

This commit is contained in:
Elias Schriefer 2024-07-05 17:18:09 +02:00
commit 922dfdd26d
27 changed files with 710 additions and 329 deletions

View File

@ -52,8 +52,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hsqldb</groupId> <groupId>com.h2database</groupId>
<artifactId>hsqldb</artifactId> <artifactId>h2</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -1,8 +1,13 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model; package de.uni_passau.fim.PADAS.group3.DataDash.Dataset;
import java.net.URL; import java.net.URL;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import de.uni_passau.fim.PADAS.group3.DataDash.category.Category;
import java.sql.Date; import java.sql.Date;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -11,6 +16,7 @@ import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType; import jakarta.persistence.GenerationType;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
@Entity @Entity
public class Dataset { public class Dataset {
@ -39,10 +45,15 @@ public class Dataset {
private int upvotes; private int upvotes;
private URL url; private URL url;
@Enumerated(EnumType.STRING)
private String licence;
private static final List<String> sortable = Arrays.asList("author", "title", "upvotes", "date");
@ManyToOne
private Category categorie; private Category categorie;
public Dataset(String title, String abst, String description, String author, URL url, Category categories, Type type) { public Dataset(String title, String abst, String description, String author, URL url, Category categories, Type type, String licence) {
this.raiting = 0; this.raiting = 0;
this.votes = 0; this.votes = 0;
@ -55,6 +66,7 @@ public class Dataset {
setCategorie(categories); setCategorie(categories);
setType(type); setType(type);
setUrl(url); setUrl(url);
setLicence(licence);
} }
public Dataset() { public Dataset() {
@ -109,6 +121,14 @@ public class Dataset {
return url; return url;
} }
public String getLicence() {
return licence;
}
public static List<String> getSort() {
return sortable;
}
public void setAbst(String abst) { public void setAbst(String abst) {
this.abst = abst.substring(0, Math.min(abst.length(), 100)); this.abst = abst.substring(0, Math.min(abst.length(), 100));
} }
@ -153,4 +173,8 @@ public class Dataset {
upvotes--; upvotes--;
} }
public void setLicence(String licence) {
this.licence = licence;
}
} }

View File

@ -0,0 +1,123 @@
package de.uni_passau.fim.PADAS.group3.DataDash.Dataset;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.data.domain.Sort;
import java.util.UUID;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import de.uni_passau.fim.PADAS.group3.DataDash.category.Category;
@RestController
@RequestMapping("/api/v1/datasets")
@EnableSpringDataWebSupport
public class DatasetController {
@Autowired
private DatasetService datasetService;
@GetMapping("/id/{id}")
public ResponseEntity<Dataset> getDatasetById(@PathVariable("id") UUID id) {
Dataset d = datasetService.getDatasetById(id);
if (d == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(d, HttpStatus.OK);
}
@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public Dataset createDataset(@RequestBody Dataset dataset) {
return datasetService.addDataset(dataset);
}
@DeleteMapping("/id/{id}")
public ResponseEntity<?> deleteDataset(@PathVariable("id") UUID id) {
if (datasetService.getDatasetById(id) == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
datasetService.deleteDataset(id);
return new ResponseEntity<>(HttpStatus.OK);
}
@PutMapping("/id/{id}/upvote")
public ResponseEntity<Dataset> upvote(@PathVariable("id") UUID id) {
if (datasetService.getDatasetById(id) == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
datasetService.upvoteDataset(id);
return new ResponseEntity<>(datasetService.getDatasetById(id), HttpStatus.OK);
}
@PutMapping("/id/{id}/downvote")
public ResponseEntity<Dataset> downvote(@PathVariable("id") UUID id) {
if (datasetService.getDatasetById(id) == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
datasetService.downvoteDataset(id);
return new ResponseEntity<>(datasetService.getDatasetById(id), HttpStatus.OK);
}
@PutMapping("/id/{id}/stars")
public ResponseEntity<Dataset> postMethodName(@PathVariable("id") UUID id,
@RequestParam("stars") int stars) {
if (datasetService.getDatasetById(id) == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
if (!(stars > 0 && stars < 6)) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
datasetService.voteDataset(id, stars);
return new ResponseEntity<>(datasetService.getDatasetById(id), HttpStatus.OK);
}
@GetMapping
public Page<Dataset> getDatasetsByDateAfter(@RequestParam(value = "author", required = false) String author,
@RequestParam(value = "title", required = false) String title,
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "abst", required = false) String abst,
@RequestParam(value = "type", required = false) Type type,
@RequestParam(value = "min-rating", required = false) Float rating,
@RequestParam(value = "page", required = false, defaultValue = "0") int page,
@RequestParam(value = "size", required = false, defaultValue = "20") int size,
@RequestParam(value = "sort", required = false, defaultValue = "upvotes") String sort,
@RequestParam(value = "direction", required = false, defaultValue = "desc") String direction,
@RequestParam(value = "category", required = false) Category category) {
Pageable pageable = PageRequest.of(page, size,
Sort.by(Sort.Direction.fromString(direction), sort));
return datasetService.getDatasetsByOptionalCriteria(title, description, author, abst, type, rating, category,
pageable);
}
@GetMapping("/search")
public ResponseEntity<Page<Dataset>> search(
@RequestParam(value = "search", required = false, defaultValue = "%") String search,
@RequestParam(value = "page", required = false, defaultValue = "0") int page,
@RequestParam(value = "size", required = false, defaultValue = "20") int size,
@RequestParam(value = "sort", required = false, defaultValue = "upvotes") String sort,
@RequestParam(value = "direction", required = false, defaultValue = "desc") String direction,
@RequestParam(value = "category", required = false, defaultValue = "%") String category,
@RequestParam(value = "type", required = false, defaultValue = "%") String type) {
Pageable pageable = null;
if (!Dataset.getSort().contains(sort))
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
try {
pageable = PageRequest.of(page, size,
Sort.by(Sort.Direction.fromString(direction), sort));
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(datasetService.searchByOptionalCriteria(search, category, type, pageable),
HttpStatus.OK);
}
}

View File

@ -1,4 +1,4 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model; package de.uni_passau.fim.PADAS.group3.DataDash.Dataset;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
@ -7,15 +7,20 @@ import java.util.UUID;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.data.domain.Page;
import de.uni_passau.fim.PADAS.group3.DataDash.category.Category;
import de.uni_passau.fim.PADAS.group3.DataDash.category.CategoryRepository;
import org.springframework.data.domain.Page;
@Service @Service
public class DatasetService { public class DatasetService {
private dataRepository datasetRepository; private dataRepository datasetRepository;
private CategoryRepository categoryRepository;
public DatasetService(dataRepository datasetRepository) { public DatasetService(dataRepository datasetRepository, CategoryRepository categoryRepository) {
this.datasetRepository = datasetRepository; this.datasetRepository = datasetRepository;
this.categoryRepository = categoryRepository;
} }
public List<Dataset> getAllDatasets() { public List<Dataset> getAllDatasets() {
@ -26,9 +31,9 @@ public class DatasetService {
return datasetRepository.getDatasetById(id); return datasetRepository.getDatasetById(id);
} }
public void addDataset(Dataset dataset) { public Dataset addDataset(Dataset dataset) {
dataset.setDate(LocalDate.now()); dataset.setDate(LocalDate.now());
datasetRepository.save(dataset); return datasetRepository.save(dataset);
} }
public void updateDatasetTitle(UUID id, String title) { public void updateDatasetTitle(UUID id, String title) {
@ -89,15 +94,22 @@ public class DatasetService {
public Page<Dataset> getDatasetsByOptionalCriteria(String title, String description, String author, String abst, public Page<Dataset> getDatasetsByOptionalCriteria(String title, String description, String author, String abst,
Type type, Float raiting, Category category, Pageable pageable) { Type type, Float raiting, Category category, Pageable pageable) {
return datasetRepository.findByOptionalCriteria(Optional.ofNullable(title), Optional.ofNullable(description), return datasetRepository.findByOptionalCriteria(Optional.ofNullable(title), Optional.ofNullable(description),
Optional.ofNullable(author), Optional.ofNullable(abst), Optional.ofNullable(type), Optional.ofNullable(category), Optional.ofNullable(author), Optional.ofNullable(abst), Optional.ofNullable(type),
Optional.ofNullable(category),
Optional.ofNullable(raiting), pageable); Optional.ofNullable(raiting), pageable);
} }
public Page<Dataset> searchByOptionalCriteria(String search, String categories, String type, Pageable pageable) { public Page<Dataset> searchByOptionalCriteria(String search, String categories, String type, Pageable pageable) {
//TODO: make it not Crash Category category = categories.equals("%") ? null
Category category = categories.equals("%") ? null : Category.valueOf(categories); : categoryRepository.getCategoryById(UUID.fromString(categories));
Type t = type.equals("%") ? null : Type.valueOf(type); Type t = type.equals("%") ? null : Type.valueOf(type);
return datasetRepository.searchByOptionalCriteria(Optional.ofNullable(search), Optional.ofNullable(category), Optional.ofNullable(t),pageable); if (category == null) {
return datasetRepository.searchByOptionalCriteria(Optional.ofNullable(search), Optional.ofNullable(t),
pageable);
}
return datasetRepository.searchByOptionalCriteriaWithCategory(Optional.ofNullable(search), category,
Optional.ofNullable(t), pageable);
} }
} }

View File

@ -0,0 +1,6 @@
package de.uni_passau.fim.PADAS.group3.DataDash.Dataset;
public enum Type {
DATASET,
API
}

View File

@ -1,4 +1,4 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model; package de.uni_passau.fim.PADAS.group3.DataDash.Dataset;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -11,6 +11,8 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
import de.uni_passau.fim.PADAS.group3.DataDash.category.Category;
public interface dataRepository extends JpaRepository<Dataset, UUID> { public interface dataRepository extends JpaRepository<Dataset, UUID> {
Dataset getDatasetById(UUID id); Dataset getDatasetById(UUID id);
@ -65,10 +67,19 @@ public interface dataRepository extends JpaRepository<Dataset, UUID> {
"((LOWER(d.title) LIKE LOWER(:search)) OR " + "((LOWER(d.title) LIKE LOWER(:search)) OR " +
"(LOWER(d.description) LIKE LOWER(:search)) OR " + "(LOWER(d.description) LIKE LOWER(:search)) OR " +
"(LOWER(d.author) LIKE LOWER(:search))) AND" + "(LOWER(d.author) LIKE LOWER(:search))) AND" +
"(:categorie IS NULL OR d.categorie = :categorie) AND" + "(d.categorie = :categorie) AND" +
"(:type IS NULL OR d.type = :type)") "(:type IS NULL OR d.type = :type)")
Page<Dataset> searchByOptionalCriteria(@Param("search") Optional<String> search, Page<Dataset> searchByOptionalCriteriaWithCategory(@Param("search") Optional<String> search,
@Param("categorie") Optional<Category> categories, @Param("categorie") Category categories,
@Param("type") Optional<Type> type, @Param("type") Optional<Type> type,
Pageable pageable); Pageable pageable);
}
@Query("SELECT d FROM Dataset d WHERE " +
"((LOWER(d.title) LIKE LOWER(:search)) OR " +
"(LOWER(d.description) LIKE LOWER(:search)) OR " +
"(LOWER(d.author) LIKE LOWER(:search))) AND" +
"(:type IS NULL OR d.type = :type)")
Page<Dataset> searchByOptionalCriteria(@Param("search") Optional<String> search,
@Param("type") Optional<Type> type,
Pageable pageable);
}

View File

@ -0,0 +1,46 @@
package de.uni_passau.fim.PADAS.group3.DataDash.category;
import java.util.List;
import java.util.UUID;
import org.springframework.context.annotation.Lazy;
import de.uni_passau.fim.PADAS.group3.DataDash.Dataset.Dataset;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
@Entity
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private UUID id;
private String name;
@Lazy
@OneToMany(mappedBy = "categorie")
private List<Dataset> datasets;
public Category(String name) {
this.name = name;
}
public Category() {
}
public UUID getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,45 @@
package de.uni_passau.fim.PADAS.group3.DataDash.category;
import java.util.List;
import java.util.UUID;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@RestController
@RequestMapping("/api/v1/categories")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@GetMapping
public List<CategoryDto> getMethodName() {
return categoryService.getAllCategories() ;
}
@GetMapping("/id/{id}")
public ResponseEntity<?> fetchCategoryById(@PathVariable("id") UUID id) {
CategoryDto category = categoryService.getCategoryById(id);
if(category == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(category, HttpStatus.OK);
}
@ResponseStatus(HttpStatus.CREATED)
@PostMapping
public void createCategory(@RequestBody CategoryDto dto) {
categoryService.addCategory(dto);
}
}

View File

@ -0,0 +1,27 @@
package de.uni_passau.fim.PADAS.group3.DataDash.category;
import java.util.UUID;
public class CategoryDto {
private String name;
private UUID id;
public CategoryDto() {
}
CategoryDto(String name, UUID id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public UUID getId() {
return id;
}
}

View File

@ -0,0 +1,10 @@
package de.uni_passau.fim.PADAS.group3.DataDash.category;
public class CategoryDtoMapper {
public static CategoryDto toDto(Category category) {
CategoryDto dto = new CategoryDto(category.getName(), category.getId());
return dto;
}
}

View File

@ -0,0 +1,20 @@
package de.uni_passau.fim.PADAS.group3.DataDash.category;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
public interface CategoryRepository extends JpaRepository<Category, UUID>{
Category getCategoryById(UUID id);
@SuppressWarnings("null")
List<Category> findAll();
List<Category> findByName(String name);
@SuppressWarnings("null")
Optional<Category> findById(UUID id);
}

View File

@ -0,0 +1,34 @@
package de.uni_passau.fim.PADAS.group3.DataDash.category;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Service
public class CategoryService {
private CategoryRepository categoryRepository;
public CategoryService(CategoryRepository categoryRepository) {
this.categoryRepository = categoryRepository;
}
public void addCategory(CategoryDto category) {
Category cat = new Category(category.getName());
categoryRepository.save(cat);
}
public List<CategoryDto> getAllCategories() {
List<Category> tmp = categoryRepository.findAll();
List<CategoryDto> s = tmp.stream().map(CategoryDtoMapper::toDto).toList();
return s;
}
public CategoryDto getCategoryById(UUID id) {
Category c = categoryRepository.getCategoryById(id);
if (c == null) {
return null;
}
return CategoryDtoMapper.toDto(c);
}
}

View File

@ -1,18 +0,0 @@
package de.uni_passau.fim.PADAS.group3.DataDash.controler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import de.uni_passau.fim.PADAS.group3.DataDash.model.Category;
import org.springframework.web.bind.annotation.GetMapping;
@RestController
@RequestMapping("/api/v1/categories")
public class CategoryController {
@GetMapping
public Category[] getMethodName() {
return Category.values();
}
}

View File

@ -1,107 +0,0 @@
package de.uni_passau.fim.PADAS.group3.DataDash.controler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;
import de.uni_passau.fim.PADAS.group3.DataDash.model.Category;
import de.uni_passau.fim.PADAS.group3.DataDash.model.Dataset;
import de.uni_passau.fim.PADAS.group3.DataDash.model.DatasetService;
import de.uni_passau.fim.PADAS.group3.DataDash.model.Type;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.data.domain.Sort;
import java.util.UUID;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@RestController
@RequestMapping("/api/v1/datasets")
@EnableSpringDataWebSupport
public class DatasetController {
@Autowired
private DatasetService datasetService;
// @GetMapping
// public List<Dataset> getAllDatasets() {
// return datasetService.getAllDatasets();
// }
@GetMapping("/id/{id}")
public Dataset getDatasetById(@PathVariable("id") UUID id) {
return datasetService.getDatasetById(id);
}
@PostMapping
public Dataset createDataset(@RequestBody Dataset dataset) {
datasetService.addDataset(dataset);
// TODO: figure out what the fuck i need to do here
return null;
}
// @PutMapping("/{id}")
// public Dataset updateDataset(@PathVariable("id") Long id, @RequestBody
// Dataset dataset) {
// return datasetService.updateDataset(id, dataset);
// }
//
@DeleteMapping("/id/{id}")
public void deleteDataset(@PathVariable("id") UUID id) {
datasetService.deleteDataset(id);
}
@PutMapping("/id/{id}/upvote")
public Dataset upvote(@PathVariable("id") UUID id) {
datasetService.upvoteDataset(id);
return datasetService.getDatasetById(id);
}
@PutMapping("/id/{id}/downvote")
public Dataset downvote(@PathVariable("id") UUID id) {
datasetService.downvoteDataset(id);
return getDatasetById(id); // new ResponseEntity<>(null, HttpStatus.OK);
}
@PutMapping("/id/{id}/stars")
public Dataset postMethodName(@PathVariable("id") UUID id,
@RequestParam("stars") int stars) {
if (stars > 0 && stars < 6) {
datasetService.voteDataset(id, stars);
}
return datasetService.getDatasetById(id);
}
@GetMapping
public Page<Dataset> getDatasetsByDateAfter(@RequestParam(value = "author", required = false) String author,
@RequestParam(value = "title", required = false) String title,
@RequestParam(value = "description", required = false) String description,
@RequestParam(value = "abst", required = false) String abst,
@RequestParam(value = "type", required = false) Type type,
@RequestParam(value = "min-rating", required = false) Float rating,
@RequestParam(value = "page", required = false, defaultValue = "0") int page,
@RequestParam(value = "size", required = false, defaultValue = "20") int size,
@RequestParam(value = "sort", required = false, defaultValue = "upvotes") String sort,
@RequestParam(value = "direction", required = false, defaultValue = "desc") String direction,
@RequestParam(value = "category", required = false) Category category) {
Pageable pageable = PageRequest.of(page, size,
Sort.by(direction.equals("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, sort));
return datasetService.getDatasetsByOptionalCriteria(title, description, author, abst, type, rating, category,pageable);
}
@GetMapping("/search")
public Page<Dataset> search(@RequestParam(value = "search", required = false, defaultValue = "%") String search,
@RequestParam(value = "page", required = false, defaultValue = "0") int page,
@RequestParam(value = "size", required = false, defaultValue = "20") int size,
@RequestParam(value = "sort", required = false, defaultValue = "upvotes") String sort,
@RequestParam(value = "direction", required = false, defaultValue = "desc") String direction,
@RequestParam(value = "category", required = false, defaultValue = "%") String category,
@RequestParam(value = "type", required = false, defaultValue = "%") String type){
Pageable pageable = PageRequest.of(page, size,
Sort.by(direction.equals("asc") ? Sort.Direction.ASC : Sort.Direction.DESC, sort));
return datasetService.searchByOptionalCriteria(search, category, type, pageable);
}
}

View File

@ -1,12 +0,0 @@
package de.uni_passau.fim.PADAS.group3.DataDash.controler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PageController {
@GetMapping("/add")
public String getAddPage() {
return "add";
}
}

View File

@ -1,14 +0,0 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model;
public enum Category {
HEALTH,
ENVIRONMENT,
ECONOMY,
POLITICS,
TECHNOLOGY,
SPORTS,
SCIENCE,
CULTURE,
EDUCATION,
OTHER
}

View File

@ -1,36 +0,0 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model;
import java.util.List;
import java.util.Random;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class LoadDummyDatabase {
private static final org.slf4j.Logger log = LoggerFactory.getLogger(LoadDummyDatabase.class);
@Bean
CommandLineRunner initDatabase(dataRepository repository) {
return args -> {
for (int i = 0; i < 100; i++) {
Dataset dataset = new Dataset("Title" + i, "Abst" + i, "Description" + i, "Author" + i,null, Category.EDUCATION, Type.API);
for (int j = 0; j < new Random().nextInt(50); j++) {
dataset.upvote();
}
log.info("Preloading" + repository.save(dataset));
}
List<Dataset> s = repository.findByTitleLike("%Title%");
log.info("Found Entry with ID: " + s.get(1).getId());};
}
}

View File

@ -1,6 +0,0 @@
package de.uni_passau.fim.PADAS.group3.DataDash.model;
public enum Type {
DATASET,
API
}

View File

@ -1 +1,14 @@
spring.application.name=DataDash spring.application.name=DataDash
# Datasource configuration
spring.datasource.url=jdbc:h2:mem:studentcoursedb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.sql.init.mode=always
spring.jpa.hibernate.ddl-auto=none
# Uncomment for web console
#spring.h2.console.enabled=true
#spring.h2.console.path=/h2-console
#spring.datasource.username=sa
#spring.datasource.password=pwd

View File

@ -0,0 +1,30 @@
-- Insert sample data into category
INSERT INTO category (id, name) VALUES
('123e4567-e89b-12d3-a456-426614174003', 'Business'),
('123e4567-e89b-12d3-a456-426614174004', 'Education'),
('123e4567-e89b-12d3-a456-426614174005', 'Sports'),
('123e4567-e89b-12d3-a456-426614174006', 'Entertainment'),
('123e4567-e89b-12d3-a456-426614174007', 'Art'),
('123e4567-e89b-12d3-a456-426614174000', 'Science'),
('123e4567-e89b-12d3-a456-426614174001', 'Technology'),
('123e4567-e89b-12d3-a456-426614174002', 'Health');
-- Insert sample data into dataset
INSERT INTO dataset (date, raiting, upvotes, votes, categorie_id, id, abst, author, description, title, url, type, licence) VALUES
('2023-01-01', 4.5, 100, 120, '123e4567-e89b-12d3-a456-426614174000', '123e4567-e89b-12d3-a456-426614174100', 'Abstract 1', 'Author 1', 'Description 1', 'Title 1', 'http://example.com/1', 'API', 'MIT'),
('2023-01-02', 4.7, 150, 170, '123e4567-e89b-12d3-a456-426614174001', '123e4567-e89b-12d3-a456-426614174101', 'Abstract 2', 'Author 2', 'Description 2', 'Title 2', 'http://example.com/2', 'DATASET', 'MIT'),
('2023-01-03', 4.9, 200, 220, '123e4567-e89b-12d3-a456-426614174002', '123e4567-e89b-12d3-a456-426614174102', 'Abstract 3', 'Author 3', 'Description 3', 'Title 3', 'http://example.com/3', 'API', 'MIT'),
('2023-01-04', 4.2, 80, 100, '123e4567-e89b-12d3-a456-426614174003', '123e4567-e89b-12d3-a456-426614174103', 'Abstract 4', 'Author 4', 'Description 4', 'Title 4', 'http://example.com/4', 'DATASET', 'MIT'),
('2023-01-05', 4.6, 120, 140, '123e4567-e89b-12d3-a456-426614174004', '123e4567-e89b-12d3-a456-426614174104', 'Abstract 5', 'Author 5', 'Description 5', 'Title 5', 'http://example.com/5', 'API', 'MIT');
-- Insert 10 more sample data into dataset
INSERT INTO dataset (date, raiting, upvotes, votes, categorie_id, id, abst, author, description, title, url, type, licence) VALUES
('2023-01-06', 4.8, 180, 200, '123e4567-e89b-12d3-a456-426614174005', '123e4567-e89b-12d3-a456-426614174105', 'Abstract 6', 'Author 6', 'Description 6', 'Title 6', 'http://example.com/6', 'API', 'MIT'),
('2023-01-07', 4.3, 90, 110, '123e4567-e89b-12d3-a456-426614174006', '123e4567-e89b-12d3-a456-426614174106', 'Abstract 7', 'Author 7', 'Description 7', 'Title 7', 'http://example.com/7', 'DATASET', 'MIT'),
('2023-01-08', 4.7, 150, 170, '123e4567-e89b-12d3-a456-426614174007', '123e4567-e89b-12d3-a456-426614174107', 'Abstract 8', 'Author 8', 'Description 8', 'Title 8', 'http://example.com/8', 'API', 'MIT'),
('2023-01-09', 4.9, 200, 220, '123e4567-e89b-12d3-a456-426614174000', '123e4567-e89b-12d3-a456-426614174108', 'Abstract 9', 'Author 9', 'Description 9', 'Title 9', 'http://example.com/9', 'DATASET', 'MIT'),
('2023-01-10', 4.2, 80, 100, '123e4567-e89b-12d3-a456-426614174001', '123e4567-e89b-12d3-a456-426614174109', 'Abstract 10', 'Author 10', 'Description 10', 'Title 10', 'http://example.com/10', 'API', 'MIT'),
('2023-11-11', 4.6, 120, 140, '123e4567-e89b-12d3-a456-426614174002', '123e4567-e89b-12d3-a456-426614174110', 'Abstract 11', 'Author 11', 'Description 11', 'Title 11', 'http://example.com/11', 'DATASET', 'MIT'),
('2023-09-12', 4.8, 180, 200, '123e4567-e89b-12d3-a456-426614174003', '123e4567-e89b-12d3-a456-426614174111', 'Abstract 12', 'Author 12', 'Description 12', 'Title 12', 'http://example.com/12', 'API', 'MIT'),
('2023-03-13', 4.3, 90, 110, '123e4567-e89b-12d3-a456-426614174004', '123e4567-e89b-12d3-a456-426614174112', 'Abstract 13', 'Author 13', 'Description 13', 'Title 13', 'http://example.com/13', 'DATASET', 'MIT'),
('2021-01-14', 4.7, 150, 170, '123e4567-e89b-12d3-a456-426614174005', '123e4567-e89b-12d3-a456-426614174113', 'Abstract 14', 'Author 14', 'Description 14', 'Title 14', 'http://example.com/14', 'API', 'MIT'),
('2024-01-15', 4.9, 200, 220, '123e4567-e89b-12d3-a456-426614174006', '123e4567-e89b-12d3-a456-426614174114', 'Abstract 15', 'Author 15', 'Description 15', 'Title 15', 'http://example.com/15', 'DATASET', 'MIT');

View File

@ -0,0 +1,7 @@
DROP TABLE IF EXISTS dataset;
DROP TABLE IF EXISTS category;
create table category (id uuid not null, name varchar(255), primary key (id));
create table dataset (date date, raiting float(24) not null, upvotes integer not null, votes integer not null, categorie_id uuid, id uuid not null, abst varchar(255), author varchar(255), description varchar(255), title varchar(255), url varchar(255), type enum ('API','DATASET'), licence varchar(255), primary key (id));
alter table if exists dataset add constraint FKq6qwq6u473f89h71s7rf97ruy foreign key (categorie_id) references category;

View File

@ -1,25 +1,38 @@
import {searchBarTimeout, searchSection} from "./main.js" // TODO consider renaming this to "searchUtility.js"
import { searchBarTimeout, searchSection, lastQuery } from "./main.js"
import Dataset from "./dataset.js" import Dataset from "./dataset.js"
export function fetchQuery(fetchString) { export async function fetchQuery(fetchString, clearResults) {
clearTimeout(searchBarTimeout); clearTimeout(searchBarTimeout);
fetch(fetchString) const response = await fetch(fetchString);
.then(resp => resp.json()) const data = await response.json();
.then((data) => {
parseContent(data.content); parseContent(data.content, clearResults);
}); lastQuery.totalPages = data.totalPages;
if (clearResults) {
lastQuery.currentPage = 0;
}
} }
function parseContent(content) { function parseContent(content, clearResults) {
const nothingFoundElement = searchSection.querySelector("#nothing-found");
if (content.length === 0) { if (content.length === 0) {
searchSection.querySelector("#nothing-found ").classList.remove("hidden"); nothingFoundElement.classList.remove("hidden");
searchSection.querySelector(".datasets").classList.add("hidden");
} else { } else {
searchSection.querySelector("#nothing-found").classList.add("hidden"); nothingFoundElement.classList.add("hidden");
const datasets = content.map(dataset => new Dataset(dataset));
Array.from(searchSection.querySelectorAll(".datasets .dataset")).forEach(e => e.remove()); const datasets = content.map(dataset => Dataset.get(dataset.id) ?? new Dataset(dataset));
searchSection.querySelector(".datasets").classList.remove("hidden");
if (clearResults) {
Array.from(searchSection.querySelectorAll(".datasets .dataset")).forEach(e => e.remove());
}
for (const dataset of datasets) { for (const dataset of datasets) {
searchSection.querySelector(".datasets").appendChild(dataset.createDatasetHTMLElement()); searchSection.querySelector(".datasets")
.appendChild(dataset.createDatasetHTMLElement());
} }
} }
} }

View File

@ -1,6 +1,8 @@
import { vote } from "./main.js"; import { DATASET_ENDPOINT, getBaseURL } from "./main.js";
export default class Dataset { export default class Dataset {
static #datasets = new Map();
#abstract; #abstract;
#author; #author;
#categories; #categories;
@ -13,8 +15,13 @@ export default class Dataset {
#upvotes; #upvotes;
#url; #url;
#votes; #votes;
#elements = [];
constructor({abst: shortDescription, author, categories, date, description, id, rating, title, type, upvotes, url, votes}) { static get(id) {
return this.#datasets.get(id);
}
constructor({ abst: shortDescription, author, categories, date, description, id, rating, title, type, upvotes, url, votes }) {
this.#abstract = shortDescription; this.#abstract = shortDescription;
this.#author = author; this.#author = author;
this.#categories = categories; this.#categories = categories;
@ -27,25 +34,179 @@ export default class Dataset {
this.#upvotes = upvotes; this.#upvotes = upvotes;
this.#url = url; this.#url = url;
this.#votes = votes; this.#votes = votes;
Dataset.#datasets.set(id, this);
} }
// Getters
get abstract() {
return this.#abstract;
}
get author() {
return this.#author;
}
get categories() {
return this.#categories;
}
get date() {
return this.#date;
}
get description() {
return this.#description;
}
get id() {
return this.#id;
}
get rating() {
return this.#rating;
}
get title() {
return this.#title;
}
get type() {
return this.#type;
}
get upvotes() {
return this.#upvotes;
}
get url() {
return this.#url;
}
get votes() {
return this.#votes;
}
get mainElement() {
return this.#elements[0];
}
get elements() {
return this.#elements;
}
// Main methods
// Only on main page
createDatasetHTMLElement() { createDatasetHTMLElement() {
let template = document.querySelector("#dataset-template"); const clone = this.#createTemplateInstance("dataset-template");
const clone = template.content.cloneNode(true); if (clone == null) {
return null;
}
clone.querySelector(".dataset").dataset.id = this.#id; clone.querySelector(".dataset").dataset.id = this.#id;
clone.querySelector("h3").innerText = this.#title; clone.querySelector(".dataset-title").innerText = this.#title;
clone.querySelector("p").innerText = this.#description; clone.querySelector(".dataset-description").innerText = this.#description;
clone.querySelector("span").innerText = this.#upvotes; clone.querySelector(".upvote-count").innerText = this.#upvotes;
// Event Listeners this.#elements.push(clone.children[0]);
clone.querySelector(".upvote-btn").addEventListener("click", () => { this.#createUpvoteButtons(clone);
vote(this.#id, true);
});
clone.querySelector(".downvote-btn").addEventListener("click", () => {
vote(this.#id, false);
})
return clone; return clone;
} }
// Only on details page
createUpvoteComponent() {
let clone = this.#createTemplateInstance("voting-template")
clone.querySelector(".upvote-count").innerText = this.#upvotes;
this.#elements.push(clone.children[0]);
this.#createUpvoteButtons(clone);
return clone;
}
#createUpvoteButtons(templateInstance) {
if (this.storageGetKey("is-voted", false)) {
// The template instance (clone) has to be pushed before this can work
this.#disableVoteBtns();
}
// Event Listeners
templateInstance.querySelector(".upvote-btn").addEventListener("click", () => {
this.vote();
});
templateInstance.querySelector(".downvote-btn").addEventListener("click", () => {
this.vote(false);
})
}
#createTemplateInstance(templateId) {
let template = document.getElementById(templateId);
if (template == null) {
return null;
}
return template.content.cloneNode(true);
}
isMainElement(element) {
return this.#elements.length > 0 && this.#elements[0].isEqualNode(element);
}
async vote(up = true) {
const fetchURL = new URL(
`${DATASET_ENDPOINT}/id/${this.#id}/${up ? "up" : "down"}vote`,
getBaseURL(),
);
const response = await fetch(fetchURL, {
method: "PUT",
headers: {
'Content-Type': 'application/json',
}
});
const data = await response.json();
this.#upvotes = data.upvotes;
for (const element of this.#elements) {
element.querySelector(".upvote-count").innerText = this.#upvotes;
}
this.storageSetKey("vote-type", up ? "upvote" : "downvote");
this.storageSetKey("is-voted", true);
this.#disableVoteBtns();
}
#disableVoteBtns() {
if (this.#elements.length > 0) {
const voteType = this.storageGetKey("vote-type", null);
this.#elements.flatMap(e => Array.from(e.querySelectorAll(".upvote .btn")))
.forEach(btn => {
btn.disabled = true;
if (btn.classList.contains(`${voteType}-btn`)) {
btn.classList.add("isVoted");
}
});
}
}
storageGet() {
const json = localStorage.getItem(this.#id);
return json != null ? JSON.parse(json) : {};
}
storageSetKey(key, value) {
let currentStorage = this.storageGet();
currentStorage[key] = value;
localStorage.setItem(this.#id, JSON.stringify(currentStorage));
}
storageGetKey(key, defaultValue) {
return this.storageGet()?.[key] ?? defaultValue;
}
} }

View File

@ -8,7 +8,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="prefetch" href="https://fonts.googleapis.com/css2?family=Flow+Circular&display=swap"> <link rel="prefetch" href="https://fonts.googleapis.com/css2?family=Flow+Circular&display=swap">
<link rel="stylesheet" href="main.css"> <link rel="stylesheet" href="main.css">
<script type="module" src="main.js" defer></script> <script type="module" src="main.js" defer></script>
</head> </head>
<body> <body>
<div id="add-btn" title="Add a new API entry"></div> <div id="add-btn" title="Add a new API entry"></div>
@ -22,8 +22,8 @@
<li class="dataset" data-id="id"> <li class="dataset" data-id="id">
<div class="dataset-info"> <div class="dataset-info">
<div class="details"> <div class="details">
<h3>title</h3> <h3 class="dataset-title">title</h3>
<p>Simply daily accountability phone call, powered by AI</p> <p class="dataset-description">Simply daily accountability phone call, powered by AI</p>
</div> </div>
</div> </div>
<aside class="upvote"> <aside class="upvote">
@ -81,6 +81,7 @@
</div> </div>
<ul class="datasets"> <ul class="datasets">
</ul> </ul>
<div id="observable" style="height: 2rem"></div>
</section> </section>
</main> </main>
</body> </body>

View File

@ -83,9 +83,6 @@ header {
text-align: center; text-align: center;
} }
.hidden {
display: none;
}
#search-entry:focus-visible { #search-entry:focus-visible {
outline: none; outline: none;
} }
@ -97,6 +94,11 @@ header {
gap: var(--gap-medium); gap: var(--gap-medium);
} }
.hidden {
display: none;
}
@container (width < 60ch) { @container (width < 60ch) {
.datasets { .datasets {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@ -122,6 +124,7 @@ header {
align-items: center; align-items: center;
gap: var(--gap-small); gap: var(--gap-small);
} }
/* Buttons */ /* Buttons */
.upvote-btn, .downvote-btn, #search-btn, #filter-btn, #sort-btn, #reset-tools-btn { .upvote-btn, .downvote-btn, #search-btn, #filter-btn, #sort-btn, #reset-tools-btn {
background: var(--icon-url) no-repeat; background: var(--icon-url) no-repeat;
@ -148,6 +151,7 @@ header {
--icon-url: url(looking-glass.svg); --icon-url: url(looking-glass.svg);
--icon-size: 1rem; --icon-size: 1rem;
} }
#filter-btn { #filter-btn {
--icon-url: url(filter.svg); --icon-url: url(filter.svg);
--icon-size: 1rem; --icon-size: 1rem;
@ -163,6 +167,10 @@ header {
--icon-size: 1rem; --icon-size: 1rem;
} }
.isVoted {
filter: brightness(1.75);
}
.divider { .divider {
width: .05rem; width: .05rem;
height: 1rem; height: 1rem;

View File

@ -1,14 +1,15 @@
import {fetchQuery} from "./contentUtility.js"; import { fetchQuery } from "./contentUtility.js";
import Dataset from "./dataset.js"; import Dataset from "./dataset.js";
const apiEndpoint = "/api/v1/datasets"; export const DATASET_ENDPOINT = "/api/v1/datasets";
const baseURL = location.origin; export const getBaseURL = () => location.origin;
const defaultPagingValue = 20; const defaultPagingValue = 20;
const lastQuery = { export const lastQuery = {
url: "",
totalPages: 0, totalPages: 0,
currentPage: 0 currentPage: 0,
}; };
const loadedCategories = new Set;
// definition of all buttons & sections // definition of all buttons & sections
const addButton = document.getElementById("add-btn"); const addButton = document.getElementById("add-btn");
@ -33,33 +34,34 @@ addButton.addEventListener("click", () => {
}); });
filterButton.addEventListener("change", () => { filterButton.addEventListener("change", () => {
const filterString = filterButton.value; fetchQuery(createQuery(), true);
if (filterString !== filterButton.querySelector("#default-filter").value) {
fetchQuery(createQuery());
}
}); });
filterButton.addEventListener("click", () => {
fetchCategories();
})
searchButton.addEventListener("click", () => { searchButton.addEventListener("click", () => {
fetchQuery(createQuery()); fetchQuery(createQuery(), true);
}); });
searchBar.addEventListener("input", () => { searchBar.addEventListener("input", () => {
updateSections();
clearTimeout(searchBarTimeout); clearTimeout(searchBarTimeout);
searchBarTimeout = setTimeout(() => { searchBarTimeout = setTimeout(() => {
fetchQuery(createQuery()); fetchQuery(createQuery(), true);
updateSections();
}, searchDelay); }, searchDelay);
}); });
searchBar.addEventListener('keypress', function (e) { searchBar.addEventListener('keypress', function (e) {
if (e.key === 'Enter') { if (e.key === 'Enter') {
fetchQuery(createQuery()); fetchQuery(createQuery(), true);
} }
}); });
sortButton.addEventListener("change", () => { sortButton.addEventListener("change", () => {
fetchQuery(createQuery()); fetchQuery(createQuery(), true);
}); });
resetButton.addEventListener("click", () => { resetButton.addEventListener("click", () => {
@ -69,37 +71,19 @@ resetButton.addEventListener("click", () => {
updateSections(); updateSections();
}); });
// Consider moving this to datasets.js completely
const upvoteButtonClickListener = e => {
const entryID = e.target.parentElement.parentElement.dataset.id;
vote(entryID, true);
};
for (const upvoteButton of upvoteButtons) {
upvoteButton.addEventListener("click", upvoteButtonClickListener);
}
// Consider moving this to datasets.js completely
const downvoteButtonClickListener = e => {
const entryID = e.target.parentElement.parentElement.dataset.id;
vote(entryID, false);
};
for (const downvoteButton of downvoteButtons) {
downvoteButton.addEventListener("click", downvoteButtonClickListener);
}
// functions of the main page // functions of the main page
function navigateToAdd() { function navigateToAdd() {
window.location.href = "/add"; window.location.href = "/add.html"; //TODO: move to EventListener?
} }
function getFilterQuery() { function getFilterQuery() {
let filterString= filterButton.value.toUpperCase(); let filterString = filterButton.value.toUpperCase();
if (filterString === "NONE") { if (filterString === "NONE") {
return ["type", "%"] return ["type", "%"]
} else if (document.querySelector('#filter-btn option:checked').parentElement.label === "Standard categories") { } else if (document.querySelector('#filter-btn option:checked').parentElement.label === "Standard categories") {
return ["type", filterString]; return ["type", filterString];
} else { } else {
return ["category", filterString]; return ["category", filterButton.options[filterButton.selectedIndex].value]
} }
} }
@ -121,39 +105,20 @@ function getSortQuery() {
// creates query for the whole toolbar, so that searching, sorting and filtering are always combined // creates query for the whole toolbar, so that searching, sorting and filtering are always combined
function createQuery() { function createQuery() {
updateSections(); updateSections();
let queryURL = new URL(apiEndpoint + "/search", baseURL); let queryURL = new URL(DATASET_ENDPOINT + "/search", getBaseURL());
queryURL.searchParams.append("search", getSearchQuery()); queryURL.searchParams.append("search", getSearchQuery());
let filterQuery = getFilterQuery(); let filterQuery = getFilterQuery();
queryURL.searchParams.append(filterQuery[0], filterQuery[1]); queryURL.searchParams.append(filterQuery[0], filterQuery[1]);
let sortQuery = getSortQuery(); let sortQuery = getSortQuery();
queryURL.searchParams.append("sort", sortQuery[0]); queryURL.searchParams.append("sort", sortQuery[0]);
queryURL.searchParams.append("direction", sortQuery[1]); queryURL.searchParams.append("direction", sortQuery[1]);
queryURL.searchParams.append("size", defaultPagingValue.toString(10)); queryURL.searchParams.append("size", defaultPagingValue.toString(10));
return queryURL; return queryURL;
} }
export function vote(entryID, up) {
const fetchURL = new URL(
`${apiEndpoint}/id/${entryID}/${up ? "up" : "down"}vote`,
baseURL,
);
fetch(fetchURL, {
method: "PUT",
headers: {
'Content-Type': 'application/json',
}})
.then(resp => resp.json())
.then((data) => {
console.log(data.upvotes); // TODO: remove, check einbauen: data.id === entryID?
let dataset = document.querySelector('[data-id= ' + CSS.escape(entryID) + ']')
dataset.querySelector("span").innerText = data.upvotes;
});
}
function incrementPageCount() {
lastQuery.currentPage++;
}
// updates the page display. If no query is present, the initial page is shown, otherwise the search results. // updates the page display. If no query is present, the initial page is shown, otherwise the search results.
function updateSections() { function updateSections() {
if (searchBar.value === "" && sortButton.value === sortButton.querySelector("#default-sort").value if (searchBar.value === "" && sortButton.value === sortButton.querySelector("#default-sort").value
@ -172,44 +137,47 @@ function updateSections() {
// fetches the further categories used in the filter function // fetches the further categories used in the filter function
function fetchCategories() { function fetchCategories() {
const fetchURL = new URL( const fetchURL = new URL("api/v1/categories", getBaseURL());
"api/v1/categories" , baseURL);
fetch(fetchURL) fetch(fetchURL)
.then(resp => resp.json()) .then(resp => resp.json())
.then((data) => { .then((data) => {
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
let category = data[i].toLowerCase(); let categoryName = data[i].name.toLowerCase();
category = category.charAt(0).toUpperCase() + category.slice(1); categoryName = categoryName.charAt(0).toUpperCase() + categoryName.slice(1);
document.getElementById("other-categories").appendChild(new Option(category)); if (!loadedCategories.has(categoryName)) {
let newCategory = new Option(categoryName, data[i].id);
document.getElementById("other-categories").appendChild(newCategory);
loadedCategories.add(categoryName);
}
} }
}); });
} }
// fetches entries for the initial page // fetches entries for the initial page
function fetchInitialEntries() { async function fetchInitialEntries() {
let recentsQueryURL = new URL(apiEndpoint + "/search", baseURL); let recentsQueryURL = new URL(DATASET_ENDPOINT + "/search", getBaseURL());
recentsQueryURL.searchParams.append("sort", "date"); recentsQueryURL.searchParams.append("sort", "date");
recentsQueryURL.searchParams.append("direction", "desc"); recentsQueryURL.searchParams.append("direction", "desc");
recentsQueryURL.searchParams.append("size", "6"); recentsQueryURL.searchParams.append("size", "6");
fetch(recentsQueryURL)
.then(resp => resp.json())
.then((data) => {
const datasets = data.content.map(dataset => new Dataset(dataset));
for (const dataset of datasets) {
document.querySelector("#recents .datasets").appendChild(dataset.createDatasetHTMLElement());
}
});
let topVotedQueryURL = new URL(apiEndpoint + "/search", baseURL); const recentsResponse = await fetch(recentsQueryURL);
const recentsData = await recentsResponse.json();
const recentsDatasets = recentsData.content.map(dataset => new Dataset(dataset));
for (const recentDataset of recentsDatasets) {
document.querySelector("#recents .datasets").appendChild(recentDataset.createDatasetHTMLElement());
}
let topVotedQueryURL = new URL(DATASET_ENDPOINT + "/search", getBaseURL());
topVotedQueryURL.searchParams.append("sort", "upvotes"); topVotedQueryURL.searchParams.append("sort", "upvotes");
topVotedQueryURL.searchParams.append("direction", "desc"); topVotedQueryURL.searchParams.append("direction", "desc");
topVotedQueryURL.searchParams.append("size", "1"); topVotedQueryURL.searchParams.append("size", "1");
fetch(topVotedQueryURL)
.then(resp => resp.json()) const topVotedResponse = await fetch(topVotedQueryURL);
.then((data) => { const topVotedData = await topVotedResponse.json();
document.querySelector("#top .datasets") const topVotedDataset = new Dataset(topVotedData.content[0]);
.appendChild(new Dataset(data.content[0]).createDatasetHTMLElement());
}); document.querySelector("#top .datasets").appendChild(topVotedDataset.createDatasetHTMLElement());
} }
window.onload = function () { window.onload = function () {
@ -217,6 +185,21 @@ window.onload = function () {
fetchInitialEntries(); fetchInitialEntries();
updateSections(); updateSections();
if (searchBar.value !== "") { if (searchBar.value !== "") {
fetchQuery(createQuery()); fetchQuery(createQuery(), true);
}
let observer = new IntersectionObserver(() => {
if (!searchSection.classList.contains("hidden")) {
fetchPagingResults();
}
}, {root: null, rootMargin: "0px", threshold: .9});
observer.observe(document.getElementById("observable"));
}
function fetchPagingResults() {
lastQuery.currentPage++
if (lastQuery.currentPage < lastQuery.totalPages) {
let pagingQuery = new URL(createQuery());
pagingQuery.searchParams.append("page", lastQuery.currentPage.toString(10));
fetchQuery(pagingQuery, false);
} }
} }