Grails Mahout Plugin with Background Job (quartz2) - grails

I am new in Grails. I am using Mahout Recommender Plugin for creating Recommender Engine. I am following this tutorial. It works fine for me.
Now What I am trying to do, I just want to do this Mahout Recommendation using Background JOB. So that User can recommendate automatically based on What they like. I am using Quartz 2.x Scheduler for Background Job purpose. How can I use Mahout as a Background JOB??
In this tutorial, You can see I have created one PrefrenceController.groovy where I am adding userID, itemID and prefrence number.
My Prefrence.groovy domain class file --
package com.rbramley.mahout
import org.apache.commons.lang.builder.HashCodeBuilder
class Preference implements Serializable {
long userId
long itemId
float prefValue
static constraints = {
userId()
itemId()
prefValue range: 0.0f..5.0f
}
boolean equals(other) {
if(!(other instanceof Preference)) {
return false
}
other.userId == userId && other.itemId == itemId
}
int hashCode() {
def builder = new HashCodeBuilder()
builder.append userId
builder.append itemId
builder.toHashCode()
}
static mapping = {
id composite: ['userId', 'itemId']
version false
}
}
and my PrefrenceController.groovy
package com.rbramley.mahout
import org.springframework.dao.DataIntegrityViolationException
class PreferenceController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index() {
redirect(action: "list", params: params)
}
def list(Integer max) {
params.max = Math.min(max ?: 10, 100)
[preferenceInstanceList: Preference.list(params), preferenceInstanceTotal: Preference.count()]
}
def create() {
[preferenceInstance: new Preference(params)]
}
def save() {
def preferenceInstance = new Preference(params)
if (!preferenceInstance.save(flush: true)) {
render(view: "create", model: [preferenceInstance: preferenceInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'preference.label', default: 'Preference'), preferenceInstance.id])
redirect(action: "show", id: preferenceInstance.id)
}
def show(Long id) {
def preferenceInstance = Preference.get(id)
if (!preferenceInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'preference.label', default: 'Preference'), id])
redirect(action: "list")
return
}
[preferenceInstance: preferenceInstance]
}
def edit(Long id) {
def preferenceInstance = Preference.get(id)
if (!preferenceInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'preference.label', default: 'Preference'), id])
redirect(action: "list")
return
}
[preferenceInstance: preferenceInstance]
}
def update(Long id, Long version) {
def preferenceInstance = Preference.get(id)
if (!preferenceInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'preference.label', default: 'Preference'), id])
redirect(action: "list")
return
}
if (version != null) {
if (preferenceInstance.version > version) {
preferenceInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'preference.label', default: 'Preference')] as Object[],
"Another user has updated this Preference while you were editing")
render(view: "edit", model: [preferenceInstance: preferenceInstance])
return
}
}
preferenceInstance.properties = params
if (!preferenceInstance.save(flush: true)) {
render(view: "edit", model: [preferenceInstance: preferenceInstance])
return
}
flash.message = message(code: 'default.updated.message', args: [message(code: 'preference.label', default: 'Preference'), preferenceInstance.id])
redirect(action: "show", id: preferenceInstance.id)
}
def delete(Long id) {
def preferenceInstance = Preference.get(id)
if (!preferenceInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'preference.label', default: 'Preference'), id])
redirect(action: "list")
return
}
try {
preferenceInstance.delete(flush: true)
flash.message = message(code: 'default.deleted.message', args: [message(code: 'preference.label', default: 'Preference'), id])
redirect(action: "list")
}
catch (DataIntegrityViolationException e) {
flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'preference.label', default: 'Preference'), id])
redirect(action: "show", id: id)
}
}
}
Suppose I have added some data manually in my Database. Now When User click on RecommendController and put particular userID then It will show Recommendation. But I want to do this as a background Job. Recommendation should be suggest automatically to all users without any human intervention.

If I understood your question you need an on-demand job that you can run it from your controller. To do so create a job without any trigger, then you can trigger it manually from your Controller and pass into it your parameters. This will trigger the job on the background. In the execute define what the job needs to do, you can also inject any services that you need into that job.
Controller:
BackgroundJob.triggerNow([id:params.id,userId:userId])
Job
class BackgroundJob {
static triggers = {}
def execute(context) {
def id = context.mergedJobDataMap.get('id')
def userId = context.mergedJobDataMap.get('userId')
...
}
}

Related

How to write a business logic and save in the database or return to the user

