Index: /trac/fivestarvoteplugin/0.11/fivestarvote/__init__.py
===================================================================
--- /trac/fivestarvoteplugin/0.11/fivestarvote/__init__.py	(revision 1689)
+++ /trac/fivestarvoteplugin/0.11/fivestarvote/__init__.py	(revision 1698)
@@ -16,75 +16,121 @@
 from genshi.builder import tag
 from pkg_resources import resource_filename
 
 
 class FiveStarVoteSystem(Component):
     """Allow up and down-voting on Trac resources."""
 
     implements(ITemplateProvider, IRequestFilter, IRequestHandler,
                IEnvironmentSetupParticipant, IPermissionRequestor)
 
     voteable_paths = ListOption('fivestarvote', 'paths', '^/$,^/wiki*,^/ticket*',
         doc='List of URL paths to allow voting on. Globs are supported.')
 
     schema = [
-        Table('fivestarvote', key=('resource', 'username', 'vote'))[
+        Table('fivestarvote', key=('resource', 'username', 'vote', 'hit'))[
             Column('resource'),
             Column('username'),
             Column('vote', 'int'),
+            Column('hit', 'int'),
             ]
         ]
 
     path_match = re.compile(r'/fivestarvote/([1-5])/(.*)')
 
 
     # Public methods
-    def get_vote_counts(self, resource):
+    def get_vote_counts(self, req, resource):
         """Get total, count and tally vote counts and return them in a tuple."""
         resource = self.normalise_resource(resource)
         db = self.env.get_db_cnx()
         cursor = db.cursor()
-        sql = "select sum(vote), count(*) from fivestarvote where (resource = '%s')" % resource
-        self.env.log.debug("sql:: %s" % sql)
-        cursor.execute(sql)
-        row = cursor.fetchone()
-        sum = row[0] or 0
+
+        # attemp to fetch the hit count for this user
+        sql = "SELECT hit FROM fivestarvote WHERE username='%s' AND resource = '%s'" % \
+              (get_reporter_id(req), resource)
+        self.env.log.debug("get_vote_counts: sql:: %s" % sql)
+        cursor.execute(sql);
+        row = cursor.fetchone()
+        if not row:  # record not existed yet, create a brand new one
+            hit  = 1
+            vote = 0
+            self.env.log.debug("get_vote_counts: ===>record does not exist yet, insert a new one")
+            sql = "INSERT INTO fivestarvote (resource, username, vote, hit) VALUES ('%s', '%s', '%s', '%s')" % \
+                  (resource, get_reporter_id(req), vote, hit)
+            self.env.log.debug("set_vote: sql:: %s" % sql)
+            cursor.execute(sql)
+            db.commit() 
+        else:
+            hit = (row[0] or 0) + 1
+            sql = "UPDATE fivestarvote SET hit='%s' WHERE resource=='%s' AND username=='%s'" % \
+                  (hit, resource, get_reporter_id(req))
+            self.env.log.debug("set_vote: sql:: %s" % sql)
+            cursor.execute(sql);
+            db.commit()
+
+        # obtain total hit count and visit count from all users regardless voted or not
+        sql = "select sum(hit), count(*) from fivestarvote where (resource = '%s')" % resource
+        self.env.log.debug("get_vote_counts: sql:: %s" % sql)
+        cursor.execute(sql)                
+        row = cursor.fetchone()
+        hits   = row[0] or 0
+        visits = row[1] or 0
+        self.env.log.debug("get_vote_counts: ===>hits=%s, visits=%s" % (hits, visits))
+
+        # obtain total vote count and voter count from only users who have voted
+        sql = "select sum(vote), count(*) from fivestarvote where (resource = '%s') AND vote != 0 " % resource
+        self.env.log.debug("get_vote_counts: sql:: %s" % sql)
+        cursor.execute(sql)                
+        row = cursor.fetchone()
+        sum   = row[0] or 0
         total = row[1] or 0
         tally = 0
         if (total > 0):
             tally = (sum / total)
-        return (sum, total, tally)
+        return (sum, total, tally, hits, visits)
 
     def get_vote(self, req, resource):
         """Return the current users vote for a resource."""
         resource = self.normalise_resource(resource)
         db = self.env.get_db_cnx()
         cursor = db.cursor()
-        cursor.execute('SELECT vote FROM fivestarvote WHERE username=%s '
-                       'AND resource = %s', (get_reporter_id(req), resource))
+        sql = "SELECT vote FROM fivestarvote WHERE username='%s' AND resource = '%s'" % \
+              (get_reporter_id(req), resource)
+        self.env.log.debug("get_vote: sql:: %s" % sql)
+        cursor.execute(sql)
         row = cursor.fetchone()
         vote = row and row[0] or 0
         return vote
 
     def set_vote(self, req, resource, vote):
         """Vote for a resource."""
-        resource = self.normalise_resource(resource)
-        db = self.env.get_db_cnx()
-        cursor = db.cursor()
-        cursor.execute('DELETE FROM fivestarvote WHERE username=%s '
-                       'AND resource = %s', (get_reporter_id(req), resource))
-        if vote:
-            cursor.execute('INSERT INTO fivestarvote (resource, username, vote) '
-                           'VALUES (%s, %s, %s)',
-                           (resource, get_reporter_id(req), vote))
-        db.commit()
-
+        if vote:           
+            resource = self.normalise_resource(resource)
+            db = self.env.get_db_cnx()
+            cursor = db.cursor()
+        
+            sql = "UPDATE fivestarvote SET vote='%s' WHERE resource=='%s' AND username=='%s'" % \
+                  (vote, resource, get_reporter_id(req))
+            self.env.log.debug("set_vote: sql:: %s" % sql)
+            cursor.execute(sql);
+
+            db.commit()
+            if not cursor.rowcount:
+                self.env.log.debug("===>record does not exist yet, insert a new one")
+                sql = "INSERT INTO fivestarvote (resource, username, vote, hit) VALUES ('%s', '%s', '%s', '%s')" % \
+                      (resource, get_reporter_id(req), vote, 0)
+                self.env.log.debug("set_vote: sql:: %s" % sql)
+                cursor.execute(sql)
+                db.commit() 
+        return
+        
     # IPermissionRequestor methods
     def get_permission_actions(self):
         return ['VOTE_VIEW', 'VOTE_MODIFY']
 
     # ITemplateProvider methods
     def get_templates_dirs(self):
         return []
 
     def get_htdocs_dirs(self):
         return [('fivestarvote', resource_filename(__name__, 'htdocs'))]
@@ -96,21 +142,21 @@
     def process_request(self, req):
         req.perm.require('VOTE_MODIFY')
         match = self.path_match.match(req.path_info)
         vote, resource = match.groups()
         resource = self.normalise_resource(resource)
 
         self.set_vote(req, resource, vote)
         self.env.log.debug("DAV::")
 
         if req.args.get('js'):
-            percent, str, title = self.format_votes(resource)
+            percent, str, title = self.format_votes(req, resource)
             req.send(','.join(("%s" % percent, str, title)))
         
         req.redirect(req.get_header('Referer'))
 
     # IRequestFilter methods
     def pre_process_request(self, req, handler):
         if 'VOTE_VIEW' not in req.perm:
             return handler
 
         for path in self.voteable_paths:
@@ -123,41 +169,43 @@
     def post_process_request(self, req, template, data, content_type):
         return (template, data, content_type)
 
     # IEnvironmentSetupParticipant methods
     def environment_created(self):
         self.upgrade_environment(self.env.get_db_cnx())
 
     def environment_needs_upgrade(self, db):
         cursor = db.cursor()
         try:
-            cursor.execute("select count(*) from fivestarvote")
+            cursor.execute("select count(hit) from fivestarvote")
             cursor.fetchone()
             return False
         except:
             cursor.connection.rollback() 
             return True
 
     def upgrade_environment(self, db):
         db_backend, _ = DatabaseManager(self.env)._get_connector()
         cursor = db.cursor()
+        cursor.execute("DROP TABLE fivestarvote")   # WARNINGS: WILL DISCARD ALL EXISTING VOTE INFO
+        
         for table in self.schema:
             for stmt in db_backend.to_sql(table):
                 self.env.log.debug(stmt)
                 cursor.execute(stmt)
         db.commit()
 
     # Internal methods
     def render_voter(self, req):
         resource = self.normalise_resource(req.path_info)
 
-        count = self.get_vote_counts(resource)
+        count = self.get_vote_counts(req, resource)
 
         add_stylesheet(req, 'fivestarvote/css/fivestarvote.css')
 
         names = ['', 'one', 'two', 'three', 'four', 'five']
         els = []
         percent = 0
         if count[2] > 0:
             percent = count[2] * 20
 
         str = "Currently %s/5 stars." % count[2]
@@ -172,34 +220,34 @@
                 href = req.href.fivestarvote(i, resource)
                 add_script(req, 'fivestarvote/js/fivestarvote.js', mimetype='text/javascript')
             a = tag.a(i, href=href, class_=className)
             li = tag.li(a)
             els.append(li)
         
         ul = tag.ul(els, class_='star-rating')
         className = ''
         if 'VOTE_MODIFY' in req.perm and get_reporter_id(req) != 'anonymous':
             className = 'active'
-        title = "Current Vote: %s users voted for a total of %s" % (count[1], count[0]) 
+        title = "Current Vote: %s users voted for a total of %s (user=%s hit:%s)" % (count[1], count[0], count[4], count[3]) 
         add_ctxtnav(req, tag.span(tag.object(ul), id='fivestarvotes', title=title, class_=className))
 
 
     def normalise_resource(self, resource):
         if isinstance(resource, basestring):
             resource = resource.strip('/')
             # Special-case start page with "/" or "/wiki"
-            if resource == '' or resource == 'wiki':
+            if not resource or resource == 'wiki':
                 resource = 'wiki/WikiStart'
             
             return resource
         return get_resource_url(self.env, resource, Href('')).strip('/')
 
-    def format_votes(self, resource):
-        count = self.get_vote_counts(resource)
+    def format_votes(self, req, resource):
+        count = self.get_vote_counts(req, resource)
 
         percent = 0
         if count[2] > 0:
             percent = count[2] * 20
 
         str = "Currently %s/5 stars." % count[2]
-        title = "Current Vote: %s users voted for a total of %s" % (count[1], count[0]) 
+        title = "Current Vote: %s users voted for a total of %s (user=%s hit=%s)" % (count[1], count[0], count[4], count[3]) 
         return (percent, str, title)
