git clone git@github.com:Taack/myLibrary.git
Introduzione
Questa sezione spiega come generare dati e implementare diversi tipi di grafici nella tua applicazione di gestione della biblioteca utilizzando Grails e il framework Taack UI.
Configurazione
La soluzione e gli scheletri per questo esercizio sono disponibili per essere clonati da questo repository:
myLibrary) con cd e copia la riga seguente.git checkout part-4-skeleton
Oppure in IntelliJ puoi selezionare direttamente il branch desiderato. Seleziona in alto a sinistra main/myLibrary/remote per visualizzare tutti i branch. Seleziona un branch e fai clic su checkout.
Creazione dei dati
Abbiamo bisogno dei dati dei nostri utenti per creare dei grafici. I dati vengono creati utilizzando classi di dominio e servizi Grails per generare voci realistiche per utenti, autori, libri e record di prestito. Per maggiori informazioni, consulta la documentazione GORM.
Puoi anche dare un’occhiata al file appena aggiunto situato in app/myLibrary/grails-app/controllers/my/library/MyLibraryDataController.groovy.
Per creare i dati, richiama semplicemente /myLibraryData/createDataUsers nella barra degli indirizzi del tuo browser. Nota che questa operazione non cancellerà i dati creati in precedenza e non creerà nuovamente i dati se sono già stati generati.
Creazione dei grafici
Codice di esempio
Per creare un grafico, utilizzerai un UiDiagramSpecifier(). Esiste una varietà di diverse opzioni di grafico (a barre, a linee, a torta, box-and-whiskers, ad area). Per vedere come implementarli tutti, fai riferimento a DSL dei diagrammi.
In questo esempio, vogliamo visualizzare il numero di libri per autore per i primi 10 autori con il maggior numero di libri nella biblioteca.
Per prima cosa, dobbiamo recuperare le informazioni da MyLibraryBook.
List<Object[]> results = MyLibraryBorrowed.executeQuery("""\
select book.author, count(book)
from MyLibraryBook book
group by book.author
""", [max: 10])
Qui memorizziamo il cognome (lastName) di ciascuno dei primi 10 autori insieme al relativo numero di libri.
Ora dobbiamo inserire queste informazioni in un formato leggibile per il DiagramSpecifier.
Map<String, BigDecimal> authorCounts = [:]
results.each { row ->
String lastName = row[0] as String
Long count = row[1] as Long
authorCounts[lastName] = count.toBigDecimal()
}
Ora possiamo creare il grafico a torta.
new UiDiagramSpecifier().ui {
pie(hasSlice, {
List<String> authorsSorted = authorCounts.keySet().toList()
List<BigDecimal> counts = authorsSorted.collect { authorCounts[it] }
// For pie, each dataset entry is a separate slice with its label
labels "Author Pie"
(0..<authorsSorted.size()).each { i ->
dataset(authorsSorted[i], counts[i])
}
})
}
Questo codice è già stato implementato per te in MyLibraryUiService, insieme al menu Diagram. Abbiamo avviato l’implementazione di listDiagram per comodità. Dai un’occhiata a MyLibraryController.
Puoi avviare il server e navigare fino al menu Diagram per vedere il grafico a torta di esempio. Creiamo ora i nostri diagrammi.
Grafico a torta (Pie Diagram)
Vogliamo poter visualizzare quali libri sono i più popolari tra i mutuatari. Per questo, abbiamo deciso di creare un grafico a torta che mostri i primi 10 libri più presi in prestito.
Implementazione
UiDiagramSpecifier buildBookPopularityPieDiagram(boolean hasSlice) {
Map<String, BigDecimal> bookCounts = [:]
List<Object[]> results = MyLibraryBorrowed.executeQuery("""\
select bi.book.title, count(borrowed)
from MyLibraryBorrowed borrowed
join borrowed.bookInstance bi
group by bi.book.title
order by count(borrowed) desc
""", [max: 10])
results.each { row ->
String title = row[0] as String
Long count = row[1] as Long
bookCounts[title] = count.toBigDecimal()
}
new UiDiagramSpecifier().ui {
pie(hasSlice, {
List<String> titlesSorted = bookCounts.keySet().toList()
List<BigDecimal> counts = titlesSorted.collect { bookCounts[it] }
labels "Pie"
(0..<titlesSorted.size()).each { i ->
dataset(titlesSorted[i], counts[i])
}
})
}
}
Rendering del grafico
UiDiagramSpecifier diagramPieSpec = myLibraryUiService.buildBookPopularityPieDiagram(true) // TODO 1.2.1
diagram diagramPieSpec, {label "Book Popularity Pie"} // TODO 1.2.2
Ora puoi riavviare il server e navigare fino al menu Diagram per vedere il grafico a torta appena creato.
Grafico a barre (Bar Diagram)
Vogliamo visualizzare il numero di libri richiesti dai mutuatari. Per questo, abbiamo deciso di creare un grafico a barre che mostri il numero di richieste (per anno, mese e giorno).
Implementazione
UiDiagramSpecifier barDiagram(boolean isStacked) {
new UiDiagramSpecifier().ui {
bar(isStacked, {
labels "T1", "T2", "T3", "T4"
dataset 'Truc1', 1.0, 2.0, 1.0, 4.0
dataset 'Truc2', 2.0, 0.1, 1.0, 0.0
dataset 'Truc3', 2.0, 0.1, 1.0, 1.0
})
}
}
buildBarDiagram:UiDiagramSpecifier buildBarDiagram(boolean isStacked, String labelDateFormat) {
Map<Date, BigDecimal> borrowedCounts = [:]
List<Object[]> results1 = MyLibraryBorrowed.executeQuery("""\
select count(borrowed), borrowed.requestDate
from MyLibraryBorrowed borrowed
group by borrowed.requestDate
""")
results1.each { row ->
Long count = row[0] as Long
Date date = row[1] as Date
borrowedCounts[date] = (borrowedCounts[date] ?: 0) + count.toBigDecimal()
}
new UiDiagramSpecifier().ui {
bar(isStacked, {
List<Map.Entry<Date, BigDecimal>> countsSorted = borrowedCounts.entrySet().sort { it.key }.collect { it }
labels((labelDateFormat ?: 'YEAR') as DiagramXLabelDateFormat, countsSorted*.key as Date[]) //(1)
dataset('Borrowed Books', countsSorted*.value as BigDecimal[])
})
}
}
Per impostazione predefinita, il grafico verrà visualizzato per anno.
Rendering
UiDiagramSpecifier diagramBarSpec = myLibraryUiService.buildBarDiagram(true, labelDateFormat) // TODO 2.2.1
// TODO 2.2.2
diagram(diagramBarSpec, {
menu 'Yearly', this.&listDiagrams as MethodClosure, [labelDateFormat: 'YEAR']
menu 'Monthly', this.&listDiagrams as MethodClosure, [labelDateFormat: 'MONTH']
menu 'Daily', this.&listDiagrams as MethodClosure, [labelDateFormat: 'DAY']
})
Ora puoi riavviare il server per vedere il grafico a barre appena creato.
Grafico a scatola e baffi (Whiskers Diagram)
Vogliamo poter visualizzare il numero di giorni per cui un libro viene preso in prestito dai mutuatari. Per ottenere questo risultato, abbiamo deciso di creare un grafico a scatola e baffi (whiskers) che mostri la distribuzione dei libri presi in prestito all’interno di ciascun intervallo di giorni specificato (1–3, 4–7, 8–14, 15–20 e oltre 21).
Implementazione
diagram new UiDiagramSpecifier().ui({
whiskers {
labels "T1", "T2", "T3", "T4"
dataset 'Truc1', {
boxData 1.0, 2.0, 3.0, 3.5, 4.0, 4.5, 5.0
boxData 1.5, 2.5, 3.5, 6.0, 7.0, 8.0, 9.0
boxData 2.0, 2.0, 2.1, 2.5, 5.5, 5.6, 6.7
boxData 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3
}
}
})
buildBorrowDurationWhiskersDiagram:UiDiagramSpecifier buildBorrowDurationWhiskersDiagram() {
Map<Integer, BigDecimal> borrowedDates = [:]
Map<String, List<BigDecimal>> bins = [
"1-3d" : [],
"4-7d" : [],
"8-14d" : [],
"15-20d": [],
"21+d" : []
]
List<Object[]> results1 = MyLibraryBorrowed.executeQuery("""\
select borrowed.approvalDate, borrowed.returnDate
from MyLibraryBorrowed borrowed
where borrowed.approvalDate is not null and borrowed.returnDate is not null
""")
results1.each { row ->
Date approvalDate = row[0] as Date
Date returnDate = row[1] as Date
long durationMillis = returnDate.time - approvalDate.time
int duration = (durationMillis / (1000 * 60 * 60 * 24)).toInteger()
borrowedDates[duration] = (borrowedDates[duration] ?: 0.toBigDecimal()) + 1.toBigDecimal()
BigDecimal durationBD = duration as BigDecimal
if (duration <= 3) bins["1-3d"] << durationBD
else if (duration <= 7) bins["4-7d"] << durationBD
else if (duration <= 14) bins["8-14d"] << durationBD
else if (duration <= 20) bins["15-20d"] << durationBD
else bins["21+d"] << durationBD
}
new UiDiagramSpecifier().ui {
whiskers {
labels bins.keySet() as String[]
dataset('Borrow Durations', {
bins.each { label, values ->
if (values) {
List<BigDecimal> sorted = values.sort()
// min, Q1, median, Q3, max + dummy outliers if needed
boxData sorted[0], sorted[(int)(sorted.size()*0.25)], sorted[(int)(sorted.size()*0.5)],
sorted[(int)(sorted.size()*0.75)], sorted[-1],
sorted[0], sorted[-1]
} else {
boxData 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
}
}
})
}
}
}
Rendering
UiDiagramSpecifier durationDiagramSpec = myLibraryUiService.buildBorrowDurationWhiskersDiagram() // TODO 3.2.1
diagram durationDiagramSpec // TODO 3.2.2
Ora puoi riavviare il server per vedere il grafico a scatola e baffi appena creato.
Soluzione
package sid.books
import crew.Role
import crew.User
import crew.UserRole
import grails.compiler.GrailsCompileStatic
import grails.gorm.transactions.Transactional
import grails.plugin.springsecurity.annotation.Secured
import java.text.SimpleDateFormat
import java.time.LocalDate
import java.time.ZoneId
@Secured(['ROLE_ADMIN'])
@GrailsCompileStatic
class GraphsController {
Random random = new Random(42)
def index() {
redirect action: 'createDataUsers'
}
@Secured(['ROLE_ADMIN'])
@Transactional
def createDataUsers() {
Role borrowerRole = Role.findByAuthority('ROLE_BORROWER') ?: new Role(authority: "ROLE_BORROWER")
borrowerRole.save(flush: true)
Role adminRole = Role.findByAuthority('ROLE_ADMIN') ?: new Role(authority: "ROLE_ADMIN")
adminRole.save(flush: true)
List<Map> adminUsers = [
[firstName: 'John', lastName: 'Doe', username: 'jdoe'],
[firstName: 'Alice', lastName: 'Smith', username: 'asmith'],
[firstName: 'Brian', lastName: 'Williams', username: 'bwilliams']
]
List<Map> borrowerUsers = [
[firstName: 'Emily', lastName: 'Johnson', username: 'ejohnson'], [firstName: 'Michael', lastName: 'Brown', username: 'mbrown'],
[firstName: 'Jessica', lastName: 'Davis', username: 'jdavis'], [firstName: 'David', lastName: 'Miller', username: 'dmiller'],
[firstName: 'Sarah', lastName: 'Wilson', username: 'swilson'], [firstName: 'Daniel', lastName: 'Moore', username: 'dmoore'],
[firstName: 'Laura', lastName: 'Taylor', username: 'ltaylor'], [firstName: 'James', lastName: 'Anderson', username: 'janderson'],
[firstName: 'Olivia', lastName: 'Thomas', username: 'othomas'], [firstName: 'Matthew', lastName: 'Jackson', username: 'mjackson'],
[firstName: 'Sophia', lastName: 'White', username: 'swhite'], [firstName: 'Anthony', lastName: 'Harris', username: 'aharris'],
[firstName: 'Isabella', lastName: 'Martin', username: 'imartin'], [firstName: 'Andrew', lastName: 'Thompson', username: 'athompson'],
[firstName: 'Megan', lastName: 'Garcia', username: 'mgarcia'], [firstName: 'Joshua', lastName: 'Martinez', username: 'jmartinez'],
[firstName: 'Grace', lastName: 'Robinson', username: 'grobinson'], [firstName: 'Christopher', lastName: 'Clark', username: 'cclark'],
[firstName: 'Natalie', lastName: 'Rodriguez', username: 'nrodriguez'], [firstName: 'Brandon', lastName: 'Lewis', username: 'blewis'],
[firstName: 'Ashley', lastName: 'Walker', username: 'awalker'], [firstName: 'Ryan', lastName: 'Hall', username: 'rhall'],
[firstName: 'Abigail', lastName: 'Allen', username: 'aallen'], [firstName: 'Justin', lastName: 'Young', username: 'jyoung'],
[firstName: 'Samantha', lastName: 'Hernandez', username: 'shernandez'], [firstName: 'Jacob', lastName: 'King', username: 'jking'],
[firstName: 'Madison', lastName: 'Wright', username: 'mwright'], [firstName: 'Ethan', lastName: 'Lopez', username: 'elopez'],
[firstName: 'Elizabeth', lastName: 'Hill', username: 'ehill'], [firstName: 'Alexander', lastName: 'Scott', username: 'ascott'],
[firstName: 'Victoria', lastName: 'Green', username: 'vgreen'], [firstName: 'Benjamin', lastName: 'Adams', username: 'badams'],
[firstName: 'Chloe', lastName: 'Baker', username: 'cbaker'], [firstName: 'William', lastName: 'Nelson', username: 'wnelson'],
[firstName: 'Ella', lastName: 'Carter', username: 'ecarter'], [firstName: 'Joseph', lastName: 'Mitchell', username: 'jmitchell'],
[firstName: 'Mia', lastName: 'Perez', username: 'mperez'], [firstName: 'Noah', lastName: 'Roberts', username: 'nroberts'],
[firstName: 'Lily', lastName: 'Turner', username: 'lturner'], [firstName: 'Logan', lastName: 'Phillips', username: 'lphillips'],
[firstName: 'Zoe', lastName: 'Campbell', username: 'zcampbell'], [firstName: 'Jayden', lastName: 'Parker', username: 'jparker'],
[firstName: 'Hannah', lastName: 'Evans', username: 'hevans'], [firstName: 'Lucas', lastName: 'Edwards', username: 'ledwards'],
[firstName: 'Avery', lastName: 'Collins', username: 'acollins'], [firstName: 'Mason', lastName: 'Stewart', username: 'mstewart'],
[firstName: 'Scarlett', lastName: 'Sanchez', username: 'ssanchez'], [firstName: 'Jack', lastName: 'Morris', username: 'jmorris'],
[firstName: 'Aria', lastName: 'Rogers', username: 'arogers'], [firstName: 'Henry', lastName: 'Reed', username: 'hreed'],
[firstName: 'Amelia', lastName: 'Cook', username: 'acook'], [firstName: 'Sebastian', lastName: 'Morgan', username: 'smorgan'],
[firstName: 'Layla', lastName: 'Bell', username: 'lbell'], [firstName: 'Owen', lastName: 'Murphy', username: 'omurphy'],
[firstName: 'Ella', lastName: 'Bailey', username: 'ebailey'], [firstName: 'Gabriel', lastName: 'Rivera', username: 'grivera'],
[firstName: 'Aubrey', lastName: 'Cooper', username: 'acooper'], [firstName: 'Carter', lastName: 'Richardson', username: 'crichardson'],
[firstName: 'Sofia', lastName: 'Cox', username: 'scox'], [firstName: 'Wyatt', lastName: 'Howard', username: 'whoward'],
[firstName: 'Riley', lastName: 'Ward', username: 'rward'], [firstName: 'Dylan', lastName: 'Torres', username: 'dtorres'],
[firstName: 'Camila', lastName: 'Peterson', username: 'cpeterson'], [firstName: 'Nathan', lastName: 'Gray', username: 'ngray'],
[firstName: 'Leah', lastName: 'Ramirez', username: 'lramirez'], [firstName: 'Elijah', lastName: 'James', username: 'ejames'],
[firstName: 'Hazel', lastName: 'Watson', username: 'hwatson'], [firstName: 'Isaac', lastName: 'Brooks', username: 'ibrooks'],
[firstName: 'Lillian', lastName: 'Sanders', username: 'lsanders'], [firstName: 'Matthew', lastName: 'Price', username: 'mprice']
]
def allUsers = adminUsers.collect {it -> it + [role: 'ADMIN'] } +
borrowerUsers.collect { it -> it + [role: 'BORROWER'] }
allUsers.each {
item ->
{
User user = User.findByUsername(item.username as String)
if (!user) {
user = new User(
username: item.username,
password: '{noop}123',
businessUnit: 'IT',
firstName: item.firstName,
lastName: item.lastName
)
user.save(flush: true)
println "Created ${item.role} user: ${user.username} with errors:=${user.errors}"
} else {
println "Admin user: ${user.username} already created"
}
UserRole userRole = null
if (item.role == "ADMIN") {
if (!UserRole.findByUserAndRole(user, adminRole)) {
userRole = new UserRole(user: user, role: adminRole)
}
} else {
if (!UserRole.findByUserAndRole(user, borrowerRole)) {
userRole = new UserRole(user: user, role: borrowerRole)
}
}
if (userRole) {
userRole?.save(flush: true)
println "Linked ${item.username} to ROLE_${item.role}"
}
}
}
createDataAuthorsBooks()
}
@Transactional
def createDataAuthorsBooks() {
List<Map> authorData = [
[firstName: 'Emily', lastName: 'Stone', dob: '1975-04-12'],
[firstName: 'Michael', lastName: 'King', dob: '1968-11-03'],
[firstName: 'Olivia', lastName: 'Reed', dob: '1982-06-25'],
[firstName: 'Daniel', lastName: 'Bennett', dob: '1959-09-17'],
[firstName: 'Sophia', lastName: 'Ward', dob: '1970-12-29'],
[firstName: 'James', lastName: 'Brooks', dob: '1965-03-05'],
[firstName: 'Isabella', lastName: 'Gray', dob: '1987-10-14'],
[firstName: 'Matthew', lastName: 'Kelly', dob: '1978-07-21'],
[firstName: 'Mia', lastName: 'Cook', dob: '1990-02-09'],
[firstName: 'Alexander', lastName: 'Murphy', dob: '1963-01-31'],
[firstName: 'Charlotte', lastName: 'Bailey', dob: '1984-05-16'],
[firstName: 'David', lastName: 'Cooper', dob: '1955-08-27'],
[firstName: 'Amelia', lastName: 'Rivera', dob: '1981-04-03'],
[firstName: 'Benjamin', lastName: 'Richardson', dob: '1973-09-12'],
[firstName: 'Evelyn', lastName: 'Cox', dob: '1969-06-18'],
[firstName: 'Lucas', lastName: 'Howard', dob: '1976-02-22'],
[firstName: 'Harper', lastName: 'Ward', dob: '1985-10-30'],
[firstName: 'Henry', lastName: 'Torres', dob: '1960-07-04'],
[firstName: 'Ella', lastName: 'Peterson', dob: '1989-03-27'],
[firstName: 'Jack', lastName: 'Gray', dob: '1974-12-08'],
]
authorData.each { data ->
Author author = Author.findByFirstNameAndLastName(data.firstName as String, data.lastName as String)
if (!author) {
author = new Author(
firstName: data.firstName,
lastName: data.lastName,
dateOfBirth: new SimpleDateFormat('yyyy-MM-dd').parse(data.dob as String),
listOfBooks: []
)
author.save(flush: true)
println "Created author: ${author.firstName} ${author.lastName} with errors:=${author.errors}"
} else {
println "Author: ${author.firstName} ${author.lastName} already created"
}
}
List<Map> booksData = [
[title: "The Silent Patient", description: "A thrilling psychological mystery novel.", numberOfPages: "336"],
[title: "Educated: A Memoir", description: "An inspiring journey through education and resilience.", numberOfPages: "352"],
[title: "Becoming", description: "A powerful memoir exploring identity and purpose.", numberOfPages: "448"],
[title: "Where the Crawdads Sing", description: "A captivating blend of mystery and nature writing.", numberOfPages: "384"],
[title: "Normal People", description: "A complex love story set in Ireland.", numberOfPages: "288"],
[title: "Atomic Habits", description: "A comprehensive guide to building good habits and breaking bad ones.", numberOfPages: "320"],
[title: "The Subtle Art of Not Giving a F*ck", description: "A brutally honest self-help book.", numberOfPages: "224"],
[title: "Thinking, Fast and Slow", description: "An in-depth analysis of human decision-making and psychology.", numberOfPages: "512"],
[title: "Sapiens: A Brief History of Humankind", description: "A sweeping exploration of human history.", numberOfPages: "464"],
[title: "Dune", description: "A classic science fiction epic.", numberOfPages: "896"],
[title: "The Great Gatsby", description: "A timeless novel of wealth, love, and loss.", numberOfPages: "180"],
[title: "1984", description: "A chilling dystopian classic about surveillance and control.", numberOfPages: "328"],
[title: "To Kill a Mockingbird", description: "A powerful novel about justice and morality.", numberOfPages: "336"],
[title: "The Catcher in the Rye", description: "A coming-of-age story of teenage alienation.", numberOfPages: "277"],
[title: "Brave New World", description: "A dystopian vision of a technologically controlled society.", numberOfPages: "311"],
[title: "The Alchemist", description: "A philosophical novel about destiny and dreams.", numberOfPages: "208"],
[title: "The Road", description: "A bleak and beautiful post-apocalyptic novel.", numberOfPages: "287"],
[title: "Gone Girl", description: "A dark psychological thriller with shocking twists.", numberOfPages: "422"],
[title: "The Girl on the Train", description: "A gripping psychological thriller about memory and murder.", numberOfPages: "395"],
[title: "The Book Thief", description: "A story of hope and loss in Nazi Germany.", numberOfPages: "552"],
[title: "Life of Pi", description: "An imaginative survival story with spiritual undertones.", numberOfPages: "460"],
[title: "The Kite Runner", description: "A novel of friendship and redemption set in Afghanistan.", numberOfPages: "371"],
[title: "The Fault in Our Stars", description: "A touching love story between teenagers with cancer.", numberOfPages: "313"],
[title: "Memoirs of a Geisha", description: "An immersive journey into Japanese culture and history.", numberOfPages: "434"],
[title: "The Help", description: "A novel about race, class, and friendship in the American South.", numberOfPages: "464"],
[title: "The Hunger Games", description: "A dystopian young adult adventure of survival.", numberOfPages: "374"],
[title: "Catch-22", description: "A satirical novel about the absurdities of war.", numberOfPages: "453"],
[title: "Pride and Prejudice", description: "A classic romantic novel with wit and insight.", numberOfPages: "279"],
[title: "Jane Eyre", description: "A gothic novel exploring love and independence.", numberOfPages: "500"],
[title: "Wuthering Heights", description: "A dark, passionate story of love and revenge.", numberOfPages: "416"],
[title: "Little Women", description: "A timeless novel about family and womanhood.", numberOfPages: "759"],
[title: "Beloved", description: "A haunting novel about slavery and memory.", numberOfPages: "324"],
[title: "One Hundred Years of Solitude", description: "A magical realism epic of a family’s generations.", numberOfPages: "417"],
[title: "The Handmaid’s Tale", description: "A dystopian novel exploring gender and power.", numberOfPages: "311"],
[title: "The Shining", description: "A terrifying psychological horror novel.", numberOfPages: "447"],
[title: "Dracula", description: "The classic gothic horror story of the vampire count.", numberOfPages: "418"],
[title: "Frankenstein", description: "The original science fiction horror novel.", numberOfPages: "280"],
[title: "Moby Dick", description: "An epic tale of obsession and revenge at sea.", numberOfPages: "635"],
[title: "War and Peace", description: "A sweeping historical epic set during Napoleon’s invasion of Russia.", numberOfPages: "1225"],
[title: "Crime and Punishment", description: "A psychological exploration of guilt and redemption.", numberOfPages: "671"],
[title: "The Brothers Karamazov", description: "A philosophical novel about faith, doubt, and family.", numberOfPages: "796"],
[title: "Les Misérables", description: "A story of injustice, redemption, and love in 19th-century France.", numberOfPages: "1463"],
[title: "Anna Karenina", description: "A tragic novel of love and societal constraints.", numberOfPages: "864"],
[title: "Madame Bovary", description: "A realist novel about desire and dissatisfaction.", numberOfPages: "329"],
[title: "The Odyssey", description: "The epic Greek poem of adventure and homecoming.", numberOfPages: "541"],
[title: "The Iliad", description: "An epic poem about heroism and the Trojan War.", numberOfPages: "683"],
[title: "A Tale of Two Cities", description: "A historical novel set during the French Revolution.", numberOfPages: "489"],
[title: "Great Expectations", description: "A coming-of-age story about ambition and love.", numberOfPages: "505"],
[title: "Oliver Twist", description: "A social novel exposing the plight of orphans in Victorian London.", numberOfPages: "554"],
[title: "David Copperfield", description: "A semi-autobiographical novel of personal growth.", numberOfPages: "624"],
[title: "Bleak House", description: "A satirical novel about the flaws of the legal system.", numberOfPages: "768"],
]
List<Author> authors = Author.list()
booksData.eachWithIndex { data, index ->
Author author = authors[random.nextInt(authors.size() - 1) + 1]
Book book = Book.findByTitle(data.title as String)
if (!book) {
book = new Book(
title: data.title,
author: author,
description: data.description,
numberOfPages: data.numberOfPages as int,
listOfBookInstance: []
)
book.save(flush: true)
println "Created book: ${book.title} by ${book.author.firstName} ${book.author.lastName} (${data.numberOfPages} pages) with errors:=${author.errors}"
} else {
println "Book: ${book.title} by ${book.author.firstName} ${book.author.lastName} (${data.numberOfPages} pages) already created"
}
}
createDataBookInstances()
}
@Transactional
def createDataBookInstances() {
List<Integer> instanceCounts = [
50, 43, 0, 39, 50, 10, 33, 30, 50, 5, 38, 4, 12, 43, 47, 0, 19, 22, 3, 17,
14, 4, 50, 27, 43, 1, 2, 25, 31, 0, 7, 4, 20, 7, 3, 29, 0, 44, 24, 9,
44, 8, 0, 31, 28, 13, 15, 20, 6, 17, 26, 0, 40, 27, 13, 0, 15, 10, 32, 4,
41, 10, 22, 0, 25, 19, 4, 3, 30, 27, 9, 18, 22, 0, 8, 12, 31, 26, 18, 0,
13, 24, 50, 11, 3, 0, 12, 5, 4, 37, 2, 49, 12, 21, 35, 0, 50, 33, 0, 17,
44, 10, 33, 30, 50, 5, 38, 4, 12, 43, 47, 0, 10, 22, 3, 17, 14, 4, 50, 27,
43, 1, 10, 25, 31, 0, 7, 4, 20, 7, 3, 29, 0, 44, 24, 9, 44, 8, 0, 31, 28,
13, 15, 19, 6, 17, 26
]
List<Book> books = Book.list(sort: 'id')
books.eachWithIndex { book, idx ->
if (book.numberOfInstances == 0) {
int count = instanceCounts[idx]
(1..count).each { index ->
BookInstance instance = new BookInstance(
book: book,
isActive: true,
serialNumber: random.nextInt(100000),
isAvailableB: true,
borrowHistoryOfBook: []
)
instance.save(flush: true)
}
println "Created ${count} instances for book: ${book.title}"
} else {
println "Book instances for book: ${book.title} already created"
}
}
createDataBorrowed()
}
@Transactional
def createDataBorrowed() {
Role borrowerRole = Role.findByAuthority('ROLE_BORROWER')
List<UserRole> userRoles = UserRole.findAllByRole(borrowerRole)
List<User> borrowerUsers = userRoles*.user
List<BookInstance> bookInstances = BookInstance.list(sort: 'id')
List<User> someUsers = User.findAll();
bookInstances.each { instance ->
if (BorrowedBooks.countByBookInstance(instance) > 0) {
println "Skipping bookInstance id=${instance.id}, already has borrow records"
return
}
int borrowCount = random.nextInt(31)
LocalDate currentDate = LocalDate.of(2020, 1, 1).plusDays(random.nextInt(365 * 3))
(1..borrowCount).each { idx ->
int duration = 1 + random.nextInt(22)
Date requestDate = Date.from(currentDate.atStartOfDay(ZoneId.systemDefault()).toInstant())
Date approvalDate = null
Date returnDate = null
ApprovalStatus status
Boolean isAvailableB = true
if (idx == borrowCount && borrowCount % 2 == 0) {
if (borrowCount % 3 == 0) {
status = ApprovalStatus.APPROVED
approvalDate = requestDate
} else {
status = ApprovalStatus.PENDING
}
isAvailableB = false
} else {
approvalDate = requestDate
currentDate = currentDate.plusDays(duration)
returnDate = Date.from(currentDate.atStartOfDay(ZoneId.systemDefault()).toInstant())
status = ApprovalStatus.APPROVED
int gap = 1 + random.nextInt(30)
currentDate = currentDate.plusDays(gap)
}
User user = borrowerUsers[random.nextInt(borrowerUsers.size())]
BorrowedBooks borrowed = new BorrowedBooks(
bookInstance: instance,
user: user,
requestDate: requestDate,
approvalDate: approvalDate,
returnDate: returnDate,
statusOfApproval: status,
)
instance.isAvailableB = isAvailableB
borrowed.save(flush: false)
instance.save(flush: false)
}
println "Created ${borrowCount} borrowed entries for bookInstance id=${instance.id}"
}
redirect controller: 'sidBooks', action: 'listAuthor'
}
}