Calculate total days from two dates in python openerp - python

I have three fields start_date, end_date and total_days.
If i enter start_date and end_date field it should automatically display total_days.
This is my Function which is not working.
<field name="start_date" on_change="get_number_of_days(start_date,end_date)"/>
<field name="end_date" on_change="get_number_of_days(end_date,start_date)"/>
<field name="total_days" />
def get_number_of_days(self, cr, uid, ids, start_date, end_date):
"""Returns a float equals to the timedelta between two dates given as string."""
result = self.browse(cr, uid, ids)
if (end_date and start_date) and (start_date <= end_date):
DATETIME_FORMAT = "%d/%m/%Y %H:%M:%S"
from_dt = datetime.datetime.strptime(start_date, DATETIME_FORMAT)
to_dt = datetime.datetime.strptime(end_date, DATETIME_FORMAT)
timedelta = to_dt - from_dt
diff_day = timedelta.days + float(timedelta.seconds) / 86400
result['value']['total_days'] = round(math.floor(diff_day))+1
return result
Is there any other method? or what may be the issue

The section:
diff_day = timedelta.days + float(timedelta.seconds) / 86400
result['value']['total_days'] = round(math.floor(diff_day))+1
is largely unnecessary as datetime.timedelta already has the number of whole days as a value timedelta.days - I suspect that your problem is that you are not converting to a string try:
result['value']['total_days'] = str(timedelta.days)
You should also consider catching problems when the strptime function cannot convert the current value, e.g. when it is blank.

If your dates are given as strings (as your docstring says), the second part of your conditional may behave unexpectedly (depending on the date/time formatting), because you perform a lexicographical comparison:
if (end_date and start_date) and (start_date <= end_date):
# ...
You should convert them to datetime-objects first and then check your conditional.

Thank You,
I wrote new function for this which helped to give total days:
def _get_days(self, cr, uid, ids, field_name, arg ,context=None):
res = {}
fmt = '%Y-%m-%d'
for object in self.browse(cr, uid, ids, context=context):
res[object.id] = {'total_days':0, }
from_date = object.start_date
to_date = object.end_date
d1 = datetime.strptime(from_date, fmt)
d2 = datetime.strptime(to_date, fmt)
daysDiff = str((d2-d1).days+1)
res[object.id]['total_days'] = daysDiff
return res
'start_date': fields.date('Start Date', required=True),
'end_date': fields.date('End Date', required=True),
'total_days': fields.function(_get_days, string="Diff days", multi='sums', store=True),

Related

How can I make a list of last two digits of year plus month

Can I get some help to create a list of days and months in the folowing format:
collection = ['2108', '2109', '2110', '2111', '2112', '2201']
I am trying in this way:
def make_collections(start_date: Date, end_date: Date):
result = []
date = start_date
while True:
if start_date >= end_date:
return result
date = date.strftime('%y%m%d%H%M%S')
result.append(date[:4])
date = (date.strptime(date, '%y%m%d%H%M%S')) + timedelta(days=1)
# test = MakeDataFrame()
# test.run()
if __name__ == '__main__':
start = datetime.now() - timedelta(days=365)
print(make_collections(start, datetime.now()))
But it doesn't work.
I want to give a start date and end date as an argument in function and make a list as I mentioned above with year and month.
Can I get some help to make a a simple function with start and end date as an arguments?
Thanks
I revised your code a little bit, so that the code will print what you expected. I used dateutil.relativedelta module because timedelta provide only day-based delta. dateutil.relativedelta support to calculate month and year differences.
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
def make_collections(start_date, end_date):
result = []
while start_date <= end_date:
result.append(str(start_date.year)[2:] + str(start_date.month).zfill(2))
start_date += relativedelta(months=1)
return result
if __name__ == '__main__':
start = datetime.now() - timedelta(days=365)
print(make_collections(start, datetime.now()))
#['2101', '2102', '2103', '2104', '2105', '2106', '2107', '2108', '2109', '2110', '2111', '2112', '2201']

Create objects based on date and time

