Skip to content

Commit

Permalink
feat: add possibility to filter by owned field
Browse files Browse the repository at this point in the history
  • Loading branch information
bayang committed Jun 10, 2022
1 parent 231997b commit 7afde5c
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 26 deletions.
53 changes: 51 additions & 2 deletions src/jelu-ui/src/components/BookList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,43 @@ const open = ref(false)
const getBookIsLoading: Ref<boolean> = ref(false)
const toRead: Ref<string|null> = useRouteQuery('toRead', "null")
const owned: Ref<string|null> = useRouteQuery('owned', "null")
const eventTypes: Ref<Array<ReadingEventType>> = useRouteQueryArray('lastEventTypes', [])
watch([page, eventTypes, toRead, sortQuery], (newVal, oldVal) => {
watch([page, eventTypes, toRead, owned, sortQuery], (newVal, oldVal) => {
console.log("all " + newVal + " " + oldVal)
if (newVal !== oldVal) {
throttledGetBooks()
}
})
const toReadAsBool = computed(() => toRead.value?.toLowerCase() === "null" ? null : toRead.value?.toLowerCase() === "true")
const toReadAsBool = computed(() => {
if (toRead.value?.toLowerCase() === "null") {
return null
} else if (toRead.value?.toLowerCase() === "true") {
return true
} else {
return false
}
}
)
const ownedAsBool = computed(() => {
if (owned.value?.toLowerCase() === "null") {
return null
} else if (owned.value?.toLowerCase() === "true") {
return true
} else {
return false
}
}
)
const getBooks = () => {
getBookIsLoading.value = true
dataService.findUserBookByCriteria(eventTypes.value, toReadAsBool.value,
ownedAsBool.value,
pageAsNumber.value - 1, perPage.value, sortQuery.value)
.then(res => {
console.log(res)
Expand Down Expand Up @@ -188,6 +210,33 @@ onMounted(() => {
</o-radio>
</div>
</div>
<div class="field">
<label class="label">{{ t('filtering.owned') }} : </label>
<div class="field">
<o-radio
v-model="owned"
native-value="null"
>
{{ t('filtering.unset') }}
</o-radio>
</div>
<div class="field">
<o-radio
v-model="owned"
native-value="false"
>
{{ t('labels.false') }}
</o-radio>
</div>
<div class="field">
<o-radio
v-model="owned"
native-value="true"
>
{{ t('labels.true') }}
</o-radio>
</div>
</div>
</template>
</sort-filter-bar-vue>
<div class="flex flex-row justify-between">
Expand Down
47 changes: 44 additions & 3 deletions src/jelu-ui/src/components/ToReadList.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { onMounted, Ref, ref, watch } from 'vue'
import { computed, onMounted, Ref, ref, watch } from 'vue'
import { useRouteQuery } from '@vueuse/router';
import { UserBook } from '../model/Book'
import dataService from "../services/DataService";
import BookCard from "./BookCard.vue";
Expand Down Expand Up @@ -29,13 +30,26 @@ const eventTypes: Ref<Array<ReadingEventType>> = useRouteQueryArray('lastEventTy
const open = ref(false)
const owned: Ref<string|null> = useRouteQuery('owned', "null")
const ownedAsBool = computed(() => {
if (owned.value?.toLowerCase() === "null") {
return null
} else if (owned.value?.toLowerCase() === "true") {
return true
} else {
return false
}
}
)
const getToReadIsLoading: Ref<boolean> = ref(false)
const getToRead = async () => {
getToReadIsLoading.value = true
try {
const res = await dataService.findUserBookByCriteria(eventTypes.value,
true, pageAsNumber.value - 1, perPage.value, sortQuery.value)
true, ownedAsBool.value,
pageAsNumber.value - 1, perPage.value, sortQuery.value)
total.value = res.totalElements
books.value = res.content
if (! res.empty) {
Expand All @@ -59,7 +73,7 @@ const throttledGetToRead = useThrottleFn(() => {
getToRead()
}, 100, false)
watch([page, eventTypes, sortQuery], (newVal, oldVal) => {
watch([page, eventTypes, sortQuery, owned], (newVal, oldVal) => {
console.log("all " + newVal + " " + oldVal)
if (newVal !== oldVal) {
throttledGetToRead()
Expand Down Expand Up @@ -143,6 +157,33 @@ getToRead()
{{ t('reading_events.dropped') }}
</o-checkbox>
</div>
<div class="field">
<label class="label">{{ t('filtering.owned') }} : </label>
<div class="field">
<o-radio
v-model="owned"
native-value="null"
>
{{ t('filtering.unset') }}
</o-radio>
</div>
<div class="field">
<o-radio
v-model="owned"
native-value="false"
>
{{ t('labels.false') }}
</o-radio>
</div>
<div class="field">
<o-radio
v-model="owned"
native-value="true"
>
{{ t('labels.true') }}
</o-radio>
</div>
</div>
</template>
</sort-filter-bar-vue>
<div class="flex flex-row justify-between">
Expand Down
3 changes: 2 additions & 1 deletion src/jelu-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@
"books_type" : "Books type",
"any" : "Any",
"only_in_my_list" : "Only books in my lists",
"only_not_in_my_list" : "Only books not in my lists"
"only_not_in_my_list" : "Only books not in my lists",
"owned" : "Book is owned"
},
"book" : {
"owned" : "owned",
Expand Down
3 changes: 2 additions & 1 deletion src/jelu-ui/src/services/DataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,12 +410,13 @@ class DataService {
}

findUserBookByCriteria = async (lastEventTypes?: Array<ReadingEventType>|null,
toRead?: boolean|null, page?: number, size?: number, sort?: string) => {
toRead?: boolean|null, owned?: boolean|null, page?: number, size?: number, sort?: string) => {
try {
const response = await this.apiClient.get<Page<UserBook>>(`${this.API_USERBOOK}`, {
params: {
lastEventTypes: lastEventTypes,
toRead: toRead,
owned: owned,
page: page,
size: size,
sort: sort
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class BooksController(
principal: Authentication,
@RequestParam(name = "lastEventTypes", required = false) eventTypes: List<ReadingEventType>?,
@RequestParam(name = "toRead", required = false) toRead: Boolean?,
@RequestParam(name = "owned", required = false) owned: Boolean?,
@RequestParam(name = "user", required = false) userId: UUID?,
@PageableDefault(page = 0, size = 20, direction = Sort.Direction.DESC, sort = ["modificationDate"]) @ParameterObject pageable: Pageable
): Page<UserBookWithoutEventsAndUserDto> {
Expand All @@ -132,7 +133,7 @@ class BooksController(
} else {
(principal.principal as JeluUser).user.id.value
}
return repository.findUserBookByCriteria(finalUserId, eventTypes, toRead, pageable)
return repository.findUserBookByCriteria(finalUserId, eventTypes, toRead, owned, pageable)
}

@GetMapping(path = ["/authors"])
Expand Down
21 changes: 19 additions & 2 deletions src/main/kotlin/io/github/bayang/jelu/dao/BookRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.deleteWhere
import org.jetbrains.exposed.sql.lowerCase
import org.jetbrains.exposed.sql.or
import org.jetbrains.exposed.sql.orWhere
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.selectAll
Expand Down Expand Up @@ -533,6 +534,7 @@ class BookRepository(
userID: UUID,
eventTypes: List<ReadingEventType>?,
toRead: Boolean?,
owned: Boolean?,
pageable: Pageable
): PageImpl<UserBook> {
val cols = mutableListOf<Expression<*>>()
Expand All @@ -545,8 +547,23 @@ class BookRepository(
if (eventTypes != null && eventTypes.isNotEmpty()) {
query.andWhere { UserBookTable.lastReadingEvent inList eventTypes }
}
toRead?.let {
query.andWhere { UserBookTable.toRead eq toRead }
if (toRead != null) {
if (toRead) {
query.andWhere { UserBookTable.toRead eq toRead }
} else {
// default value if checkbox not set is null, so if caller asks explicitly with toRead == false,
// try to return everything that is not true
query.andWhere { UserBookTable.toRead eq toRead or (UserBookTable.toRead.isNull()) }
}
}
if (owned != null) {
if (owned) {
query.andWhere { UserBookTable.owned eq owned }
} else {
// default value if checkbox not set is null, so if caller asks explicitly with owned == false,
// try to return everything that is not true
query.andWhere { UserBookTable.owned eq owned or (UserBookTable.owned.isNull()) }
}
}
val total = query.count()
query.limit(pageable.pageSize, pageable.offset)
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/io/github/bayang/jelu/service/BookService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,10 @@ class BookService(
userId: UUID,
eventTypes: List<ReadingEventType>?,
toRead: Boolean?,
owned: Boolean? = null,
pageable: Pageable
): Page<UserBookWithoutEventsAndUserDto> {
return bookRepository.findUserBookByCriteria(userId, eventTypes, toRead, pageable).map { it.toUserBookWthoutEventsAndUserDto() }
return bookRepository.findUserBookByCriteria(userId, eventTypes, toRead, owned, pageable).map { it.toUserBookWthoutEventsAndUserDto() }
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class CsvExportService(
CSVPrinter(BufferedWriter(FileWriter(destFile)), format).use { printer ->
printer.printRecord("Title", "Author", "ISBN", "Publisher", "Date Read", "Shelves", "Bookshelves", "read_dates", "tags", "authors", "isbn10", "isbn13", "owned")
do {
books = bookService.findUserBookByCriteria(userId, null, null, PageRequest.of(currentPage, pageSize))
books = bookService.findUserBookByCriteria(userId, null, null, null, PageRequest.of(currentPage, pageSize))
currentPage ++
logger.debug { "current $currentPage" }
count += books.content.size
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/io/github/bayang/jelu/TestHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import io.github.bayang.jelu.dto.ImportConfigurationDto
import io.github.bayang.jelu.dto.TagDto
import java.time.Instant

fun createUserBookDto(bookDto: BookCreateDto, lastReadingEvent: ReadingEventType? = null, lastreadingEventDate: Instant? = null, toRead: Boolean = false): CreateUserBookDto {
fun createUserBookDto(bookDto: BookCreateDto, lastReadingEvent: ReadingEventType? = null, lastreadingEventDate: Instant? = null, toRead: Boolean = false, owned: Boolean? = true): CreateUserBookDto {
return CreateUserBookDto(
personalNotes = "test personal notes\nwith a newline",
lastReadingEvent = lastReadingEvent,
lastReadingEventDate = lastreadingEventDate,
owned = true,
owned = owned,
toRead = toRead,
percentRead = null,
book = bookDto
Expand Down
68 changes: 63 additions & 5 deletions src/test/kotlin/io/github/bayang/jelu/service/BookServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class BookServiceTest(
readingEventService.findAll(null, null, null, null, null, Pageable.ofSize(30)).content.forEach {
readingEventService.deleteReadingEventById(it.id!!)
}
bookService.findUserBookByCriteria(user().id.value, null, null, Pageable.ofSize(30))
bookService.findUserBookByCriteria(user().id.value, null, null, null, Pageable.ofSize(30))
.forEach { bookService.deleteUserBookById(it.id!!) }
bookService.findAllAuthors(null, Pageable.ofSize(30)).forEach {
bookService.deleteAuthorById(it.id!!)
Expand Down Expand Up @@ -248,6 +248,64 @@ class BookServiceTest(
Assertions.assertEquals(0, File(jeluProperties.files.images).listFiles().size)
}

@Test
fun testQueryByOwnedField() {
val createBook = bookDto("title owned")
val createUserBookDto = createUserBookDto(createBook)
val saved: UserBookLightDto = bookService.save(createUserBookDto, user(), null)
Assertions.assertEquals(createBook.title, saved.book.title)
Assertions.assertEquals(createBook.isbn10, saved.book.isbn10)
Assertions.assertEquals(createBook.isbn13?.trim(), saved.book.isbn13)
Assertions.assertEquals("This is a test summary with a newline", saved.book.summary)
Assertions.assertEquals(createBook.publisher, saved.book.publisher)
Assertions.assertEquals(createBook.pageCount, saved.book.pageCount)
Assertions.assertEquals(createBook.goodreadsId, saved.book.goodreadsId)
Assertions.assertNull(saved.book.librarythingId)
Assertions.assertEquals(createUserBookDto.owned, saved.owned)

val createBook1 = bookDto("title not owned")
val createUserBookDto1 = createUserBookDto(createBook1, owned = false)
val saved1: UserBookLightDto = bookService.save(createUserBookDto1, user(), null)
Assertions.assertEquals(createBook1.title, saved1.book.title)
Assertions.assertEquals(createBook1.isbn10, saved1.book.isbn10)
Assertions.assertEquals(createBook1.isbn13?.trim(), saved1.book.isbn13)
Assertions.assertEquals("This is a test summary with a newline", saved1.book.summary)
Assertions.assertEquals(createBook1.publisher, saved1.book.publisher)
Assertions.assertEquals(createBook1.pageCount, saved1.book.pageCount)
Assertions.assertEquals(createBook1.goodreadsId, saved1.book.goodreadsId)
Assertions.assertNull(saved1.book.librarythingId)
Assertions.assertEquals(createUserBookDto1.owned, saved1.owned)

val createBook2 = bookDto("owned is null")
val createUserBookDto2 = createUserBookDto(createBook2, owned = null)
val saved2: UserBookLightDto = bookService.save(createUserBookDto2, user(), null)
Assertions.assertEquals(createBook2.title, saved2.book.title)
Assertions.assertEquals(createBook2.isbn10, saved2.book.isbn10)
Assertions.assertEquals(createBook2.isbn13?.trim(), saved2.book.isbn13)
Assertions.assertEquals("This is a test summary with a newline", saved2.book.summary)
Assertions.assertEquals(createBook2.publisher, saved2.book.publisher)
Assertions.assertEquals(createBook2.pageCount, saved2.book.pageCount)
Assertions.assertEquals(createBook2.goodreadsId, saved2.book.goodreadsId)
Assertions.assertNull(saved2.book.librarythingId)
Assertions.assertEquals(createUserBookDto2.owned, saved2.owned)

var nb = bookService.findUserBookByCriteria(user().id.value, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(3, nb)

var res = bookService.findUserBookByCriteria(user().id.value, null, null, true, Pageable.ofSize(30))
nb = res.totalElements
Assertions.assertEquals(1, nb)
Assertions.assertEquals(createBook.title, res.content[0].book.title)

res = bookService.findUserBookByCriteria(user().id.value, null, null, false, Pageable.ofSize(30))
nb = res.totalElements
Assertions.assertEquals(2, nb)
val titles = listOf<String>(createBook1.title, createBook2.title)
res.content.forEach {
Assertions.assertTrue(titles.contains(it.book.title))
}
}

@Test
fun testInsertUserbookWithNewBookImageAndEvent() {
val createBook = bookDto()
Expand Down Expand Up @@ -657,12 +715,12 @@ class BookServiceTest(

Assertions.assertNotNull(saved1)
Assertions.assertNotNull(saved2)
var nb = bookService.findUserBookByCriteria(user().id.value, null, null, Pageable.ofSize(30)).totalElements
var nb = bookService.findUserBookByCriteria(user().id.value, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(2, nb)
var eventsNb = readingEventService.findAll(null, null, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(2, eventsNb)
bookService.deleteUserBookById(saved1.id!!)
nb = bookService.findUserBookByCriteria(user().id.value, null, null, Pageable.ofSize(30)).totalElements
nb = bookService.findUserBookByCriteria(user().id.value, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(1, nb)
eventsNb = readingEventService.findAll(null, null, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(1, eventsNb)
Expand Down Expand Up @@ -690,7 +748,7 @@ class BookServiceTest(

Assertions.assertNotNull(saved1)
Assertions.assertNotNull(saved2)
var nb = bookService.findUserBookByCriteria(user().id.value, null, null, Pageable.ofSize(30)).totalElements
var nb = bookService.findUserBookByCriteria(user().id.value, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(2, nb)
var eventsNb = readingEventService.findAll(null, null, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(2, eventsNb)
Expand All @@ -700,7 +758,7 @@ class BookServiceTest(
Assertions.assertEquals(2, tagsNb)
Assertions.assertEquals(1, File(jeluProperties.files.images).listFiles().size)
bookService.deleteBookById(savedBook.id!!)
nb = bookService.findUserBookByCriteria(user().id.value, null, null, Pageable.ofSize(30)).totalElements
nb = bookService.findUserBookByCriteria(user().id.value, null, null, null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(0, nb)
authorsNb = bookService.findAllAuthors(null, Pageable.ofSize(30)).totalElements
Assertions.assertEquals(1, authorsNb)
Expand Down
Loading

0 comments on commit 7afde5c

Please sign in to comment.