views:

83

answers:

2

I have a Django model, shown below, that I use to keep track of which ip addresses visit my site and when.

class Visit(models.Model):
    created         = models.DateTimeField(default=datetime.utcnow)
    ip              = models.IPAddressField(editable=False)

I'd like to write a method on this model that returns the number of days in took for the last 100 visits from a particular IP. Multiple visits in a single day (hour etc) from an IP all count as separate visits. So, if someone visted the site 100 times in the past 2 days, it would return 2, 100 times in the past 8 days, it would return 8, and so on.

A: 

An easy approach is to get the last 100 Visit objects for an ip address and count the number of unique created objets in them.

def num_of_dates(ip_addr)    
    dates = [v.created for v in Visit.objects.filter(ip=ip_addr).order_by('-created')[0:100]]
    unique_dates = set(dates)
    return len(unique_dates)
rubayeet
Thanks this looks nearly right. However, it always returns 100 rather than the number of days. I suspect this is because created is a DateTime field so every date is unique.
swisstony
You can form Date objects from the DateTime object to get unique dates instead of unique timestamps. Or you can follow @fahhem's solution which seems neater.
rubayeet
+1  A: 

You probably want to change the default= for created_on to be auto_now_add since the datetime.utcnow doesn't update if you're using servers other than the dev server:

class Visit(models.Model):
    created         = models.DateTimeField(auto_now_add=True,editable=False)
    ip              = models.IPAddressField(editable=False)

from datetime import datetime
def days_for_100(ip_addr):
  now = datetime.now()
  dates = Visit.objects.filter(ip=ip_addr)
  if dates.count()<100:
    latest = dates.latest('-created')
  else:
    latest = dates.dates('created','day',order='DESC')[99]
  return (now-latest).days # timedelta.days

This returns how many days earlier the 100th visit ago happened (or how long ago the first visit occurred if there are less than 100 visits)

fahhem
I tried this (after changing created_on to created) but got a list index out of range error. With regards to the auto_add_now I use a custom save() method so hopefully this is okay.
swisstony
a custom save() should work, but using Django's code for auto_now_add reduces the amount of code you have to maintain/test. If you're getting an out-of-range error, it's because that IP hasn't visited 100 times, I'll edit it to make sure the error doesn't happen.
fahhem
@fahhem, thanks for trying to fix it. The IP had over 600 visits, so don't know why I got the error. I've tried the new code, but it still gives an error. This time: Invalid order_by arguments: ['--created'].
swisstony
That can be either caused by putting two dashes or, if that was a typo on SO and not in the code, it could be an undocumented problem with DateQuerySets that means one can't get the latest() of one. Either way, I've modified the code again to make sure .latest() is only called on normal QuerySets.
fahhem
Thanks fahhem. Unfortunately it still didn't work. I will investigate further when I get chance, which probably won't be till next week now.
swisstony