Before I begin this entry, I wanted to post a quick notice about the code in my previous entry. I discovered a bug that broke the date filter. I've mentioned the fix in the comments but have not updated the zip. Rather, in this entry, I will include the fixed code along with the new version of the report I'll be discussing below. Even if you don't care for the new features I'm showing here, please grab the updated code if you want to run the simpler Twitter report. Thanks.
In my last blog entry, I talked about how you can use the Twitter API within ColdFusion to create a custom report. While reading through the documentation I found something interesting. Twitter's search (both API and public) includes some cool search operators. The two that interested me the most were the attitude flags. By appending either :) or :( to a search you can return tweets that have a positive or negative attitude. This could be very useful. For a company, it could help flag tweets that need a quicker response. For trending, you can see if a marketing campaign results in higher or lower positive results. Really, the use cases for this are endless. So how can we make use of this?
Unfortunately, there isn't (as far as I know) a way to get the "attitude" rating for general searches. This means that you have to perform a separate search to get the filtered results. Give my previous code, it means we would have to follow up the 10 (at most) network requests with 10 more (at most) requests. I'd have to perform N searches for positive results and N searches for negative results. Most likely these would be far less than 10 since we can assume Twitter hasn't built the "perfect" language parser yet. I'd then have to loop over these new results and match them up with the general results. I did ask on the Twitter Dev newsgroup if there was some way to get the attitude flag on generic searches, but I've yet to hear back.
So given that a) I assume their language parsing isn't perfect anyway and b) I don't really want to do more network requests, I've come up with a simpler system. I've created a simple UDF that works with two lists - a positive and negative list. For each I populated them with common words that you see in positive or negative tweets. So for example, #fail is very common in complaint tweets, so that is on my list. While certainly far from perfect, it does have another advantage as well. A company, like Adobe for example, could tailor the list to their needs. So for example, "slow" could be considered a negative term in regards to ColdFusion. "buggy" as well. So a company specific Twitter report could edit the lists to include stuff like that. You could also add your competition to the negative list as well.
So with that in mind, here is the simple UDF I built. If foul language offends you, well, you probably don't write much code. ;)
<!--- I return a mood value of either: positive, negative, or I return an empty string. I attempt to determine the mood using simple keyword checks. You can modify/improve by editing the lists. ---> <cffunction name="determineMood" output="false" returnType="string"> <cfargument name="twit" type="string" required="true">
<!--- List of positive stuff. Makes you feel warm and fuzzy. ---> <cfset var positiveList = ":),:>,:-),##ftw,awesome,great"> <!--- The negative list. Here lies darkness and dispair... ---> <cfset var negativeList = ":(,##fail,sucks,sux,shit,crap"> <cfset var i = "">
<cfloop index="i" list="#positiveList#"> <cfif findNoCase(i, arguments.twit)> <cfreturn "positive"> </cfif> </cfloop> <cfloop index="i" list="#negativeList#"> <cfif findNoCase(i, arguments.twit)> <cfreturn "negative"> </cfif> </cfloop> <cfreturn ""> </cffunction>
As you can see, this is extremely simply code here. Loop over each keyword in each list and leave immediately if there is a match. I do not pretend to be a language expert. But for basic checking this seems to work nice.
In my report I also added a simple counter. This then lets me parse the Twits and keep track:
<cfloop index="item" array="#data.results#"> <!--- do mood scanning. ---> <cfset mood = determineMood(item.text)> <cfif mood eq "positive"> <cfset positiveCount++> <cfset item.mood = "positive"> <cfelseif mood is "negative"> <cfset negativeCount++> <cfset item.mood = "negative"> <cfelse> <cfset item.mood = ""> </cfif> <cfset arrayAppend(results, item)> </cfloop>
I'm adding the mood before I append to my total results. Finally, I added the count to my report header and used some nice CSS to add a border to the tweets:
<h2>Twitter Search Results</h2> <p> The following report was generated for the search term(s): #search#.<br/> It contains matches found from <b>#dateFormat(yesterday,"mmmm dd, yyyy")#</b> to now.<br/> A total of <b>#arrayLen(results)#</b> result(s) were found.<br/> A total of <b>#positiveCount#</b> positive results were found.<br/> A total of <b>#negativeCount#</b> negative results were found.<br/> <cfif maxFlag><b>Note: The maximumum number of results were found. More may be available.</b><br/></cfif> <cfif errorFlag><b>Note: An error ocurred during the report.</b><br/></cfif> </p>
<cfloop index="x" from="1" to="#arrayLen(results)#"> <cfset twit = results[x]> <cfif x mod 2 is 0> <cfset class = "twit_even"> <cfelse> <cfset class = "twit_odd"> </cfif>
<!--- massage date a bit to remove +XXXX ---> <cfset twitdate = twit.created_at> <cfset twitdate = listDeleteAt(twitdate, listLen(twitdate, " "), " ")>
<p class="#class# <cfif twit.mood neq ''>twit_#twit.mood#</cfif>"> <img src="#twit.profile_image_url#" align="left" class="twit_profile_image"> <a href="http://twitter.com/#twit.from_user#">#twit.from_user#</a> #twit.text#<br/> <span class="twit_date">#twitdate#</span> <br clear="left"> </p> </cfloop>
The result is a bit large but I saved it as HTML and uploaded it here. As you scroll, notice the items with the green border on the left, or the red.
I've attached a zip of this to the blog entry. Oh, and I also added some CSS to help keep the images from being too large. Someone stop me before I turn into a designer.