Over the past several years, quite a few 'voting' solutions have passed through the drupal forums and CVS. Most are specific to nodes, and many are tightly integrated with a particular set of UI widgets (flash animations, etc).

Using some of the core code from voting.module, I've created a first stab at a module-independent voting API for Drupal. It's nothing to write home about, and most of it is directly adapted from the existing voting.module. I'm using it in another project and it's doing well so far, but I'd like to get others on board with the idea if possible. Is there any way that existing voting modules, or even comment moderation and other rating-based features, might be adapted to use a single simple API?

Comments

eaton’s picture

boris mann’s picture

Good job on picking up the ball with this. Probably best to contact the module others of, e.g., nodevotes, etc. to see what they think of the idea. I like that voting module could easily be extended to work with comments, users, or even taxonomy, just by adding extra tables.

Is that link supposed to point to the livejournal module? I'm a bit confused, unless you've somehow stuck it inside livejournal?

--
Please turn on the "story" type, so we can use it to have an archive of best practices, how tos, and configuration recipes.

eaton’s picture

Nope, that's just a very embarassing copy-paste error.

http://cvs.drupal.org/viewcvs/drupal/contributions/modules/votingapi/

is the correct directory.

I started with the core code from voting.module, as I think it's one of the best underlying implementations for its flexibility. It assums that a particular Flash movie or UI widget will be driving the UI, though.

Later today I do want to drop other voting module authors a line to talk about the possibilities. This basic API would be usable for thumbsup/thumbsdown voting, 1-10 style ratings, and other subtler stuff. My only concern is the potential slowdown if there are hojillions of votes in the table for a popular site.

--
Eaton — Partner at Autogram

eaton’s picture

Technically, extra tables wouldn't be necessary unless the module required more complexity. For example, glomping extra information onto a node once the number of votes passes 100, or what not.

In my 'latestgreatest' module I use a side table for speed optimization on large sites, as I don't need to load voting information once the total number of votes passes a certain threshold. In most cases, though, just setting the content-type to 'comment' or 'taxonomy' would be enough to save and retrieve votes for a given record.

--
Eaton — Partner at Autogram

Uwe Hermann’s picture

Good idea. Is anyone working on porting all current vote* modules to this API? It there an AJAX-style voting module in the works?

Thanks, Uwe.
--
hermann-uwe.de | crazy-hacks.org | unmaintained-free-software.org

moshe weitzman’s picture

schema looks good. module might be a b it too specific by returning max min and average. not sure.

i will port nmoderation to this API when it is ready. hopefully soon.

eaton’s picture

I want to emphasize that this module is based almost completely on benshell's work with voting.module. I just pulled out the flash dependencies and cleaned up one of the queries for the module I needed it for.

Part of the reason I'm soliciting thoughts from other people who've implemented voting modules is because I'm not sure what functionality would be universally applicable. What makes sense to centralize, beyond simple saving/retrieving?

I suppose if the schema is standardized, at least, writing custom queries against it would be simpler.

--
Eaton — Partner at Autogram

benshell’s picture

I haven't looked at your work yet, Eaton, but this is a good idea. I'm all for it. However, one thing I've been meaning to change about the voting.module, to make it more flexible, is to store the vote in the database using a scale of 0-100, rating than 1-5 (like it is). This way the table structure/values don't need to change for various voting scales. You use it for promote/demote of comments (0 or 1, which would be saved in the database as 0 or 100), you can use it for a 1-5 stars scale like I did (which would be saved as 0, 25, 50, 75, or 100), or whatever scale you want. You can even change scales without having to change the database values.

Functions like these could be used to go back and forth between the displayed scale that the user sees, and 0-100 scale that the database uses:

display2db($user_vote, $user_scale) {
  return ($user_vote - 1) * (100 / ($user_scale - 1));
}

db2display($db_value, $user_scale) {
  return ($db_value / 100) * ($user_scale - 1) + 1;
}
Uwe Hermann’s picture

Sounds good! I like the idea. Can you roll a patch for this?

Uwe.
--
hermann-uwe.de | crazy-hacks.org | unmaintained-free-software.org

eaton’s picture

One 'holy grail' application of a robust rating system would be a 'trust matrix' or karma-like system.

