Django-Background-Tasks: Initialize Task at midnight and repeat every midnight - python

Good day SO,
I am currently using Django, and the Django-Background-Tasks package. I have a periodic task that I need to run at midnight, and it is to be repeated every midnight.
I am a beginner at using the package, and am confused by the following:
How do I set the repeat parameter during initialization?
Here is my code:
from background_task import background
from datetime import datetime, date
today_date = datetime.datetime.today()
today_midnight = today_date.replace(hour=23, minute=59, second=59)
#background(schedule=today_midnight)
def send_reminders():...
send_reminders(repeat=Task.DAILY)
I wanted to set the parameter 'repeat' to task.DAILY, as stated in the documentation. However, I have encountered the following:
NameError: name 'Task' is not defined
I know I have to import something to define Task, but I couldn't find it. Can anyone help me?

This is defined in the background_task.models module [GitHub]. So you should import this with:
from background_task.models import Task
DAILY itself just specifies the number of seconds, so 24×60×60=86'400:
class Task(models.Model):
# ...
HOURLY = 3600
DAILY = 24 * HOURLY

Related

Python - Get current UTC time. Ignore computer clock always

Does anyone have any tips to get the current UTC time, from online somewhere, and write some decent python code assuming my computer clock is always wrong?
current_datetime = datetime.datetime.utcnow() #---> assume always wrong
current_datetime = datetime.datetime.now() #---> assume always wrong
Using '.utcnow()' or '.now()' both depend upon the accuracy of my computer clock.
I want to write the code assuming that if it runs from a computer with a bad clock, it still gets the correct time.
BACKGROUND:
I am trying to retool my code to entirely live in UTC time.
My use case is to do some time series analysis.
I keep finding myself accidentally being off 5 hours from EST, or off 1 hour from daylight savings when doing calculations.
The tools within the datetime.datetime objects are great, however it would be nice be able to flag some setting when importing the datetime library and prevent reading my computer clock entirely, to avoid any accidental clock badness issue.
EXAMPLE OF CODE I AM LOOKING FOR:
import datetime
import requests
#force datetime libaries to never read my computer clock:
datetime.some_settings_function( readcomputerclock = False/'Never' )
#get the current time with some API:
current_utc_date_and_time_from_online = requests.get(...) #some api get request
current_utc_datetime = transform( current_utc_date_and_time_from_oneline )
#Transform back and forth to UTC Epoch time:
current_utc_epoch = current_utc_datetime.timestamp()
current_utc_datetime_again = datetime.datetime.fromtimestamp(current_utc_epoch)
#current_utc_datetime == current_utc_datetime_again
#Trigger exception with new settings, when i accidentally write code
# that would ask datetime library to attempt to read computer clock:
fail_code_line = datetime.datetime.now()
# >>> trigger some exception here
TLDR; I am looking for a reliable UTC api for python, and a way to prevent datetime from ever reading my computer clock again.
UPDATE: After accepting the provided answer it has become clear to me for my purposes, trusting my computer clock for a few seconds after updating my computer clock from a trusted source, then asking my computer clock for UTC time within those few seconds is good enough. It is a feasible coding practice to write a "get UTC time now" code using all the information within the accepted answer, that is accurate to within a second or two. (No I have not done the statistical confidence interval posterior on the accuracy) It is then further feasible to write all the rest of my code such that all logic will assume UTC time.
Getting correct, timezone aware datetimes and unix timestamps
Turns out this question was rather about how to convert to / from unix timestamps and datetimes.
The correct solution in python 3 should be:
from datetime import datetime, timezone
# get the current utc time
t = datetime.now(timezone.utc)
# convert to unix, this will keep the utc timezone
unix = t.timestamp()
# convert back to datetime, specifying that the timestamp is in UTC
t2 = datetime.fromtimestamp(unix, tz=timezone.utc)
Other timezones
Since python 3.9, the stdlib has the zoneinfo library, you can use this to convert between timezones.
For python < 3.9, you have to use a thirdparty library like dateutil.
from datetime import datetime
from zoneinfo import ZoneInfo
now_berlin = datetime.now(ZoneInfo('Europe/Berlin'))
now_ny = now_berlin.astimezone(ZoneInfo('America/New_York'))
print('Time in Berlin:', now_berlin)
print('Time in New York', now_ny)
Actually using ntp instead of the computer clock
You can use ntplib:
from ntplib import NTPClient
from datetime import datetime, timezone
client = NTPClient()
response = client.request('europe.pool.ntp.org', version=3)
time = datetime.fromtimestamp(resp.tx_time, tz=timezone.utc)
Edit: I however don't see a real reason, why just from traveling this should go wrong:
from datetime import datetime, timezone
dt = datetime.now(timezone.utc)
for more information see: https://blog.ganssle.io/articles/2019/11/utcnow.html

Prefect workflow: How to persist data of previous/every schedule run?

