Django filter if weekend between range - python

I have the following models:
class Destination_Deal(models.Model):
name = models.CharField(_("Nombre"),max_length=200)
duration = models.IntegerField(_(u"Días"))
class Departure_Date(models.Model):
date_from= models.DateField(_('Desde'))
date_to= models.DateField(_('Hasta'))
destination_deal = models.ForeignKey(Destination_Deal,verbose_name = _("Oferta de Destino"))
I would like to filter Destination Deals that are suitable to travel in a weekend. That means:
Departure Day = Friday or Saturday
Return Day = Sunday. So duration must be 3 or 2 if departure day is Friday or Saturday.
Example
Destination_Deal
id name duration
1 Deal1 3
2 Deal2 5
3 Deal3 2
4 Deal4 7
Departure_Date
id date_from date_to destination_deal_id
1 2012-11-05 2012-11-15 1
2 2012-11-01 2012-12-16 2
3 2013-01-21 2013-01-27 3
4 2013-01-14 2013-01-18 3
5 2013-01-04 2013-01-11 4
Desired Result
ID1: 2012-11-09 was Friday and the deal's duration is 3. So in this case, Friday, Saturday and Sunday conform a valid weekend.
ID3: 2013-01-26 is Saturday and the deal's duration is 2. Also is valid.
--Edit--
Ok, sorry if I was not clear. I need to filter the Destination Deals based on the above weekend rule. I was thinking to do it by getting the date_from from the model (DateField) to python (datetime), iterate it until date_to and use weekday() function to check if it is a Friday or Saturday. I am aware of django weekday function but it will only work on a specific date (no range) so I would like to know if there is a simpler approach for this scenario.

where = '(deparure_date__date_from - deparure_date__date_to) > 4 || \
DAYOFWEEK(deparure_date__date_from) IN (1, 6, 7) || \
DAYOFWEEK(deparure_date__date_to) IN (1, 6, 7)'
Destination_Deal.objects.filter(duration__in=[1, 2, 3]) \
.extra(where=[where])
If duration in 1, 2, 3
And If difference of date_from and date_to > 4 then definitely there is going to be either Friday, or Saturday or Sunday in between.
or check if date_from is Friday, or Saturday or Sunday
or date_to is Friday, or Saturday or Sunday

Related

Combine weekday with hours in Pandas