One user asks for ideas on something like this in a recent post: http://drupal.org/node/34042 ... A product node, with review nodes attached. Each review can be rated for 'helpfulness' or 'accuracy' by other users, and that rating determines how much 'weight' the review has. The 'weighted' reviews are used to calculate a single aggregate rating for the product itself.

I can imagine this voting API being used for something like that -- perhaps a set of two custom modules (product and review?) and ratings being applied to individual reviews.

Another more complex application would be a karma-style system. User (a) authors a node, and user (b) rates it. The karma of user (b) influences how much their rating affects user (a)'s karma, and the cycle repeats. I can imagine a nightly cron hook being used to calculate a user's overal karma from the aggregate ratings...

Any thoughts? Would an API based on these fundamentals be appropriate for those kind of applications?

--
Eaton — Partner at Autogram

budda’s picture

I've been using the votingapi in a custom module which allows rating of aggregator items for my site.

However I noticed that the current votingAPI doesn't provide any way to see if a user has already voted for the content_id being displayed.

I've done this check directly using some SQL for now, but maybe this functionality could live in the votingAPI too?

i use the check to decide wether a "vote" link should be added to the display.

--
www.bargainspy.co.uk

eaton’s picture

...it inherits the same behavior voting.module uses: if you load the 'vote' object for a given content ID, the 'vote' property of it contains the vote for the currently logged in user. Or, in the case of anonymous users, any vote cast by the user's IP address.

If that variable isn't set, the user hasn't yet voted...

I can definitely see how making this more explicit would be good in an API. In addition, it might be useful to be able to pull all of a user's votes in one batch. Thoughts?

--
Eaton — Partner at Autogram

budda’s picture

When i load the vote for a certain content id, i don't see where the specific users vote is? I only have two attributes in the returned object, neither of which *appear* to indicate they are specific to the current user:

object(stdClass)(2) { ["avg_vote"]=>  string(3) "1.0" ["total_votes"]=>  string(1) "1" }

If that is the case, then the votingapi_get_vote($content_type, $content_id) function needs to accept an optional 3rd parameter to supply an alternative user account to check.

Consider an admin member wanting to see if a user has voted on a certain item.

--
www.bargainspy.co.uk

eaton’s picture

I've created an actual project for the voting api (http://drupal.org/node/36041) so that future issues and feature requests can be pinned to it, rather than this thread. To the developers of other voting-modules, what do you think would prevent you from using a shared voting API at present? Is there functionality do you feel is currently missing or too different from your own modules?

I'd like to flesh out the voting infrastructure based on feedback here (per-user and per-node queries, instead of always returning aggregate values, for one). Some of the following scenerios could be important:

Threshold voting (ala user moderation)
Product Ratings?
Karma voting (integrated with userpoints, where a member uses their points to cast a vote, and the vote affects another user's points)

In the coming days I'll have some additional time to pick up work on the API again. Thoughts and feedback greatly appreciated.

--
Jeff Eaton | Click Here To Find Out Why Drupal "Sucks"

--
Eaton — Partner at Autogram

benshell’s picture

Hmm... I can't seem to post issues to this project page. I get the "Submit issue" page, but the Project name is blank and Voting API isn't available in the list.

Anyway, I've been doing a lot of work trying to finish up some changes to voting.module (some of which should go in votingapi.module). However, instead of finishing anything I've dug myself deeper into voting algorithms than I've ever wanted to go!

As I mentioned earlier in this thread, I'd like to be able to use different voting scales/ranges with the same database table. If the votingapi.module ever gets in core (which I think should be the goal) it needs to work for a lot of different purposes. I'd suggested storing all votes in the database using a scale of 0-100 and re-scaling all votes to fit that, and then scaling them back again for other uses. I got that method working, however I have some hesitations about it:

1) There are many ways to re-scale votes. The one I posted earlier was my first thought, but there are other possibilities too. The only voting system I'm aware of that does scale votes is Yahoo Music (Launchcast). You can rate songs/artists/albums with a 1-5 scale or a 1-100 scale. Votes made in the 1-5 scale translate to 10, 30, 50, 70, 90 in the 1-100 scale. That's different than my algorithm, and probably better. However, I haven't figured out the math to make this conversion, both ways, for every possible voting scale.

