Friday, December 21, 2007

SnTT: A Simple Notes Based Document Rating Idea

Today, I wanted to whip up a simple document rating system that could be used in the Notes client. We see these all the time on the web. Netflix, a site I use regularly, has a nice implementation, as do many others. Jake showed us a nice version using Domino just recently too. I wanted to come up with a pure Notes solution, so I decided to make use of the editable view column property to accomplish this and it turns out it is very easy. I'll refer you back to this post, where I first discussed the concept of using an editable view column to perform an action. Before I review the specifics of the implementation, here's a quick movie to see how it works.

Click for animated demo

This is just a first pass, but I can see that this could be very handy. Basically all that is happening is that we are checking which star (column) is being clicked in the InViewEdit event. Then, we're equating this column to a value, which we add on to the total votes count to date divided by the number of votes. Then, in the final step of the InViewEdit code, we write the new rating value back to the document. I even added a simple check to make sure the user doesn't vote more than once.

Let's break it down step by step:

Step 1: Add columns to your view. I created a separate column for each star, since I want to know explicitly which one was clicked. Set the column to be editable and set it to "Display values as icons".

Step 2: The column value checks the current rating and based on the position of the column determines if it should show the filled or unfilled star.

@If(num_Rating >= 1; "star_red"; "star_open")

Step 3: Add the code to the InViewEdit event. I've included a bunch of comments so you can see what I was going for

Sub Inviewedit(Source As Notesuiview, Requesttype As Integer, Colprogname As Variant, Columnvalue As Variant, Continue As Variant)

Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim caret As String

'Get the CaretNoteID - exit if it does not point at a document
caret = Source.CaretNoteID
If caret = "0" Then Exit Sub

'Get the current database and document
Set db = Source.View.Parent
Set doc = db.GetDocumentByID(caret)

'Check if user already voted by looking if the user's name is in the nam_UsersRated field
'This is just one possible approach. Not sure how this will scale...might want to create a
'new doc in the backend that just records the user's vote for this doc. Have to explore...
Dim alreadyRated As NotesItem
Dim check As Variant
Dim currentUser As String

currentUser = session.UserName
check = Evaluate ( |@IsMember("| & currentUser & |" ; nam_UsersRated)| , doc )
If check(0) = 1 Then
Messagebox "'ve already voted for this document"
Exit Sub
End If

'If we got this far, the user hasn't voted yet, so we'll take their entry
Dim numRatings As Double '# of users who have rated doc
Dim voteTotal As Double 'numeric total of votes
Dim newRating As Integer 'the new rating for the doc (1 to 5)

numRatings = doc.num_NumberOfRatings(0) + 1
voteTotal = doc.num_VoteTotal(0)

Select Case Colprogname(0)
Case "Star1"
voteTotal = voteTotal + 1
Case "Star2"
voteTotal = voteTotal + 2
Case "Star3"
voteTotal = voteTotal + 3
Case "Star4"
voteTotal = voteTotal + 4
Case "Star5"
voteTotal = voteTotal + 5
End Select

newRating = Round((voteTotal / numRatings), 1)

doc.num_Rating = newRating
doc.num_VoteTotal = voteTotal
doc.num_NumberOfRatings = numRatings

'Add the user's name to the list of people that voted
Set item = doc.GetFirstItem( "nam_UsersRated" )
Call item.AppendToTextList( session.UserName )

Call doc.Save(True, False, True)

End Sub

This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at


Of course, before you implement something like this, you need to think through the implications of it in a production database. Do all users have the ability to edit documents? If not, perhaps you could use stub documents instead. Also, do you want to give the users the ability to see their vote vs. the actual vote? All of these things can be done...this is just the tip of the iceberg.

While this is really rough and I can think of several enhancements to be made, please feel free to download the sample database if you want to play around with it. If you actually use this technique or enhance it, please let me know...I'd be interested to see it.

Have a great holiday everyone!


Mac Guidera said...

yoink!...much appreciated...thanks!

Nathan T. Freeman said...

F'ing brilliant, yet again.

See your email. :-D

Thilo Hamberger said...

Chris, I like the idea alot but I am not happy with giving all "readers" author access. Can this work with mail-in or two databases?

Chris Blatnick said...

@Thilo...Sure. You can do just about anything with this. Instead of updating the actual document in the InViewEdit code, go ahead and create a "Vote" document in that database or a separate one. You could then use the same idea of updating the vote average on that new document.

tbahn said...

Hi Chris,

once again, a GREAT idea about using what IBM has given us in ways, no other would or could even think of :-D


Dmytro said...

very nice. keep this way :)
really impressive

Familie Kats said...

Real nice, I was just looking for this and not sure what to do. Your code is 100% usable for me, so thanks again..!

Anonymous said...

Great Idea..and great code..Like Manna from Heaven

Anonymous said...

I like it! But this will only work for databases having no "unread/read" marks. When a user rates a document it will be marked as "unread" for the other users.

Anyaway it's cool.