Skip to content

Commit

Permalink
feat: autocomplete publisher #159
Browse files Browse the repository at this point in the history
  • Loading branch information
bayang committed Jan 6, 2025
1 parent 12892af commit dd48440
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 11 deletions.
28 changes: 24 additions & 4 deletions src/jelu-ui/src/components/AddBook.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ let tags: Ref<Array<Tag>> = ref([]);
let translators: Ref<Array<Author>> = ref([]);
let filteredTranslators: Ref<Array<Author>> = ref([]);
let filteredPublishers: Ref<Array<string>> = ref([])
let seriesCopy: Ref<Array<SeriesOrder>> = ref([])
const showModal: Ref<boolean> = ref(false)
Expand Down Expand Up @@ -290,6 +292,10 @@ function getFilteredTags(text: string) {
dataService.findTagsByCriteria(text).then((data) => filteredTags.value = data.content)
}
function getFilteredPublishers(text: string) {
dataService.findPublisherByCriteria(text).then(data => filteredPublishers.value = data.content)
}
function beforeAdd(item: Author | string) {
let shouldAdd = true
if (item instanceof Object) {
Expand Down Expand Up @@ -377,6 +383,14 @@ function createTag(item: Tag | string) {
}
}
function selectPublisher(publisher: string, event: UIEvent) {
// we receive from oruga weird events while nothing is selected
// so try to get rid of those null data we receive
if (publisher != null && event != null) {
form.publisher = publisher
}
}
const toggleModal = (file: boolean) => {
showModal.value = !showModal.value
oruga.modal.open({
Expand Down Expand Up @@ -725,13 +739,13 @@ let displayDatepicker = computed(() => {
:placeholder="t('book.openlibrary_id')"
class="input focus:input-accent"
/>
<o-input
<o-input
v-model="form.noosfereId"
name="noosfereId"
:placeholder="t('book.noosfere_id')"
class="input focus:input-accent"
/>
<o-input
<o-input
v-model="form.inventaireId"
name="inventaireId"
:placeholder="t('book.inventaire_id')"
Expand All @@ -745,9 +759,15 @@ let displayDatepicker = computed(() => {
:label="t('book.publisher')"
class="capitalize"
>
<o-input
<o-autocomplete
v-model="form.publisher"
class="input focus:input-accent"
:root-class="'grow, w-full'"
:input-classes="{rootClass:'border-2 border-accent'}"
:data="filteredPublishers"
:clear-on-select="true"
:debounce="100"
@input="getFilteredPublishers"
@select="selectPublisher"
/>
</o-field>
</div>
Expand Down
20 changes: 18 additions & 2 deletions src/jelu-ui/src/components/BookDataCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ const getUserbookId = async () => {
})
}
const publisherQuery = computed(() => {
if (props.book.publisher) {
return "\"" + props.book.publisher + "\""
}
return ""
})
watch(() => props.book.id, (newVal, oldVal) => {
console.log("props.book.id ")
console.log(newVal + " " + oldVal)
Expand Down Expand Up @@ -169,8 +176,17 @@ function modalClosed() {
</li>
</ul>
<p v-if="props.book.publisher">
<span class="font-semibold capitalize">{{ t('book.publisher') }} :</span>
{{ props.book.publisher }}
<span class="font-semibold capitalize">{{ t('book.publisher') }} :&nbsp;</span>
<router-link
v-if="links != null && links === true"
class="link hover:underline hover:decoration-4 hover:decoration-secondary"
:to="{ name: 'search', query: { q: `publisher:` + publisherQuery } }"
>
{{ props.book.publisher }}
</router-link>
<span v-else>
{{ props.book.publisher }}
</span>
</p>
<p v-if="props.book.isbn10">
<span class="font-semibold uppercase">{{ t('book.isbn10') }} :</span>
Expand Down
14 changes: 13 additions & 1 deletion src/jelu-ui/src/components/BookDetail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,13 @@ function defaultCreateEvent(): CreateReadingEvent {
}
}
const publisherQuery = computed(() => {
if (book.value?.book.publisher) {
return "\"" + book.value.book.publisher + "\""
}
return ""
})
const embedCode = computed(() => {
if (book.value) {
return generateEmbed(book.value)
Expand Down Expand Up @@ -757,7 +764,12 @@ getBook()
</ul>
<p v-if="book?.book?.publisher">
<span class="font-semibold capitalize">{{ t('book.publisher') }} :</span>
{{ book.book.publisher }}
<router-link
class="link hover:underline hover:decoration-4 hover:decoration-secondary"
:to="{ name: 'search', query: { q: `publisher:` + publisherQuery } }"
>
{{ book.book.publisher }}
</router-link>
</p>
<p v-if="book?.book?.isbn10">
<span class="font-semibold uppercase">{{ t('book.isbn10') }} :</span>
Expand Down
25 changes: 22 additions & 3 deletions src/jelu-ui/src/components/EditBookModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const emit = defineEmits(['close']);
let filteredAuthors: Ref<Array<Author>> = ref([]);
let filteredTags: Ref<Array<Tag>> = ref([]);
let filteredTranslators: Ref<Array<Author>> = ref([]);
let filteredPublishers: Ref<Array<string>> = ref([])
let userbook: Ref<UserBook> = ref(copyInput(props.book))
let hasImage: Ref<boolean> = ref(userbook.value.book.image != null)
let deleteImage: Ref<boolean> = ref(false)
Expand Down Expand Up @@ -156,6 +157,10 @@ function getFilteredTags(text: string) {
dataService.findTagsByCriteria(text).then((data) => filteredTags.value = data.content)
}
function getFilteredPublishers(text: string) {
dataService.findPublisherByCriteria(text).then(data => filteredPublishers.value = data.content)
}
function itemAdded() {
console.log("added")
console.log(userbook.value.book.authors)
Expand Down Expand Up @@ -248,6 +253,14 @@ function createTag(item: Tag | string) {
}
}
function selectPublisher(publisher: string, event: UIEvent) {
// we receive from oruga weird events while nothing is selected
// so try to get rid of those null data we receive
if (publisher != null && event != null) {
userbook.value.book.publisher = publisher
}
}
const toggleImagePickerModal = () => {
showImagePickerModal.value = !showImagePickerModal.value
oruga.modal.open({
Expand Down Expand Up @@ -456,9 +469,15 @@ watch(() => publishedDate.value, (newVal, oldVal) => {
:label="t('book.publisher')"
class="capitalize"
>
<o-input
v-model="userbook.book.publisher"
class="input focus:input-accent"
<o-autocomplete
v-model="userbook.book.publisher"
:root-class="'grow, w-full'"
:input-classes="{rootClass:'border-2 border-accent'}"
:data="filteredPublishers"
:clear-on-select="true"
:debounce="100"
@input="getFilteredPublishers"
@select="selectPublisher"
/>
</o-field>
</div>
Expand Down
21 changes: 21 additions & 0 deletions src/jelu-ui/src/services/DataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,27 @@ class DataService {
throw new Error("error get series by criteria " + error)
}
}


findPublisherByCriteria = async (query?: string | null) => {
try {
const response = await this.apiClient.get<Page<string>>(`${this.API_BOOK}/publishers`, {
params: {
name: query
}
});
console.log("called publishers by criteria")
console.log(response)
return response.data;
}
catch (error) {
if (axios.isAxiosError(error) && error.response) {
console.log("error axios " + error.response.status + " " + error.response.data.error)
}
console.log("error publishers by criteria " + (error as AxiosError).code)
throw new Error("error get publishers by criteria " + error)
}
}

getTagById = async (tagId: string) => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ class BooksController(
@PageableDefault(page = 0, size = 20, direction = Sort.Direction.ASC, sort = ["name"]) @ParameterObject pageable: Pageable,
): Page<TagDto> = repository.findAllTags(name, pageable)

@GetMapping(path = ["/books/publishers"])
fun publishers(
@RequestParam(name = "name", required = false) name: String?,
@PageableDefault(page = 0, size = 20, direction = Sort.Direction.ASC, sort = ["name"]) @ParameterObject pageable: Pageable,
): Page<String> {
return repository.findPublishers(name, pageable)
}

@GetMapping(path = ["/tags/{id}"])
fun tagById(
@PathVariable("id") tagId: UUID,
Expand Down
17 changes: 17 additions & 0 deletions src/main/kotlin/io/github/bayang/jelu/dao/BookRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,23 @@ class BookRepository(
)
}

fun findAllPublishers(name: String?, pageable: Pageable): Page<String> {
val query: Query = BookTable.select(BookTable.publisher)
name?.let {
query.andWhere { BookTable.publisher like "%$name%" }
}
query.withDistinct()
val total = query.count()
query.limit(pageable.pageSize, pageable.offset)
query.orderBy(BookTable.publisher, SortOrder.ASC)
val res = query.map { it[BookTable.publisher] }.stream().filter { it != null }.toList()
return PageImpl(
res,
pageable,
total,
)
}

fun wrapRow(resultRow: ResultRow, userId: UUID): Book {
val e = Book.wrapRow(resultRow)
val userbook = e.userBooks.firstOrNull { u -> u.user.id.value == userId }
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/io/github/bayang/jelu/service/BookService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class BookService(
@Transactional
fun findAuthorsById(authorId: UUID): AuthorDto = bookRepository.findAuthorsById(authorId).toAuthorDto()

@Transactional
fun findPublishers(name: String?, pageable: Pageable): Page<String> = bookRepository.findAllPublishers(name, pageable)

/**
* Image not updated, to add or update an image call the variant which accepts a MultiPartFile
*/
Expand Down
73 changes: 72 additions & 1 deletion src/test/kotlin/io/github/bayang/jelu/service/BookServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class BookServiceTest(
readingEventService.findAll(null, null, null, null, null, null, null, Pageable.ofSize(30)).content.forEach {
readingEventService.deleteReadingEventById(it.id!!)
}
bookService.findUserBookByCriteria(user().id!!, null, null, null, null, null, Pageable.ofSize(30))
bookService.findUserBookByCriteria(user().id!!, null, null, null, null, null, Pageable.ofSize(100))
.forEach { bookService.deleteUserBookById(it.id!!) }
bookService.findUserBookByCriteria(user2().id!!, null, null, null, null, null, Pageable.ofSize(100))
.forEach { bookService.deleteUserBookById(it.id!!) }
bookService.findAllAuthors(null, Pageable.ofSize(30)).forEach {
bookService.deleteAuthorById(it.id!!)
Expand All @@ -96,6 +98,12 @@ class BookServiceTest(
bookService.findAllSeries(null, null, Pageable.ofSize(20)).content.forEach {
bookService.deleteSeriesById(it.id!!)
}
bookService.findAll(null, Pageable.ofSize(100), user(), LibraryFilter.ANY).forEach {
bookService.deleteBookById(it.id!!)
}
bookService.findAll(null, Pageable.ofSize(100), user2(), LibraryFilter.ANY).forEach {
bookService.deleteBookById(it.id!!)
}
luceneHelper.getIndexWriter().use { indexWriter ->
indexWriter.deleteDocuments(Term(LuceneEntity.TYPE, LuceneEntity.Book.type))
indexWriter.deleteDocuments(Term(LuceneEntity.TYPE, LuceneEntity.Author.type))
Expand Down Expand Up @@ -1086,6 +1094,69 @@ class BookServiceTest(
Assertions.assertEquals(res.id, UUID.fromString(entitiesIds?.get(0)))
}

@Test
fun testFindPublishers() {
val res: BookDto = bookService.save(
BookCreateDto(
id = null,
title = "title1",
isbn10 = "",
isbn13 = "",
summary = "",
image = "",
publisher = "publisher1",
pageCount = 50,
publishedDate = "",
authors = emptyList(),
tags = emptyList(),
goodreadsId = "",
googleId = "",
librarythingId = "",
language = "",
amazonId = "",
),
null,
)
Assertions.assertNotNull(res.id)
val found = bookService.findBookById(res.id!!)
Assertions.assertEquals(found.id, res.id)
Assertions.assertEquals(found.authors, res.authors)
Assertions.assertEquals(found.title, res.title)
Assertions.assertEquals(found.isbn10, res.isbn10)
Assertions.assertEquals(found.pageCount, res.pageCount)
Assertions.assertEquals(0, File(jeluProperties.files.images).listFiles().size)
val entitiesIds = luceneHelper.searchEntitiesIds("tit", LuceneEntity.Book)
Assertions.assertEquals(1, entitiesIds?.size)
Assertions.assertEquals(res.id, UUID.fromString(entitiesIds?.get(0)))
var page = bookService.findPublishers(null, Pageable.ofSize(30))
Assertions.assertEquals(1, page.totalElements)
Assertions.assertEquals("publisher1", page.content[0])
val res1: BookDto = bookService.save(
BookCreateDto(
id = null,
title = "title2",
isbn10 = "",
isbn13 = "",
summary = "",
image = "",
publisher = "publisher2",
pageCount = 50,
publishedDate = "",
authors = emptyList(),
tags = emptyList(),
goodreadsId = "",
googleId = "",
librarythingId = "",
language = "",
amazonId = "",
),
null,
)
Assertions.assertNotNull(res1.id)
page = bookService.findPublishers(null, Pageable.ofSize(20))
Assertions.assertEquals(2, page.totalElements)
}

@Test
fun testDeletedAuthorShouldBeRemovedFromBook() {
val res: BookDto = bookService.save(bookDto(), null)
Expand Down

0 comments on commit dd48440

Please sign in to comment.