2) Could there ever be time when you'd want to know what scale a user voted with? This information would be lost with this design.

Anyway, I'm not comfortable imposing a database change on current users of voting.module unless I'm sure that it's the best move that will be the most compatible with future uses of voting.module and/or votingapi.module.

A different solution is to add a 'range' or 'scale' field to the database table. You'd still be able to use scaling algorithms, but only if you needed them. Plus, the algorithm(s) could be changed in the future without affecting the database content.

The problem I ran into with this method is calculating the average vote for a piece of content. It is possible, of course, but it gets to be much more difficult since the database could potentially have votes for a piece of content that use different scales! It could also be a little slower since it relies more on PHP, and less on MySQL queries.

An in between solution that I haven't tried yet is having the votes saved in 0-100 format, but also having a field for range/scale. As long as the algorithm worked both ways exactly the same, the original vote could always be recovered. If a new algorithm were developed it would be easy to scale votes back to their original values using the old algorithm and then scale them back up with the new algorithm.

I suppose another in between solution is having yet another field where votes could ALSO be saved as 0-100 for faster/easier calculation of the averages... but I hate redundant data so this is a last resort in my opinion.

And then there's also vote weighting used by user karma systems and some moderation systems. I haven't looked into how to implement this yet.

If anyone has read this far, do you have any ideas? The better the database structure we design we now, the easier it will be for everyone in the future.

eaton’s picture

I think you're definitely right in recognizing some of the issues. I've been spending a lot of time pondering this lately... and the reluctance to change the DB schema underneath your current users makes a lot of sense.

The reason I think a voting API of SOME sort is important is simple: many modules, and many areas of the Drupal system, involve mechanisms by which multiple users contribute a data point to arrive at some group decision. That may be 'How funny is this joke?' or 'Should this content be promoted to the front page?' or 'Do we trust this person's opinions?'

As Drupal's capabilities for building community sites grow, and more sites with LARGE communites are built on this foundation, I think it'll be more and more important. For community-oriented sites, I think that mechanisms for these systems can be as critical as image support or RSS feeds.

There are a couple of distinct questions, though, that keep coming up.

  1. How is data for a variety of voting mechanisms stored consistently? Thumbs up/thumbs down systems, 1-10 systems, 0-100% systems, multicriteria rating*, and 'microcommentary**' systems, like slashdot moderation, are all different options.
  2. If different modules can apply different voting metrics to pieces of content, will the 'community opinion' data become just as fragmented as with a collection of separate modules?
  3. Different modules (and different users) will want to act on this information in different ways. One module might want to promote highly-rated comments into front-page nodes for further discussion. Another might want to expose voting totals to end users and allow them to hide messages under a certain threshold. Another might want to change a user's role, or assign them userpoints, if they accumulate enough 'karma' via other users' votes on their content.

One possible answer to this is to provide a very very bare-bones voting API, only a step removed from the one that's currently posted. There are already fields for the vote itself, user information, and content-type information. Adding one additional field -- 'vote_type' -- would allow modules to capture information like 'this vote is a karma point' or 'this vote pertains to the given node's technical quality.'

The API would support saving and retrieving individual votes and sets of votes, and expose a set of hooks to allow modules to act on votes whenever they're cast. Some might even store their own additional information -- my 'latestgreatest' module stores its own record for each aggregator item that's reached a promotion threshold, so that the voting user-widgets are hidden once an item gets promoted. Once it hits the promotion point, no more vote aggregation is necessary. I can envision complicated user-trust or karma calculations happening in that way -- the vote API being used to collect 'raw data' and the karma module being used to calculate the results.

Thoughts? Feedback?

--Jeff

* With multicriteria rating, a given story might be rated both on concept and execution.

** With microcommentary systems, 'votes' are more like tags a user applies to a piece of content, some with positive and some with negative connotations. 'Funny,' 'Flamebait,' 'Informative,' 'Disingenuous,' and so on. Drupal's comment moderation matrix was a bit like this in 4.6, but it's been stripped out in 4.7 to be replaced by a module. That module has yet to be written, thus my early work on voting API :-)

--
Jeff Eaton | Click Here To Find Out Why Drupal "Sucks"