I have a data frame with a weekday column that contains the name of the weekdays and a time column that contains hours on these days. How can I combine these 2 columns, so they can be also sortable?
I have tried the string version but it is not sortable based on weekdays and hours.
This is the sample table how it looks like.
weekday
time
Monday
12:00
Monday
13:00
Tuesday
20:00
Friday
10:00
This is what I want to get.
weekday_hours
Monday 12:00
Monday 13:00
Tuesday 20:00
Friday 10:00
Asumming that df is your initial dataframe
import json
datas = json.loads(df.to_json(orient="records"))
final_data = {"weekday_hours": []}
for data in datas:
final_data["weekday_hours"].append(data['weekday'] + ' ' + data['time'])
final_df = pd.DataFrame(final_data)
final_df
Ouptput:
you first need to create a datetime object of 7 days at an hourly level to sort by. In a normal Data warehousing world you normally have a calendar and a time dimension with all the different representation of your date data that you can merge and sort by, this is an adaptation of that methodology.
import pandas as pd
df1 = pd.DataFrame({'date' : pd.date_range('01 Jan 2021', '08 Jan 2021',freq='H')})
df1['str_date'] = df1['date'].dt.strftime('%A %H:%M')
print(df1.head(5))
date str_date
0 2021-01-01 00:00:00 Friday 00:00
1 2021-01-01 01:00:00 Friday 01:00
2 2021-01-01 02:00:00 Friday 02:00
3 2021-01-01 03:00:00 Friday 03:00
4 2021-01-01 04:00:00 Friday 04:00
Then create your column to merge on.
df['str_date'] = df['weekday'] + ' ' + df['time']
df2 = pd.merge(df[['str_date']],df1,on=['str_date'],how='left')\
.sort_values('date').drop('date',1)
print(df2)
str_date
3 Friday 10:00
0 Monday 12:00
1 Monday 13:00
2 Tuesday 20:00
Based on my understanding of the question, you want a single column, "weekday_hours," but you also want to be able to sort the data based on this column. This is a bit tricky because "Monday" doesn't provide enough information to define a valid datetime. Parsing using pd.to_datetime(df['weekday_hours'], format='%A %H:%M' for example, will return 1900-01-01 <hour::minute::second> if given just weekday and time. When sorted, this only sorts by time.
One workaround is to use dateutil to parse the dates. In lieu of a date, it will return the next date corresponding to the day of the week. For example, today (9 April 2021) dateutil.parser.parse('Friday 10:00') returns datetime.datetime(2021, 4, 9, 10, 0) and dateutil.parser.parse('Monday 10:00') returns datetime.datetime(2021, 4, 12, 10, 0). Therefore, we need to set the "default" date to something corresponding to our "first" day of the week. Here is an example starting with unsorted dates:
import datetime
import dateutil
import pandas as pd
weekdays = ['Friday', 'Monday', 'Monday', 'Tuesday']
times = ['10:00', '13:00', '12:00', '20:00', ]
df = pd.DataFrame({'weekday' : weekdays, 'time' : times})
df2 = pd.DataFrame()
df2['weekday_hours'] = df[['weekday', 'time']].agg(' '.join, axis=1)
amonday = datetime.datetime(2021, 2, 1, 0, 0) # assuming week starts monday
sorter = lambda t: [dateutil.parser.parse(ti, default=amonday) for ti in t]
print(df2.sort_values('weekday_hours', key=sorter))
Produces the output:
weekday_hours
2 Monday 12:00
1 Monday 13:00
3 Tuesday 20:00
0 Friday 10:00
Note there are probably more computationaly efficient ways if you are working with a lot of data, but this should illustrate the idea of a sortable weekday/time pair.

Python: Delta between two dates based on a fixed range

I would like to get the delta in hours between 8p.m. and 9 a.m based on a start_time and end_time.
It is like counting the delta in hours between two dates but taking only in account the hours between 8 p.m. and 9.am
for example:
start_date, end_date, result
2020-10-25 10:35:30, 2020-10-25 23:35:32, 3hours and 35 minutes (start counting at 8pm)
2020-10-25 08:14:37, 2020-10-25 20:14:37, approx 1 hour (from 8h14 to 9am + from 8pm to 8h14 )
2020-10-25 00:00:00, 2020-10-26 00:00:00, from 8pm to 9 am = (4+9 = 13 hours)
Thank you

Python Dataframe: Get number of week days present in last month?

I have df with column day_name. I'm trying to get number of week_days present in last month?
I'm trying to get number of week_days present in last month.
For ex: There are 4 Fridays and 5 Thrusdays in April
df
day_name
0 Friday
1 Sunday
2 Thursday
3 Wednesday
4 Monday
As per python for a single day:
import calendar
year = 2020
month = 4
day_to_count = calendar.WEDNESDAY
matrix = calendar.monthcalendar(year,month)
num_days = sum(1 for x in matrix if x[day_to_count] != 0)
How do i use this in dataframe or any suggestions?
expected output
day_name last_months_count
0 Friday 4
1 Sunday 4
2 Thursday 5
3 Wednesday 5
4 Monday 4
For last month:
year, month = 2020, 4
start,end = f'{year}/{month}/1', f'{year}/{month+1}/1'
# we exclude the last day
# which is first day of next month
last_month = pd.date_range(start,end,freq='D')[:-1]
df['last_month_count'] = df['day_name'].map(last_month.day_name().value_counts())
Output:
day_name last_month_count
0 Friday 4
1 Sunday 4
2 Thursday 5
3 Wednesday 5
4 Monday 4
Bonus: to extract last month programatically:
from datetime import datetime
now = datetime.now()
year, month = now.year, now.month
# first month of the year
if month == 1:
year, month = year-1, 12
Here you go:
from datetime import date, timedelta
from calendar import day_name
import pandas as pd
today = date.today()
dt = date(today.year, today.month, 1) - timedelta(days=1)
day_to_count = {}
month = dt.month
while dt.month == month:
key = day_name[dt.weekday()]
day_to_count[key] = day_to_count.get(key, 0) + 1
dt -= timedelta(days = 1)
df = pd.DataFrame({
'day_name': ['Friday', 'Sunday', 'Thursday', 'Wednesday', 'Monday']
})
df['last_months_count'] = df['day_name'].apply(lambda day : day_to_count[day])
print(df)
Output:
day_name last_months_count
0 Friday 4
1 Sunday 4
2 Thursday 5
3 Wednesday 5
4 Monday 4

