Subtraction of two dates in django query - django

class Case( models.Model ):
created = models.DateTimeField()
modified = models.DateTimeField()
STATUS = (
('Active', 'Active'),
('Hold', 'Hold'),
('Expired', 'Expired'),
('Cancelled', 'Cancelled'),
)
status = models.CharField(max_length=32, choices=STATUS)
Now i want to extract records, having status expired less than 2 months ago, simply expired more than 2 months ago shouldn't be counted.
i have read __here subtraction of dates but it doesn't work in my case.
expired_cases = Case.objects.filter( status = 'Expired', modified__lt = datetime.now() - timedelta(days=60) ).count()
this kind of query may work but i didn't want to hard code the days in this.
Please help me in this issue.
thanx in advance :)

You can use python's calendar lib to return a list of days. Note that it doesn't handle leap years so you need to implement a solution for that.
>>> days = calendar.mdays
[0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
Get the days of the current month this way, then calculate the timedelta you need. You'll need to strip the first element from the list to make it work.
>>> days = days[1:]
>>> month = datetime.date.today().month-1 #-1 due to 0-based indexing in list
>>> delta = days[month-1] + days[month-2]

Use the python-dateutil module. Create a Python datetimeobject out of the modified time ans subtract. It should work.

Related

Get record's age in seconds if older than 5 minutes (otherwise 0) in Django (with PostgreSQL database)

I'm retrieving all records, and I would like to display the record's age for those records that are older than 5 minutes.
The output should be something like this (in this example, two records: 1.8.9.1 and 2.7.3.1 are older than 5 minutes) :
ip ... status
---------------------
1.8.9.1 ... 3 hours
2.7.3.1 ... 7 minutes
1.1.1.1 ... up
1.1.1.2 ... up
1.1.1.3 ... up
1.1.1.4 ... up
1.1.1.5 ... up
Here's my current code:
Interfaces.objects.all()
.annotate(
age = (datetime.utcnow() - F('timestamp')), # 0:00:08.535704
age2 = Epoch(datetime.utcnow() - F('timestamp')), # 8.535704
# age3 = int(Epoch(datetime.utcnow() - F('timestamp'))/300),
current_time=Value(str(datetime.utcnow()),
output_field=null_char_field),
)
.order_by('age','ip')
age and age2 both work, but the problem is that I want the records that are older than 5 minutes sorted by age, and the rest by ip
So I'm trying to set age to 0, if it's less than 5 minutes.
If I would do it directly in postgresql, I'd use this query:
select ip, <other fields>,
case when extract('epoch' from now() - "timestamp") > 300
then extract('epoch' from now() - "timestamp")
else 0
end
Is there a way to do it in django?
I figured it out:
Interfaces.objects.all()
.annotate(
age=Case(
When(timestamp__lt=datetime.utcnow() - timedelta(minutes=5),
then=Cast(Epoch(datetime.utcnow() - F('timestamp')),
NullIntegerField)),
default=0,
output_field=NullIntegerField
),
)
.order_by('age','ip')
By the way, my imports and relevant settings:
from django.db.models import F, Func, Case, When, IntegerField
from django.db.models.functions import Coalesce, Cast
NullIntegerField = IntegerField(null=True)
class Epoch(Func):
function = 'EXTRACT'
template = "%(function)s('epoch' from %(expressions)s)"
This website ended up being the most helpful: https://micropyramid.com/blog/django-conditional-expression-in-queries/
You can do it in other way also which will be faster.
Get current time, subtract from that 5 minutes, after that search all the Interfaces
where age is less or equal than the subtracted date.
example:
current_time = datetime.now()
older_than_five = current_time - datetime.timedelta(minutes=5)
Interfaces.objects.all()
.annotate(
age=Case(
When(age__lt=older_than_five, then=Value(0)),
default=F('age')
)
)
.order_by('age','ip')

python Find the most reported month

I am trying to find out October(mentioned 2 times), I had the idea to use dictionary to solve this problem. However I struggled a lot to figure out how to find/separate the months, I was not able to use my solution for the 1st str values where there are some spaces. Can someone please suggest how can I modify that split section to cover - , and white space?
import re
#str="May-29-1990, Oct-18-1980 ,Sept-1-1980, Oct-2-1990"
str="May-29-1990,Oct-18-1980,Sept-1-1980,Oct-2-1990"
val=re.split(',',str)
monthList=[]
myDictionary={}
#put the months in a list
def sep_month():
for item in val:
if not item.isdigit():
month,day,year=item.split("-")
monthList.append(month)
#process the month list from above
def count_month():
for item in monthList:
if item not in myDictionary.keys():
myDictionary[item]=1
else:
myDictionary[item]=myDictionary.get(item)+1
for k,v in myDictionary.items():
if v==2:
print(k)
sep_month()
count_month()
from datetime import datetime
import calendar
from collections import Counter
datesString = "May-29-1990,Oct-18-1980,Sep-1-1980,Oct-2-1990"
datesListString = datesString.split(",")
datesList = []
for dateStr in datesListString:
datesList.append(datetime.strptime(dateStr, '%b-%d-%Y'))
monthsOccurrencies = Counter((calendar.month_name[date.month] for date in datesList))
print(monthsOccurrencies)
# Counter({'October': 2, 'May': 1, 'September': 1})
Something to be aware in my solution with %b for the month is that Sept has changed to Sep to work (Month as locale’s abbreviated name). In this case you can either use fullname months (%B) or abbreviated name (%b). If you can not have the big string as with correct month name formatting, just replace the wrong ones ("Sept" for example with "Sep" and always work with date obj).
Not sure that regex is the best tool for this job, I would just use strip() along with split() to handle your whitespace issues and get a list of just the month abbreviations. Then you could create a dict with counts by month using the list method count(). For example:
dates = 'May-29-1990, Oct-18-1980 ,Sept-1-1980, Oct-2-1990'
months = [d.split('-')[0].strip() for d in dates.split(',')]
month_counts = {m: months.count(m) for m in set(months)}
print(month_counts)
# {'May': 1, 'Oct': 2, 'Sept': 1}
Or even better with collections.Counter:
from collections import Counter
dates = 'May-29-1990, Oct-18-1980 ,Sept-1-1980, Oct-2-1990'
months = [d.split('-')[0].strip() for d in dates.split(',')]
month_counts = Counter(months)
print(month_counts)
# Counter({'Oct': 2, 'May': 1, 'Sept': 1})

Calculating julian date in python

I'm trying to create a julian date in python and having major struggles. Is there nothing out there as simple as:
jul = juliandate(year,month,day,hour,minute,second)
where jul would be something like 2457152.0 (the decimal changing with the time)?
I've tried jdcal, but can't figure out how to add the time component (jdcal.gcal2jd() only accepts year, month and day).
Not a pure Python solution but you can use the SQLite in memory db which has a julianday() function:
import sqlite3
con = sqlite3.connect(":memory:")
list(con.execute("select julianday('2017-01-01')"))[0][0]
which returns: 2457754.5
Here you go - a pure python solution with datetime and math library.
This is based on the the Navy's Astronomical Equation found here and verified with their own calculator: http://aa.usno.navy.mil/faq/docs/JD_Formula.php
import datetime
import math
def get_julian_datetime(date):
"""
Convert a datetime object into julian float.
Args:
date: datetime-object of date in question
Returns: float - Julian calculated datetime.
Raises:
TypeError : Incorrect parameter type
ValueError: Date out of range of equation
"""
# Ensure correct format
if not isinstance(date, datetime.datetime):
raise TypeError('Invalid type for parameter "date" - expecting datetime')
elif date.year < 1801 or date.year > 2099:
raise ValueError('Datetime must be between year 1801 and 2099')
# Perform the calculation
julian_datetime = 367 * date.year - int((7 * (date.year + int((date.month + 9) / 12.0))) / 4.0) + int(
(275 * date.month) / 9.0) + date.day + 1721013.5 + (
date.hour + date.minute / 60.0 + date.second / math.pow(60,
2)) / 24.0 - 0.5 * math.copysign(
1, 100 * date.year + date.month - 190002.5) + 0.5
return julian_datetime
Usage Example:
# Set the same example as the Naval site.
example_datetime = datetime.datetime(1877, 8, 11, 7, 30, 0)
print get_julian_datetime(example_datetime)
Answer one from Extract day of year and Julian day from a string date in python. No libraries required.
Answer two is a library from https://pypi.python.org/pypi/jdcal
The easiest: df['Julian_Dates']= df.index.to_julian_date(). you need to set your date time column to index (Use Pandas).
There is a way with using Astropy. First, change your time to a list (t). Second, change that list to astropy time (Time). Finally, compute your JD or MJD (t.jd t.mjd).
https://docs.astropy.org/en/stable/time/
For df:
t = Time(DF.JulianDates,format='jd',scale='utc')
A simple fudge the numbers script via wiki don't know either. Please note that this was written using Python 3.6 so I'm not sure it would work on Python 2.7 but this is also an old question.
def julian_day(now):
"""
1. Get current values for year, month, and day
2. Same for time and make it a day fraction
3. Calculate the julian day number via https://en.wikipedia.org/wiki/Julian_day
4. Add the day fraction to the julian day number
"""
year = now.year
month = now.month
day = now.day
day_fraction = now.hour + now.minute / 60.0 + now.second / 3600.0 / 24.0
# The value 'march_on' will be 1 for January and February, and 0 for other months.
march_on = math.floor((14 - month) / 12)
year = year + 4800 - march_on
# And 'month' will be 0 for March and 11 for February. 0 - 11 months
month = month + 12 * march_on - 3
y_quarter = math.floor(year / 4)
jdn = day + math.floor((month * 153 + 2) / 5) + 365 * year + y_quarter
julian = year < 1582 or year == (1582 and month < 10) or (month == 10 and day < 15)
if julian:
reform = 32083 # might need adjusting so needs a test
else:
reform = math.floor(year / 100) + math.floor(year / 400) + 32030.1875 # fudged this
return jdn - reform + day_fraction
Generally this was just to try for myself as the most common algorithm was giving me trouble. That works and if you search around for it and write your script using it as it comes in many languages. But this one has steps in the docs to try to keep it simple. The biggest decision is how often are you going to look for dates that are before Gregorian reform. That is why I never tested that yet but go ahead and play with it as it needs a lot of massaging. :-D At least I think is conforms to PEP8 even if it isn't up to best practices. Go ahead and pylint it.
You could just use source packages like PyEphem or whatever but you still would like to know what's going on with it so you could write your own tests. I'll link that PyEphem for you but there are lots of ready made packages that have Julian Day calculations.
Your best bet if you are doing lots of work with these types of numbers is to get a list of the constant ones such as J2000.
datetime.datetime(2000, 1, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
datetime.datetime.toordinal() + 1721425 - 0.5 # not tested
# or even
datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
It's not so hard to figure these out if you get familiar with what datetime library does. Just for fun did you notice the PyEphem logo? I suspect it comes from something like this
One post that I saw seems to work but has no tests is jiffyclub
Now here is the more common way to calculate two values using a datetime object.
def jdn(dto):
"""
Given datetime object returns Julian Day Number
"""
year = dto.year
month = dto.month
day = dto.day
not_march = month < 3
if not_march:
year -= 1
month += 12
fr_y = math.floor(year / 100)
reform = 2 - fr_y + math.floor(fr_y / 4)
jjs = day + (
math.floor(365.25 * (year + 4716)) + math.floor(30.6001 * (month + 1)) + reform - 1524)
if jjs < ITALY:
jjs -= reform
return jjs
# end jdn
def ajd(dto):
"""
Given datetime object returns Astronomical Julian Day.
Day is from midnight 00:00:00+00:00 with day fractional
value added.
"""
jdd = jdn(dto)
day_fraction = dto.hour / 24.0 + dto.minute / 1440.0 + dto.second / 86400.0
return jdd + day_fraction - 0.5
# end ajd
It may not be the best practice in Python but you did ask how to calculate it not just get it or extract it although if that is what you want those questions have been answered as of late.
Try https://www.egenix.com/products/python/mxBase/mxDateTime/
First construct a DateTime object via the syntax
DateTime(year,month=1,day=1,hour=0,minute=0,second=0.0)
Then you can use '.jdn' object method to get the value you are looking for.

Get objects created in last 30 days, for each past day

I am looking for fast method to count model's objects created within past 30 days, for each day separately. For example:
27.07.2013 (today) - 3 objects created
26.07.2013 - 0 objects created
25.07.2013 - 2 objects created
...
27.06.2013 - 1 objects created
I am going to use this data in google charts API. Have you any idea how to get this data efficiently?
items = Foo.objects.filter(createdate__lte=datetime.datetime.today(), createdate__gt=datetime.datetime.today()-datetime.timedelta(days=30)).\
values('createdate').annotate(count=Count('id'))
This will (1) filter results to contain the last 30 days, (2) select just the createdate field and (3) count the id's, grouping by all selected fields (i.e. createdate). This will return a list of dictionaries of the format:
[
{'createdate': <datetime.date object>, 'count': <int>},
{'createdate': <datetime.date object>, 'count': <int>},
...
]
EDIT:
I don't believe there's a way to get all dates, even those with count == 0, with just SQL. You'll have to insert each missing date through python code, e.g.:
import datetime
# needed to use .append() later on
items = list(items)
dates = [x.get('createdate') for x in items]
for d in (datetime.datetime.today() - datetime.timedelta(days=x) for x in range(0,30)):
if d not in dates:
items.append({'createdate': d, 'count': 0})
I think this can be somewhat more optimized solution with #knbk 's solution with python. This has fewer iterations and iterations inside SET is highly optimized in python (both in processing and in CPU-cycles).
from_date = datetime.date.today() - datetime.timedelta(days=7)
orders = Order.objects.filter(created_at=from_date, dealer__executive__branch__user=user)
orders = orders.annotate(count=Count('id')).values('created_at').order_by('created_at')
if len(orders) < 7:
orders_list = list(orders)
dates = set([(datetime.date.today() - datetime.timedelta(days=i)) for i in range(6)])
order_set = set([ord['created_at'] for ord in orders])
for dt in (order_set - dates):
orders_list.append({'created_at': dt, 'count': 0})
orders_list = sorted(orders_list, key=lambda item: item['created_at'])
else:
orders_list = orders

Subtract two values

I'd like to subtract two values, one in the current record, then the next in the next record...they are time clock entries and I want to calculate the amount of time an employee spend on his/her break, so I'll have to subtract the time the employee clocked out, and the time the employee clocked back in. This will be done for several records, then at the end of it all I also want to have total of all the breaks taken.
So how can I do this? I'm doing this in Django BTW.
UPDATE
The records look a bit like this:
employee_id, rec_date, start_time, end_time
18, 2010-08-23, 09:58:00, 14:13:00
18, 2010-08-23, 14:39:00, 18:47:00
19, 2010-08-23, 14:15:00, 18:31:00
21, 2010-08-23, 12:05:00, 14:52:00
21, 2010-08-23, 15:23:00, 18:49:00
21, 2010-08-31, 08:00:00, 12:00:00
21, 2010-08-31, 12:45:00, 19:00:00
You'll have to iterate over the clock actions and do the calculation manually. Something of this sort:
breaks = 0
break_start = None
clock_actions = user.clock_actions.filter(date=desired_day).order_by('date')
for action in clock_actions:
if action.type == 'CLOCK IN':
break_start = action.time
elif break_start is not None:
breaks = breaks + action.time - break_start
break_start = None
You may have to adjust the subtraction to work with timedelta objects, depending on your choice of field for time.