--
Eaton — Partner at Autogram

eaton’s picture

After spending some time away from it, I think I may have come up with a useful solution for the 'simple voting' scenerios and the 'complex multicriteria voting' scenerios that's flexible without being too topheavy.

I posted about it here... any comments or thoughts are welcome, there or in this thread.

--
Jeff Eaton | Click Here To Find Out Why Drupal "Sucks"

--
Eaton — Partner at Autogram

eaton’s picture

http://drupal.org/node/36041 has a completely new version of the voting API ready for perusal, if anyone's interested. I believe (knock on wood) that I've figured out a relatively clean solution to the problem of multi-criteria voting. Thoughts and feedback from anyone interested would be appreciated.

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

budda’s picture

I've got about 7 votes for the same piece of content in the "votes" table. In the old API it was simple to get a total votes.

In the new API I assume i need to implement a 'calculate' operation in my modules votingapi hook.

However, the $votes object passed in to my votingapi hook doesn't look too good. Specifically the ["raw_votes"] array contains a single item with no key.

["raw_votes"]=> array(1) { [""]=> object(stdClass)(8) { ["voting_id"]=> string(1) "7" ["content_type"]=> string(6) "report" ["content_id"]=> string(2) "25" ["vote"]=> string(1) "2" ["tag"]=> string(4) "clip" ["uid"]=> string(1) "1" ["timestamp"]=> string(10) "1133281896" ["hostname"]=> string(13) "192.168.123.6" } }

Bug? I think i should have 7 array items here based on the dbase content.

In function votingapi_get_votes() what should $voting_id be? This is coming out as an empty string in the current CVS code.

--
www.bargainspy.co.uk | More Drupal modules

budda’s picture

eaton’s picture

The bad key is, in fact, a bug. Fixing it shortly.

I want to emphasize that the VotingAPI as it stands right now (the CVS version, not the 4.6 tagged version) is not at all functionally complete. To solve the problem of multicriteria voting, I had to drop down a level. Implementing the functionality of the 4.6 version will require another layer on top of the basic set/get vote calls.

I've been tinkering around with ideas on how to best do this, and haven't gotten a lot of feedback yet, so I decided to sart marching forward. If you want it to stay in place, I'd recommend sticking with the 4.6 tagged version until the next layer of functionality is complete.

The question of what vote results should be calculated by the API itself, and what results will need 'knowledge' from the modules that inserted them, is a difficult one... Especially if the API is to be flexible enough to store more than one kind of vote.

Because of the possibility that multiple modules could record votes for the same content with diffrent tags and different meanings ('popularity' and 'accuracy' for example), or the potential for the 'vote' value to be a foreign key for something like the Comment Moderation Matrix, it gets tricky.

Thus, a single flexible 'get' function that can filter by tag or by user, and a calculation hook.

I'd like to whip up a basic votingapi_totals.module over the next week that implements basic calculations like 'highest and lowest votes by tag,' 'median vote by tag,' and 'sum of all votes by tag' for a given $votes object. They wouldn't be accurate for some of the esoteric situations, but it could certainly be useful. Once that's in place your module would be able to look at the calculated vote without doing any of the work.

Do you have any thoughts? As a consumer of votingapi data, what do YOU want it to give you as a result?

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

budda’s picture

I've adapted my module to work with the new API and calculate the total votes for each content_id now.

Works great (minus the bugs i reported on the project tracker).

My module allows simple 1-click to vote system, using ajax which autop updates the total count after they clicked - just like on digg.com

I'm going to use the voting api to allow voting on comments too -5 to +5

Not sure if i am re-creating the wheel with some of this though...

--
www.bargainspy.co.uk | More Drupal modules

eaton’s picture

I'd be very interested to see what you're working on...

Also, I'd be interested in seeing how multiple modules can use the same infrastructure. My end goal is to do as much of the 'grunt work' as possible for someone wanting to implement a voting system like yours. The 'special sauce' of how the raw voting data affects the UI and other stuff, that's what I hope more authors can spend their time on...

I'd also like to look into speed optimizations in the future. Using the cache system to retrieve voting data for each content-type could be a boon on heavily used systems, I think, and well suited for handling at the API level.