How can I find the fiscal Week of date in Pandas

I have a column with many dates: sample of the said list below
Dates
1 2019-02-01
2 2018-03-10
3 2019-08-01
4 2020-02-07
I would like to have it so that if input a date, of any year I can get the week number.
However, the fiscal year starts on Aug 1 of any given year.
I tried just shifting the date to Jan 1 but it's different for every year due to leap years.
data['Dates'] = pd.to_datetime(data['Dates'])
data['Week'] = (data['Dates'] - timedelta(days=215)).week
print(data)
how can I get a result similar to this one below
Dates Week
1 2019-02-01 27
2 2018-03-10 32
3 2019-08-01 1
4 2020-02-07 28
-Note: the weeks are probably incorrect.
The other answer ignores the fiscal year part of the OP. I am leaving the fiscal year start date calc to the reader but this will calculate the week number (where Monday is the start of the week) from an arbitrary start date.
from dateutil import relativedelta
from datetime import date, datetime, timedelta
NEXT_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO)
LAST_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO(-1))
ONE_WEEK = timedelta(weeks=1)
def week_in_fiscal_year(d: date, fiscal_year_start: date) -> int:
fy_week_2_monday = fiscal_year_start + NEXT_MONDAY
if d < fy_week_2_monday:
return 1
else:
cur_week_monday = d + LAST_MONDAY
return int((cur_week_monday - fy_week_2_monday) / ONE_WEEK) + 2
adapted from this post
Convert it to a datetime, then call datetime.date(2010, 6, 16).strftime("%V")4
You can also use isocalendar which will return a tuple, as opposed to a string above datetime.date(2010, 6, 16).isocalendar()[1]
How to get week number in Python?

Capturing Holiday when computing 3 business days prior from given date

Lets say we have a date as 2019-11-19 which is Tuesday. Now I want to get the 3 business days back from Tuesday i.e. I want to get 2019-11-15 as 16th and 17th are Saturday and Sunday respectively. To achieve this I have the following code:
dt_tue = datetime.strptime('2019-11-19','%Y-%m-%d')
bd-3 = dt_tue - timedelta(days=3) #<--- 3 business days prior
for i in range(bd_3.day,dt_tue.day+1):
dt_in = datetime(dt_tue.year,dt_tue.month,i)
if dt_in.weekday() > 5:
bd_3 = dt_tue - timedelta(4)
The above code generates bd_3 as 15th Nov 2019 which is Friday, and this is correct.
I want to handle a holiday (as provided in dataframe) in the above code. So for example, if dt_in falls on any holiday (including bd_3 and the dt_tue), then the bd_3 should be 14th Nov. Except that Holiday falls on Saturday or Sunday then bd_3 should be 15th Nov only.
Can any body please throw some light? Assume holiday Dataframe looks like below:
Date Holiday_name Day
January 1, 2019 New Year's Day Tuesday
January 21, 2019 Martin Luther King Day Monday
February 18, 2019 Presidents' Day* Monday
May 27, 2019 Memorial Day Monday
Since you're busy looping over all the days anyway, I suggest just doing a simple back-up and check each day as you go, something like:
dt_tue = datetime.strptime('2019-11-19','%Y-%m-%d')
current_day = dt_tue
days_before = 0
while days_before < 2:
# Skip weekends and holiday (without counting as a business day)
while current_day.weekday() >= 5 or current_day in df['Date']:
current_day -= timedelta(days=1)
# Step back a business day
current_day -= timedelta(days=1)
days_before += 1
bd_3 = current_day
You may need to tweak that a bit as I'm not 100% sure how your holidays dataframe is formatted.

Categories