In prefect workflow, I'm trying to persist data of every schedule run. I need to compare data of every previous and current result. I tried Localresult and checkpoint=true but its not working. For example,
from prefect import Flow, task
from prefect.engine.results import LocalResult
from prefect.schedules import IntervalSchedule
from datetime import timedelta, datetime
import os
import prefect
#task("func_task_target.txt", checkpoint=True, result=LocalResult(dir="~/.prefect"))
def file_scan():
files = os.listdir(test)
#prefect.context.a = files
return files
schedule = IntervalSchedule(interval=timedelta(seconds=61))
with Flow("Test persist data", schedule) as flow:
a = file_scan()
flow.run()
My flow scheduled for every 61 seconds/a minute. On the first run I might get empty result but for the 2nd scheduled run I should get previous flow result to compare. can anyone help me to achieve this? Thanks!
Update (15 November 2021) :
Not sure what is the reason,
LocalResult and checkpoint actually worked when I ran the registered flow through the dashboard or cli prefect run -n "your-workflow.py" --watch. It doesn't work when I manually trigger the flow (e.g.: flow.run) in python code.
Try these following two options:
Option 1 : using target argument:
https://docs.prefect.io/core/concepts/persistence.html#output-caching-based-on-a-file-target
#task(target="func_task_target.txt", checkpoint=True, result=LocalResult(dir="~/.prefect"))
def func_task():
return "999"
Option 2 : instantiate LocalResult instance and invoke write manually.
MY_RESULTS = LocalResult(dir="./.prefect").
#task(checkpoint=True, result=LocalResult(dir="./.prefect"))
def func_task():
MY_RESULTS.write("999")
return "999"
PS:
Having same problem as LocalResult doesn't seem to work for mewhen used in decorator . e.g :
#task("func_task_target.txt", checkpoint=True, result=LocalResult(dir="~/.prefect"))
def file_scan():

Comparing two Python 3 datetime objects returns "can't compare offset-naive and offset-aware datetimes: TypeError"

I am trying to compare the time of an AWS EC2 instance object that is of type datetime with another datetime being represented as datetime.datetime.now. The line of code in question looks like,
if launchTime < datetime.datetime.now()-datetime.timedelta(seconds=20):
Where launchTime is of type datetime. However when I run it I get the error
can't compare offset-naive and offset-aware datetimes: TypeError
And I'm unsure of how to convert launchTime in such a way where I can successfully compare it.
Edited fixed code below -----------------------------------------
if launchTime.replace(tzinfo=None) < datetime.datetime.now()-datetime.timedelta(minutes=4):
Full code as well in case any future people find it of value. It's Python 3 to stop EC2 instances that have been running for an "x" amount of time. In this case if an instance is running for five minutes. Terminate it. The lambda itself is set up with Cloudwatch to run every 4 minutes as well.
import boto3
import time
import datetime
#for returning data about our newly created instance later on in fuction
client = boto3.client('ec2')
def lambda_handler(event, context):
response = client.describe_instances()
#for each instance currently running/terminated/stopped
for r in response['Reservations']:
for i in r['Instances']:
#if its running then we want to see if its been running for more then 3 hours. If it has then we stop it.
if i["State"]["Name"] == "running":
launchTime = i["LaunchTime"]
#can change minutes=4 to anything
if launchTime.replace(tzinfo=None) < datetime.datetime.now()-datetime.timedelta(minutes=4):
response = client.stop_instances(
InstanceIds=[
i["InstanceId"]
]
)
The main problem is that I'm assuming launchTime is timezone aware, whereas datetime.now() is not (datetime.now().tzinfo == None).
There are a couple ways to solve this, but the easiest would be to remove the tzinfo from launchTime: if launchTime.replace(tzinfo=None) < datetime.datetime.now()-datetime.timedelta(seconds=20) should do the trick.
Alternatively, you can convert your datetime objects to Unix timestamps and then you don't have to deal with timezone silliness.
Try like this, you have to make sure pytz installed :
import pytz
utc=pytz.UTC
launchTime = utc.localize(launchTime)

Python - Recalculate current date

I have a python app running in a server (using flask) and i am having problems with the current date.
I have a method that calculates current date:
import time
def calcCurrent():
return(time.strftime("%d/%m/%Y"))
It works fine during this day, but, if I try to access to the server the day after and I call this method, it still returns the previous day date. It is, it doesn't recalculate current date.
Any idea on how could I do this?
try using this
import datetime
def calcCurrent():
return(datetime.datetime.now().strftime("%d/%m/%Y"))

Python NonExistentTimeError

I'm creating a simple Django API where access tokens are used. The idea is that, when a user requests a token, said token is valid for the next 7 hours.
However when generating the token I obtain a Python NonExistentTimeError. Code is:
#Django view
...
expires = datetime.datetime.now() + datetime.timedelta(hours=7)
token = APIMasterToken.objects.create(
token=token,
expiration_date=expires
)
However I obtain an exception generating a token with said date:
NonExistentTimeError at /api/obtain_master_token/
2013-03-10 02:05:12.179508
What does this error mean and how can I solve it?
EDIT: I just read the documentation and it would appear this error is raised when a day occurs that has a clock change following DST. However, I don't know how to solve the problem. Shouldn't the timedelta function take care of this?
Django expects to work with timezone aware datetime objects.
From the Django documentation, the now() call would become:
import datetime
from django.utils.timezone import utc
now = datetime.datetime.utcnow().replace(tzinfo=utc)
expires = now + datetime.timedelta(hours=7)
Better still, use the now() function from django.utils.timezone.
Both datetime.now() and datetime.utcnow() return naive datetime objects which is not what Django requires. With a time zone applied to expires, Django is then able to convert back to UTC for storage as UTC in the database backend.
The NonExistentTimeError is actually thrown by code from the pytz module. The pytz documentation is definitely worth reading as there's many gotchas when dealing with time zones.

Categories