Also, the three bugs you spotted have been fixed and are now checked into CVS. Thanks LOADS for catching and correcting them.

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

sabbat’s picture

I see your module very interesting. Please can I try it ?

-----------------------
aldelcast [a] gmail.com
si te aburres ya sabes
www.aburridos.info

budda’s picture

Sadly it's not polished enough to go letting others play with it right now.

Maybe after xmas when i've sorted the rest of my new site out it will be released to the Drupal Community.

--
www.bargainspy.co.uk | More Drupal modules

sabbat’s picture

Ok thnk you at all. Good loock with your proyect

si te aburres ya sabes
www.aburridos.info

benshell’s picture

Your module sounds really good. I'd really hoped to expand voting.module to support AJAX voting, Flash voting, and plain HTML voting, but I haven't gotten that far. It'd be great if you can integrate your module with voting.module. I'd rather have one module (or a set of modules working together) with one database structure than several independent voting solutions. Of course, maybe votingapi.module is the solution to this. If all the voting systems use it, then I would think that all the voting systems would be compatiable with each other.

eaton’s picture

The structure provided by votingapi DOES allow almost every voting solution I've seen to store its data in the same way, and recognize the difference between data IT recorded, and data a DIFFERENT voting solution recorded.

The bigger problem -- and the one I'm still a bit stumped on -- is how to allow multiple voting solutions to share data effectively at a functional level. A karma module, for example, could assign users a karma level based on how other users rate their content... but if a different content rating algorithm is swapped in, how will the karma module know?

votingapi can eliminate silos of data, but eliminating silos of *meaning* is tougher.

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

budda’s picture

I'd forgotten about voting.module -- is it 4.7 compatible currently?
If so I might patch in my ajax code to the voting.module and supply a patch.

--
www.bargainspy.co.uk | More Drupal modules

eaton’s picture

I've been puzzling over how content can be effectively rated in a way that multiple modules can share. Ideally, multiple modules would be able to cast and read votes from a shared pool.

I realize I'm messing around with the APIs while they're actually being used (Budda, hello!) but I'm trying to work through the wrinkles of how this can best work. What I'm contemplating is ONE additional field for every vote: 'Vote Type.' The possible values, defined as constants in votingapi.module, would be:

percentage
token
key
custom

'percentage' vote types would automatically be averaged, mean'd, high'd, and low'd. Modules with 1-x rating systems (or even -10 to +10) would have to standardize on percentages behind the scenes... though they could use any values they like in the UI to represent the vote granularity.

'token' vote types would simply be totaled up: they would represent users giving a thumbs-up vote to a piece of content, or contributing a certain number of 'points' towards another user, etc.

'key' vote types would represent options in a multiple choice vote -- for example, the various options in the 4.6.3 era comment moderation matrix. For each value in a 'key' vote type, totals would be tabulated separately and a ranked.

'custom' vote types would NOT be calculated at all, and would have to be handled by the calculation hooks.

How would this work in the real world?

Voting.module and a number of other rating solutions would all toss in 'percentage' vote types, and would be able to share each others data to come up with aggregate ratings for a piece of content. My latestgreatest.module promotes aggregator items if they've received enough thumbs-ups. It would use a VOTING_TYPE of 'token'. A slashdot-style karma mechanism would use a voting type of 'key'. it would receive totals for each possible vote from votingapi.module, and map each value to a word like 'flamebait' or 'insightful.'

With this mechanism in place, the value of the 'tag' field would be useful primarily for multi-criteria voting scenerios.

Does anyone have thoughts on this?

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

budda’s picture