I have an Event model, and each event will have different shows.
class Event(models.Model):
title = models.CharField(max_length=200)
class Show(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
date_time = models.DateTimeField(unique=True)
I have another Ticket model. Each ticket should be unique. Meaning each ticket will be unique and relate to a Show and a Seat.
class Ticket(models.Model):
show = models.ForeignKey(Show)
seat = models.ForeignKey(Seat)
class Meta:
unique_together = ('show', 'seat')
I need to create shows based on the start date and end date provide by the user. Suppose this is a JSON post:
{
"event_id": 1,
"start_date": "2018-02-16",
"end_date": "2018-02-20",
"time_list": ["11:00 AM", "8:00 PM"]
}
From the above JSON example, I need to create Show starting like this:
# Start with the start_date as the date, and for each time from the time_list
Show.objects.create(
event = 1,
date_time = datetime.strptime('2018-02-16 11:00 AM', "%Y-%m-%d %I:%M %p")
)
Show.objects.create(
event = 1,
date_time = datetime.strptime('2018-02-16 8:00 PM', "%Y-%m-%d %I:%M %p")
)
# Next date after the start_date, i.e., 16+1 = 17
Show.objects.create(
event = 1,
date_time = datetime.strptime('2018-02-17 8:00 PM', "%Y-%m-%d %I:%M %p")
)
.
.
.
# Create Show objects till the end_date and for each time from the time_list
Show.objects.create(
event = 1,
date_time = datetime.strptime('2018-02-20 8:00 PM', "%Y-%m-%d %I:%M %p")
)
Right now this is how I am creating Show objects:
def create_show_by_datetime(self, request):
event_id = request.data['event_id']
try:
event = Event.objects.get(id=event_id)
except Event.DoesNotExist:
return Response(
{'error': 'event with id: %s does not exist.' % event_id},
status=status.HTTP_400_BAD_REQUEST
)
start_date = request.data['start_date']
end_date = request.data['end_date']
time_list = request.data['time_list']
date_format = '%Y-%m-%d'
time_format = "%I:%M %p"
try:
datetime.strptime(start_date, date_format)
datetime.strptime(end_date, date_format)
for i in range(len(time_list)):
time = datetime.strptime(time_list[i], time_format)
except ValueError as e:
return Response(
{'error': 'Time was not in a supported format. %s' % e},
status=status.HTTP_400_BAD_REQUEST
)
delta_days = datetime.strptime(end_date, date_format).date() - datetime.strptime(start_date, date_format).date()
delta_days = delta_days.days + 1
dt = None
try:
with transaction.atomic():
for i in range(delta_days):
day = datetime.strptime(start_date, date_format) + timedelta(days=i)
for i in range(len(time_list)):
hrs = datetime.strptime(time_list[i], time_format).hour
mins = datetime.strptime(time_list[i], time_format).minute
dt = day + timedelta(hours=hrs, minutes=mins)
show = Show.objects.create(
event=event,
date_time=dt
)
return Response({"data": 'Post succesfull'}, status=status.HTTP_201_CREATED)
except IntegrityError as e:
return Response(
{
'error': "event with date and time already exsits. %s-%s-%s at %s:%s" % (
dt.day, dt.month, dt.year, dt.hour, dt.minute),
'detail': str(e)
}, status=status.HTTP_400_BAD_REQUEST
But I am hoping there's much more elegant way than how I am doing. I am using python 3, django 2 and django rest frameowork. How can I create Shows with the event, and date_time based on the event_id, start_date, end_date and the time_list?
My approach is a little bit different. You said in the question tag that you are using django-rest-framework.. So where are the serializers? :)
Lets create two serializers, one for user data validation (Because we don't trust the USER!) and one for multi data insert.
I haven't checked the code! But you can use it was an example...
class ShowEventSerializer(serializers.Serializer):
event_id = serializers.IntegerField()
start_date = serializers.DateField(required=True)
end_date = serializers.DateField(required=True)
time_list = serializers.ListField(
child=serializers.TimeField()
)
class Meta:
fields = ('event_id', 'start_date', 'end_date', 'time_list')
class ShowSerializer(serializers.Serializer):
date_time = serializers.DateTimeField()
class Meta:
model = Show
fields = ('event', 'date_time')
Now, with the serializers, we are going to validate the user data and then to create a json data object:
def create_show_by_datetime(self, request):
show_event_serializer = ShowEventSerializer(data=request.data)
if not show_event_serializer.is_valid():
return Response({'error': show_event_serializer.errors},status=status.HTTP_400_BAD_REQUEST)
event_id = show_event_serializer.data['event_id']
try:
event = Event.objects.get(id=event_id)
except Event.DoesNotExist:
return Response({'error': 'event with id: %s does not exist.' % event_id},status=status.HTTP_400_BAD_REQUEST)
start_date = show_event_serializer.data['start_date']
end_date = show_event_serializer.data['end_date']
time_list = show_event_serializer.data['time_list']
date_format = '%Y-%m-%d'
time_format = "%I:%M %p"
try:
datetime.strptime(start_date, date_format)
datetime.strptime(end_date, date_format)
for i in range(len(time_list)):
time = datetime.strptime(time_list[i], time_format)
except ValueError as e:
return Response(
{'error': 'Time was not in a supported format. %s' % e},
status=status.HTTP_400_BAD_REQUEST
)
delta_days = datetime.strptime(end_date, date_format).date() - datetime.strptime(start_date, date_format).date()
delta_days = delta_days.days + 1
dt = None
show_data = []
for i in range(delta_days):
day = datetime.strptime(start_date, date_format) + timedelta(days=i)
for i in range(len(time_list)):
hrs = datetime.strptime(time_list[i], time_format).hour
mins = datetime.strptime(time_list[i], time_format).minute
dt = day + timedelta(hours=hrs, minutes=mins)
show_data.append({
"event": event,
"date_time": dt
})
try:
with transaction.atomic():
show_serializer = ShowSerializer(data=show_data, many=True)
if show_serializer.is_valid():
show_serializer.save()
return Response({"data": 'Post succesfull'}, status=status.HTTP_201_CREATED)
except IntegrityError as e:
return Response(
{
'error': "event with date and time already exsits. %s-%s-%s at %s:%s" % (
dt.day, dt.month, dt.year, dt.hour, dt.minute),
'detail': str(e)
}, status=status.HTTP_400_BAD_REQUEST
So this code is basically the same as yours, with the difference of the way the objects are saved using the DRF. Look the show_data variable.
This solution is just a different way of looking at the question.
GOOD LUCK!
http://www.django-rest-framework.org/api-guide/serializers/
http://www.django-rest-framework.org/api-guide/fields/
Then your Show model should look like
from django.contrib.postgres.fields import ArrayField
class Show(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
start_date = models.DateField(blank=True, null=True)
end_date = models.DateField(blank=True, null=True)
board = ArrayField(
models.TimeField(blank=True, null=True),
size=10, # specify max array size
)
so you'll have model Show with specified DateFields and array of TimeFields.
django docs: Array Field, Time Field, Date Field
There are some tools in the datetime library that can give you a more streamlined approach to generating your times. You can use toordinal to turn a date into an integer and fromordinal to turn an integer back into a date; this makes a nice way to create a range of dates. And you can use combine to merge a date object and a time object into a datetime. I'd create the following function:
from datetime import datetime, date
def get_showtimes(post):
start = datetime.strptime(post['start_date'], '%Y-%m-%d')
end = datetime.strptime(post['end_date'], '%Y-%m-%d')
times = [datetime.strptime(t, '%I:%M %p').time() for t in post['time_list']]
for ordinal in range(start.toordinal(), end.toordinal() + 1):
date = date.fromordinal(date)
for time in times:
yield datetime.combine(date, time)
then, in your code, replace the second try: except: block and what follows it with:
try:
showtimes = list(get_showtimes(post))
except ValueError as e:
return Response(
{'error': 'Time was not in a supported format. %s' % e},
status=status.HTTP_400_BAD_REQUEST
)
try:
with transaction.atomic():
for showtime in showtimes:
show = Show.objects.create(event=event, date_time=showtime)
except IntegrityError as e:
# etc.
I am leaving the validation part and only focusing on generating Show objects from the given data:
data = request.data
date_format = '%Y-%m-%d'
time_format = "%I:%M %p"
show_time_format = f"{date_format} {time_format}"
# get the total number of days by parsing start and end dates
start_date = datetime.strptime(data['start_date'], date_format)
end_date = datetime.strptime(data['end_date'], date_format)
total_days = (end_date - start_date).days + 1
# Get the timings for the first day.
# We will use this to generate the timings for the rest of the days.
first_day_timings = [
datetime.strptime(f"{data['start_date']} {show_time}", show_time_format)
for show_time in data['time_list']
]
# generate all show objects using list comprehension and bulk create later
show_objects = [
Show(event=event, date_time=first_day_timing + timedelta(days=day_cnt))
for day_cnt in range(total_days)
for first_day_timing in first_day_timings
]
Show.objects.bulk_create(show_objects)
Enhancements made on the existing code:
Reduced the number of instances date/time is getting parsed and hours/minutes are getting computed.
Use list comprehension to generate show objects and bulk create instead of creating one object at a time in a transaction.

Slots Creation based on date and time(float) field

I have two date field.
from_date and
to_date
In One2many line item, there is three float fields
from_time ,to_time and interval
Slot have to be created based on the above parameters.
Example:
from_date = '2017-07-21'
to_date = '2017-07-21'
the duration is one day.
The One2many line items have the values
from_time = 9.0
to_time = 10.0
interval = 30(in minutes)
The output should generate two slots
1. '2017-07-21 09:00:00' '2017-07-21 09:30:00'
2. '2017-07-21 09:30:00' '2017-07-21 10:00:00'
It should generate two line items.
If the duration is for week.
it should generate 2 * 7 = 14 slots.
I have used the code which generates for one day.
#api.one
def generate(self):
cr = self.env.cr
uid = self.env.uid
context = self.env.context
event = self.pool.get('calendar.event')
slot = self.pool.get('slot.booking')
old_data_id = slot.search(cr, uid, [('slot_id', '=',self.id)], context=context)
slot.unlink(cr, uid ,old_data_id)
for each in self.shift_line:
if each.interval > 60 or each.interval == 0:
raise osv.except_osv(_('Attention!'), _('Please enter interval timings in minutes range like (10-60) '))
interval = each.interval
fmt = "%Y-%m-%d"
start_date = datetime.strptime(self.from_date, fmt)
end_date = datetime.strptime(self.to_date, fmt)
days = []
date = start_date
pdb.set_trace()
str_start_time = '%s %s' % (self.from_date,'{0:02.0f}:{1:02.0f}'.format(*divmod(each.from_time * 60, 60)))+':00'
str_end_time = '%s %s' % (self.from_date,'{0:02.0f}:{1:02.0f}'.format(*divmod(each.to_time * 60, 60)))+':00'
time = datetime.strptime(str_start_time, '%Y-%m-%d %H:%M:%S')
end = datetime.strptime(str_end_time, '%Y-%m-%d %H:%M:%S')
while date <= end_date:
hours = []
while time <= end:
hours.append(time.strftime("%Y-%m-%d %H:%M:%S"))
time += timedelta(minutes=interval)
date += timedelta(days=1)
time += timedelta(days=1)
end += timedelta(days=1)
days.append(hours)
print "\n\n\n\n\nn\+++++++++++++++++++++days",days
for hours in days[0][:-1]:
val = datetime.strptime(hours, '%Y-%m-%d %H:%M:%S')
val = val + timedelta(minutes=interval)
values = {
'name' : 'Slot for ' + self.employee_id.name,
'start_datetime' : hours,
'stop_datetime' : str(val),
'slot_id' : self.id,
'shift_lines_id' : each.id,
'partner_id': self.employee_id.id,
'duration' : each.interval,
}
print "\n\n\n\n\n\n\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++values",values
slot.create(cr, uid, values, context=context)
Any help for multiple days is appreciated.
ist_timedelta = timedelta(seconds=((time in seconds)-10800))
adding this will give the exact answer, here 10800 is because of 3 hours difference of UTC to KSA.

How to get only date from "Date" field in python

def generate_leave(self, cr, uid,ids, fields, context=None):
if context is None:
context = {}
month_split = self.browse( cr, uid,ids)
print "\n\n\n\n\n\n DATEE",month_split.name
dt = datetime.strptime(month_split.name, "%Y-%m-%d")
# date= dt.strftime(month_split.name[],'%Y-%m-%d')
# print "\n\n\n\n\n DAT",date
year = dt.year
print "\n\n\n\n YER",year
month = dt.month
print "\n\n\n MNTH",month
currentMonth = datetime.now().month
print "\n\n\n\n\n CURR MNTH",currentMonth
date = dt.date
print "\n\n\n\n\n\n DATTE",date
date = datetime.now().day
print "\n\n\n\n\n\n DATEE",date
This is my code. I am able to get current date separately. But in my scenario user will input a date and I want to take only the "Date" from that input, I don't want month and year.

Convert char to datetime odoo 9

I have two char fields, data import from excel or csv in odoo.
time_1= fields.Char(string = 'Time 1')
time_2= fields.Char(string = 'Time 2')
result= fields.Float(string = 'Result Time 2 - Time 1') #Need result 06:00
time_1 = 10:00:00,
time_2 = 16:00:00 (data from external source)
How with #api.onchange('time_1', 'time_2') or #api.depends('time_1', 'time_2')
convert char to time and subtract time_2 - time_1 and put result in result field?
It should be like that,
from datetime import datetime
#api.multi
#api.onchange('time_1', 'time_2')
def onchange_time(self):
for rec in self:
time1 = datetime.strptime(rec.time1, "%Y-%m-%d %H:%M:%S")
time2 = datetime.strptime(rec.time2, "%Y-%m-%d %H:%M:%S")
rec.result = (time2 - time1).seconds / float(60*60)
Two datetime objects will return timedelta obect in result
while you perform any arithmetic operations on it. timedelta has
property while will give you difference in seconds, so you can convert
that seconds to hours.
And then in view set
<field name="result" widget="float_time" />

Categories