I have 2 questions.
1.) I need to save values to the database AFTER making some changes to it. For example if an user parameter says Date of birth = 1990-09-14 then i want to calculate the age of the person and save it in the database. How can i do it. The code is shown below.
2.) Can someone explain me the following code:
def save() {
def appointmentInstance = new Appointment(params)
if (!appointmentInstance.save(flush: true)) {
render(view: "create", model: [appointmentInstance: appointmentInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'appointment.label', default: 'Appointment'), appointmentInstance.id])
redirect(action: "show", id: appointmentInstance.id)
}
This is what i understand:
def appointmentInstance = new Appointment(params) - It takes all the parameters and save it to an instance called appointmentInstance
if (!appointmentInstance.save(flush: true)) {
render(view: "create", model: [appointmentInstance: appointmentInstance])
return
}
What hapence here
3.) May i also know what happence in this example:
flash.message = message(code: 'default.created.message', args: [message(code: 'appointment.label', default: 'Appointment'), appointmentInstance.id])
redirect(action: "show", id: appointmentInstance.id)
To save the data into database after doing some change, you can do the following:
def save() {
// calculate the age
def age = // your calculation
// now you can save changes to db in 2 ways
// 1st way
params.age = age
def appointmentInstance = new Appointment(params)
if (!appointmentInstance.save(flush: true)) {
render(view: "create", model: [appointmentInstance: appointmentInstance])
return
}
// 2nd way
def appointmentInstance = new Appointment(params)
appointmentInstance.age = age
if (!appointmentInstance.save(flush: true)) {
render(view: "create", model: [appointmentInstance: appointmentInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'appointment.label', default: 'Appointment'), appointmentInstance.id])
redirect(action: "show", id: appointmentInstance.id)
}
def appointmentInstance = new Appointment(params)
- It takes all the parameters and save it to an instance called appointmentInstance
if (!appointmentInstance.save(flush: true)) {
render(view: "create", model: [appointmentInstance: appointmentInstance])
return
}
save() is a function of grails that retuns true or false.
The save method informs the persistence context that an instance should be saved or updated. The object will not be persisted immediately unless the flush argument is used.
When set to true flushes the persistence context, persisting the object immediately and updating the version column for optimistic locking
the purpose of render is to apply an inbuilt or user-defined Groovy template against a model so that templates can be shared and reused
If you are using render and you don't want to execute your next lines of code, then return statement is used. But in case of redirect, next line of code is not executed, so return is not used there.
flash.message = message(code: 'default.created.message', args: [message(code: 'appointment.label', default: 'Appointment'), appointmentInstance.id])
Flash is a temporary storage map that stores objects within the session for the next request and the next request only, automatically clearing out the objects held there after the next request completes.
Generally used to show messages on gsp.
message(code: 'default.created.message', args: [message(code: 'appointment.label', default: 'Appointment'), appointmentInstance.id])
This code uses the messages.properties file located in i18n folder, All the default messages of grails are stored here. You can also add your custom messages here and provide a dynamic value to your message.
redirect(action: "show", id: appointmentInstance.id)
To redirect flow from one action to the next using an HTTP redirect. You can send id as a parameter in your url.

Grails Controller Edit returns a map?

When I generate a controller for a domain class named User I get this code for the edit action:
def edit(Long id) {
def userInstance = User.get(id)
if (!userInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), id])
redirect(action: "list")
return
}
[userInstance: userInstance]
}
Can someone please explain why this returns [userInstance: userInstance], rather thank just userInstance
Thank you!
The controller returns a map containing the data that you will (presumably) use in your view. The map keys are the names you use to this data from your view. Perhaps it would be a little less confusing if you renamed the map key, e.g.
def edit(Long id) {
def userInstance = User.get(id)
if (!userInstance) {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), id])
redirect(action: "list")
return
}
[user: userInstance]
}
To get the id of the User in your view, you would use:
${user.id}

saving a second version of a grails object

one thing I would like to do is version the entries in our database. In my update method I would like to save a newer second version of an object. For instance
def update = {
def VariantInstance = Variant.get(params.id)
def NewVariantInstance = VariantInstance
NewVariantInstance.properties = params
if (VariantInstance) {
if (!VariantInstance.hasErrors()) {
VariantInstance.save()
NewVariantInstance.save()
flash.message = "${message(code: 'default.updated.message', args: [message(code: 'Variant.uniqueIdentifyingName', default: 'Variant'), VariantInstance.id])}"
redirect(action: "list")
}
else {
render(view: "edit", model: [VariantInstance: VariantInstance])
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'Variant.uniqueIdentifyingName', default: 'Variant'), params.id])}"
redirect(action: "list")
}
}
While this saves the current one, it does not create a new one. What am I doing wrong?
There's two problems; NewVariantInstance is going to just be a reference to the VariantInstance most likely, so it's dereferencing the same object. Additionally, when you do your params assignment you're also assigning the id field from VariantInstance to NewVariantInstance, so GORM will see the objects as the same when it does the save.

Getting error list from scaffolded function in Grails controller template