My head hurts thinking about all the possibilities :-(

--
www.bargainspy.co.uk | More Drupal modules

sabbat’s picture

Increible all the posibilitys. I´m trying. Great work

si te aburres ya sabes
www.aburridos.info

anders.fajerson’s picture

This could be so usefull (mostly posting to keep track of this interesting work)

eaton’s picture

A new version of the CVS voting API will be coming out shortly (hopefully this weekend, or early next week). It'll offer better support for modules that require multi-criteria voting, and MUCH better optimization and caching of voting results.

Mind you, this API still requires that OTHER modules be written to sit on top of it and implement actual voting systems. But it does provide an infrastructure, and also allows various kinds of voting systems to share data with each other if necessary.

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

eaton’s picture

Copied and pasted from my recent blog post:

After a pleasant weekend of movie-watching, relaxing, and sporadic coding, there's a new version of VotingAPI checked into the Drupal CVS. It's probably as good a time as any to step back and examine the reason for the project.

  1. A framework, not a widget
    Existing voting solutions implemented good UI widgets, but provided few options for sites that needed a different voting workflow. Creating a new system required starting from square one again. VotingAPI implements the back end -- saving and retrieving votes, calculating results in a variety of ways, and so on -- so that other modules can handle the site-specific stuff.
  2. Make easy things easy
    A contributed voting module only needs to use three functions: votingapi_set_vote(), votingapi_get_user_votes(), and votingapi_get_voting_results(). Modules that implement complex voting systems and workflow can utilize optional parameters and helper functions to tap into more functionality, but simple voting scenerios are simple to code.
  3. Vote on anything
    Voting.module, node_voting.module, and others were good solutions but they applied only to nodes. VotingAPI is content-agnostic, and can be used to rate nodes, comments, users, or fish. With comment moderation stripped out of the Drupal Core for version 4.7, there's a definite need for a clean system that can deal with comments as well.
  4. Multi-criteria voting
    This one's a show-stopper for sites that wanted to use voting to capture data on multiple aspects of a given thing. (Gathering community ratings for a game's graphics, audio, and plot, for example.)
  5. Vote in different ways
    Percentage voting, thumbs-up/thumbs-down voting, and custom voting styles are all supported. VotingAPI is smart enough to recognize the differences between the voting styles and calculate the correct results.
  6. Standardize data storage
    Existing voting systems all used custom data models. Voting data is valuable to lots of different modules; using a standard API to store and retrieve the data means other modules can be consumers of voting data without knowing what system produced the data.
  7. Expensive inserts, cheap selects
    VotingAPI automatically caches aggregate voting results (total number of votes, average vote, etc) for fast retrieval. Whenever a new vote is cast, aggregate data is recalculated and re-cached. That means easier, less expensive joins for views and filters.
  8. Hooks for other custom calculations
    Lesser-used aggregate functions, like median votes, karma-adjusted votes, and so on can be implemented via hook_votingapi(). With it, modules can insert their own calculation logic during cache-time.

With last night's checkins, VotingAPI.module finally supports all of these features. There's still work to do, and I can think of ways to improve both the caching and simplify retrieval of filtered voting results. As it stands, though, it's fully functional and ready for other module developers to tinker with. Both BuddaBoy and merlinofchaos from #drupal have been instrumental in testing and brainstorming; hopefully the results will make life easier for them, too.

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

benshell’s picture

I was just looking at the latest version of votingapi with the intent of converting the CVS version of voting.module to use it. However, I'm confused by the way you're retrieving votes. Now as long as it works I'm not going to be picky about HOW it works (and I'm sure it does), but could you provide a code sample that shows how module developers can get started with the votingapi? To be specific, why does votingapi_get_voting_results() return a array that contains objects? I had to use the following code to get the number of votes and the average vote:

$results = votingapi_get_voting_results($content_type, $content_id);
foreach($results as $result) {
	if ($result->function == 'count') {
		$num_votes = $result->value;
	} elseif ($result->function == 'average') {
		$avg_vote = $result->value;
	}
}

Also, it appears the average vote is being rounded to the nearest integer. I think this is because votingapi thinks that the vote is a percentage. I could use the VOTINGAPI_VALUE_TYPE_TOKEN or VOTINGAPI_VALUE_TYPE_KEY flags, but I'm not sure what the difference is between them. In any case, I'd prefer to use the percentages, so do I need to convert my 1-5 votes to the 1-100 scale before I send them to votingapi and then convert them back again afterwards?

BTW, good job on this module! Good things can be complicated, so I'm sure there are explainations for all my questions. It's clear that you've put a lot of time and thought into this.

eaton’s picture

Hey! Thanks for the response. I hope the basics of the API stay stable at this point, and the data scheme stays stable as well. i think there's still a lot of room for improvement in streamlining the approach to querying, etc.