I'm using Grails 1.3.7. In a Grails project, I want to add a function ajaxupdate to the default scaffolded Controller, which does exactly the same job as the update function, but returns JSON data with the list of the eventual errors.
def ajaxupdate = {
String retMessage = ""
List errMessageList = []
def ${propertyName} = ${className}.get(params.id)
if (${propertyName}) {
${propertyName}.properties = params
if (!${propertyName}.hasErrors() && ${propertyName}.save(flush: true)) {
retMessage = "\${message(code: 'default.updated.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), ${propertyName}.id])}"
}
else {
// Add errors in errMessageList
}
}
else {
errMessageList.add("\${message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])}")
}
render(contentType: "text/json") {
answer(
message:retMessage,
errors:errMessageList)
}
}
If you're asking how to get the errors list, you can access Errors like
errMessageList = ${propertyName}.errors.allErrors.collect {g.message(error:it).encodeAsHTML()}

Grails Controllers adding instances

Alright I asked a question before but wasn't quite sure about it. So I went ahead and waited till now to ask again.
Main Question
How do I add a new instance of the domain through the controller? I created a function named gather to read a file with data and then create a new Book with the specific information, however it is not adding it to the database at all.
I currently have a controller (bookController) and the domain for it.
My domain is quite simple:
class Book {
static belongsTo = Author
String toString() { bookNumber }
Author bookAuthor
String title
static constraints = {
bookAuthor()
title()
}
}
I just 'generated' my views so I have the basic create, edit, list, and show. I went ahead and added my own inside the controller called gather. For the gsp, I just copied over the 'list.gsp' as I just want the user to view the list of books once the gather function is completed.
Here is what my controller looks like (just the basic generated one plus gather):
package bookdemo
import bookClient
class BookController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index = {
redirect(action: "list", params: params)
}
def gather = {
def w = new bookClient() //bookClient will gather books from txt files
def hosts = ["localhost"] //host to connect to
w.queryData(hosts) //grab information and parse
def abc = w.bookList //list of books
w.printData(abc) //print out list of books to make sure its not null
int numberOfBooks = abc.size() //list size
//create book list and return it
numberOfBooks.times {
def bookInstance = new Book(Author:"$abc.author", Title:"$abc.title")
return [bookInstance: bookInstance]
}
//params to show once adding books
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[bookInstanceList: book.list(params), bookInstanceTotal: book.count()]
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[bookInstanceList: book.list(params), bookInstanceTotal: book.count()]
}
def create = {
def bookInstance = new Book()
bookInstance.properties = params
return [bookInstance: bookInstance]
}
def save = {
def bookInstance = new Book(params)
if (bookInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])}"
redirect(action: "show", id: bookInstance.id)
}
else {
render(view: "create", model: [bookInstance: bookInstance])
}
}
def show = {
def bookInstance = book.get(params.id)
if (!bookInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
else {
[bookInstance: bookInstance]
}
}
def edit = {
def bookInstance = book.get(params.id)
if (!bookInstance) {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
else {
return [bookInstance: bookInstance]
}
}
def update = {
def bookInstance = book.get(params.id)
if (bookInstance) {
if (params.version) {
def version = params.version.toLong()
if (bookInstance.version > version) {
bookInstance.errors.rejectValue("version", "default.optimistic.locking.failure", [message(code: 'book.label', default: 'Book')] as Object[], "Another user has updated this Book while you were editing")
render(view: "edit", model: [bookInstance: bookInstance])
return
}
}
bookInstance.properties = params
if (!bookInstance.hasErrors() && bookInstance.save(flush: true)) {
flash.message = "${message(code: 'default.updated.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])}"
redirect(action: "show", id: bookInstance.id)
}
else {
render(view: "edit", model: [bookInstance: bookInstance])
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
}
def delete = {
def bookInstance = book.get(params.id)
if (bookInstance) {
try {
bookInstance.delete(flush: true)
flash.message = "${message(code: 'default.deleted.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "${message(code: 'default.not.deleted.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "show", id: params.id)
}
}
else {
flash.message = "${message(code: 'default.not.found.message', args: [message(code: 'book.label', default: 'Book'), params.id])}"
redirect(action: "list")
}
}
}
The gsp shows up but for some reason my new books are not added. When I add a println in to test whether the information is in the list, it shows prints with the correct info. So I'm confused as to why it is not 'creating' the new book instance and adding it to the database.
Any suggestions?
Edit
Domain class for author:
class Author {
static hasMany = [books:Book]
String authorName
String notes
String toString() { authorName }
static constraints = {
machineName()
notes(maxSize:500)
}
}
You're not calling .save() on any of the Book instances...
I would suggets you write some unit tests for you controller and/or domain objects. Your objects cannot be being created successfully with this code
new Book(Author:"$abc.author", Title:"$abc.title")
Also the return statement here makes no sense
numberOfBooks.times {
def bookInstance = new Book(Author:"$abc.author", Title:"$abc.title")
return [bookInstance: bookInstance]
}
It looks like you have cut and pasted code without understanding what the code is doing. I think you want something more like this...
// iterate through the list of books and create the object array to pass back
def bookListInstance = []
w.bookList.each {
def bookInstance = new Book(Author:it.author, Title:it.title)
bookListInstance << bookInstance
}
// now return the list of domain objects
return [bookInstance: bookListInstance]

Resources