First: I'll be uploading an example of VERY simple thumbs-up voting using latestgreatest.module later this evening.

Second: The get_resuts() function returns a list of objects because (at least in some instances) the results may contain cached totals for a variety of value types, and a variety of voting tags (like 'camerawork' or 'composition' or the ever-popular default 'vote').

As such, the code you pasted would certainly work in a single-voting-system scenerio. In a scenerio where you might share space with many different types of voting, you might want to add an additional check:

<?php
$results = votingapi_get_voting_results($content_type, $content_id);
foreach($results as $result) {
    if ($result->value_type == VOTINGAPI_VALUE_TYPE_PERCENT && $result->tag == 'vote') {
        if ($result->function == 'count') {
            $num_votes = $result->value;
        } elseif ($result->function == 'average') {
            $avg_vote = $result->value;
        }
    }
}
?>

What I'd like to do is add an optional set of parameters to the get_results() function, allowing module developers to quickly and easily filter by value_type, tag, and (optionally) function. That would allow the code to look more like this:

<?php
$results = votingapi_get_voting_results($content_type, $content_id, $value_type, $tag);
foreach($results as $result) {
    if ($result->function == 'count') {
        $num_votes = $result->value;
    } elseif ($result->function == 'average') {
        $avg_vote = $result->value;
    }
}
?>

Finally, regarding the integer, I have to check into that one. It SHOULD be saving and calculating averages as floats, but that's one bit I didn't have a chance to text extensively. If it's broken, and is truncating to ints, that's my fault. ;)

You are right, though, in that working with the 'percentage' value type assumes that you'll be feeding in values that are already percentages. For example, a 1-5 scale would be represented as 0, 25, 50, 75, 100.

I'll also be trying to put together a few snippet-style examples of how to best interact with the API in different situations. Fortunately, most of THIS stuff is frosting on top of the underlying system, and adding filtered helper functions doesn't break anything. From your perspective, what kinds of helper functions would be most useful? What would simplify your life as a module developer interacting with the API? Would it be MORE useful to have the results of a get_results() call be a nested array? Perhaps something like:

<?php
$results = votingapi_get_voting_results($content_type, $content_id, $value_type, $tag);
$num_votes = $result[$value_type][$tag]['count'];
$avg_vote = $result[$value_type][$tag]['average'];
}
?>

This would mirror the array that modules can iterate through with the calculate hook.

I don't want to make things hash, but it seems like settling these questions earlier rather than later can only be a good thing...

--
Jeff Eaton | I heart Drupal.

--
Eaton — Partner at Autogram

datura-1’s picture

I noticed that the value field in the {votingapi_cache} table is of type float, but the value inserted was always an integer!

I tracked it down to the query rewrite using %d instead of %f. If you change to %f, you'll get floats in the db.

Old way:

  db_query("INSERT INTO {votingapi_cache} (vote_cache_id, content_type, content_id, value, value_type, tag, function) VALUES (%d, '%s', %d, %d, '%s', '%s', '%s')",
    $vobj->vote_cache_id, $vobj->content_type, $vobj->content_id, $vobj->value, $vobj->value_type, $vobj->tag, $vobj->function);

New way:

  db_query("INSERT INTO {votingapi_cache} (vote_cache_id, content_type, content_id, value, value_type, tag, function) VALUES (%d, '%s', %d, %f, '%s', '%s', '%s')",
    $vobj->vote_cache_id, $vobj->content_type, $vobj->content_id, $vobj->value, $vobj->value_type, $vobj->tag, $vobj->function);

Other than that, it's a great module :)

(I created an issue for this here: http://drupal.org/node/64273)

rand0mmm’s picture

Just wanted to suggest implementing some of the more interesting options in counting elections.

This could have a MAJOR impact on delivering electronic democracy to the world.
Even just getting people to understand the options would advance things greatly, w/o all the tech-security issues of a real election.

Check out this page for jumpstart... http://en.wikipedia.org/wiki/Voting_system#Ranked_voting_methods

We use a majority wins in America,
but don't miss Borda Counts - http://en.wikipedia.org/wiki/Borda_count#Voting_and_counting

Much more fair to greater base of interests. Doesn't cause two party systems to be default.

Yea for fun!