I have an app that needs a cron job. specifically, for ranking part I need my file to run synchronously so the score changes in the background. Here is my code.
I have rank.py in my utils folder
from datetime import datetime, timedelta
from math import log
epoch = datetime(1970, 1, 1)
def epoch_seconds(date):
td = date - epoch
return td.days * 86400 + td.seconds + (float(td.microseconds) / 1000000)
def score(ups, downs):
return ups - downs
def hot(ups, downs, date):
s = score(ups, downs)
order = log(max(abs(s), 1), 10)
sign = 1 if s > 0 else -1 if s < 0 else 0
seconds = epoch_seconds(date) - 1134028003
return round(sign * order + seconds / 45000, 7)
And I have the two functions under Post model, inside models.py
def get_vote_count(self):
vote_count = self.vote_set.filter(is_up=True).count() - self.vote_set.filter(is_up=False).count()
if vote_count >= 0:
return "+ " + str(vote_count)
else:
return "- " + str(abs(vote_count))
def get_score(self):
"""
:return: The score calculated by hot ranking algorithm
"""
upvote_count = self.vote_set.filter(is_up=True).count()
devote_count = self.vote_set.filter(is_up=False).count()
return hot(upvote_count, devote_count, self.pub_date.replace(tzinfo=None))
Problem is I'm not sure how to run cron job for this. I've seen http://arunrocks.com/building-a-hacker-news-clone-in-django-part-4/ and it looks like I need to create another file and another function to make the whole thing run. again and again./but what function?How do I use cron job for my code? I saw there are many applications that allow me to do this, but I'm just not sure which funcction I need to use and how I should use. my guess is I need to run get_score function in models.py but how....
you may consider celery and rabbitmq
the idea is: in your app you create a file called tasks.py and there you put the code:
# tasks.py
from celery import task
#task
def your_task_for_async_job(data):
# todo
just call the function and it does the job for you asyncly..
Here is the documentation for Celery, you find there also how to set it up with django etc..
hope, this helps
Related
I am working with the module "schedule" and trying to figure out how to update the input variables for the function "automated_ticket_booking" while it is running continuously due to schedule.run_pending().
The initial values are given by the function get_events which runs once at the start of the script. My goal ist to update the variables in the "automated_ticket_booking" function and use the returned values as new input data.
I hope the example code will help you to understand my problem.
I appreciate any help on this task.
Thank You!
import schedule
def get_events():
event_date = "01.01.1918"
event_timeslots = "08.15 - 10.00"
return event_date, event_timeslots
def automated_ticket_booking(event_date, event_timeslots):
print(event_date + " : " + event_timeslots)
event_date = "15.06.2022"
event_timeslots = "23:15 - 23:48"
return event_date, event_timeslots
# -------------
[event_date, event_timeslots] = get_events()
schedule.every(10).seconds.do(automated_ticket_booking,
event_date,
event_timeslots)
while True:
schedule.run_pending()
Lets make another function which does two jobs:
def booking_from_latest_events():
event_date, event_timeslots = get_events()
automated_ticket_booking(event_date, event_timeslots)
schedule.every(10).seconds.do(booking_from_latest_events)
So, currently, I am using multiprocessing to run these 3 functions together.
As only tokens changes, is it recommended to switch to multi-threading? (if yes, will it really help in a performance like speed-up and I think memory will be for sure used less)
This is my code:
from database_function import *
from kiteconnect import KiteTicker
import pandas as pd
from datetime import datetime, timedelta
import schedule
import time
from multiprocessing import Process
def tick_A():
#credentials code here
tokens = [x[0] for x in db_fetchquery("SELECT zerodha FROM script ORDER BY id ASC LIMIT 50")] #FETCHING FIRST 50 SCRIPTS TOKEN
#print(tokens)
##### TO MAKE SURE THE TASK STARTS AFTER 8:59 ONLY ###########
t = datetime.today()
future = datetime(t.year,t.month,t.day,8,59)
if ((future-t).total_seconds()) < 0:
future = datetime(t.year,t.month,t.day,t.hour,t.minute,(t.second+2))
time.sleep((future-t).total_seconds())
##### TO MAKE SURE THE TASK STARTS AFTER 8:59 ONLY ###########
def on_ticks(ws, ticks):
global ltp
ltp = ticks[0]["last_price"]
for tick in ticks:
print(f"{tick['instrument_token']}A")
db_runquery(f'UPDATE SCRIPT SET ltp = {tick["last_price"]} WHERE zerodha = {tick["instrument_token"]}') #UPDATING LTP IN DATABASE
#print(f"{tick['last_price']}")
def on_connect(ws, response):
#print(f"response from connect :: {response}")
# Subscribe to a list of instrument_tokens (TOKENS FETCHED ABOVE WILL BE SUBSCRIBED HERE).
# logging.debug("on connect: {}".format(response))
ws.subscribe(tokens)
ws.set_mode(ws.MODE_LTP,tokens) # SETTING TOKEN TO TICK MODE (LTP / FULL / QUOTE)
kws.on_ticks = on_ticks
kws.on_connect = on_connect
kws.connect(threaded=True)
#####TO STOP THE TASK AFTER 15:32 #######
end_time = datetime(t.year,t.month,t.day,15,32)
while True:
schedule.run_pending()
#time.sleep(1)
if datetime.now() > end_time:
break
#####TO STOP THE TASK AFTER 15:32 #######
def tick_B():
everything remains the same only tokens value changes
tokens = [x[0] for x in db_fetchquery("SELECT zerodha FROM script ORDER BY id ASC OFFSET (50) ROWS FETCH NEXT (50) ROWS ONLY")]
def tick_C():
everything remains the same only tokens value changes
tokens = [x[0] for x in db_fetchquery("SELECT zerodha FROM script ORDER BY id ASC OFFSET (100) ROWS FETCH NEXT (50) ROWS ONLY")]
if __name__ == '__main__':
def runInParallel(*fns):
proc = []
for fn in fns:
p = Process(target=fn)
p.start()
proc.append(p)
for p in proc:
p.join()
runInParallel(tick_A , tick_B , tick_C)
So, currently, I am using multiprocessing to run these 3 functions together.
As only tokens changes, is it recommended to switch to multi-threading? (if yes, will it really help in a performance like speed-up and I think memory will be for sure used less)
most Python implementations do not have true multi-threading, because they use global lock (GIL). So only one thread runs at a time.
For I/O heavy applications it should not make difference. But if you need CPU heavy operations done in parallel (and I see that you use Panda - so the answer must be yes) - you will be better off staying with multi-process app.
How can I run a function in Python, at a given time?
For example:
run_it_at(func, '2012-07-17 15:50:00')
and it will run the function func at 2012-07-17 15:50:00.
I tried the sched.scheduler, but it didn't start my function.
import time as time_module
scheduler = sched.scheduler(time_module.time, time_module.sleep)
t = time_module.strptime('2012-07-17 15:50:00', '%Y-%m-%d %H:%M:%S')
t = time_module.mktime(t)
scheduler_e = scheduler.enterabs(t, 1, self.update, ())
What can I do?
Reading the docs from http://docs.python.org/py3k/library/sched.html:
Going from that we need to work out a delay (in seconds)...
from datetime import datetime
now = datetime.now()
Then use datetime.strptime to parse '2012-07-17 15:50:00' (I'll leave the format string to you)
# I'm just creating a datetime in 3 hours... (you'd use output from above)
from datetime import timedelta
run_at = now + timedelta(hours=3)
delay = (run_at - now).total_seconds()
You can then use delay to pass into a threading.Timer instance, eg:
threading.Timer(delay, self.update).start()
Take a look at the Advanced Python Scheduler, APScheduler: http://packages.python.org/APScheduler/index.html
They have an example for just this usecase:
http://packages.python.org/APScheduler/dateschedule.html
from datetime import date
from apscheduler.scheduler import Scheduler
# Start the scheduler
sched = Scheduler()
sched.start()
# Define the function that is to be executed
def my_job(text):
print text
# The job will be executed on November 6th, 2009
exec_date = date(2009, 11, 6)
# Store the job in a variable in case we want to cancel it
job = sched.add_date_job(my_job, exec_date, ['text'])
Might be worth installing this library: https://pypi.python.org/pypi/schedule, basically helps do everything you just described. Here's an example:
import schedule
import time
def job():
print("I'm working...")
schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
while True:
schedule.run_pending()
time.sleep(1)
Here's an update to stephenbez' answer for version 3.5 of APScheduler using Python 2.7:
import os, time
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime, timedelta
def tick(text):
print(text + '! The time is: %s' % datetime.now())
scheduler = BackgroundScheduler()
dd = datetime.now() + timedelta(seconds=3)
scheduler.add_job(tick, 'date',run_date=dd, args=['TICK'])
dd = datetime.now() + timedelta(seconds=6)
scheduler.add_job(tick, 'date',run_date=dd, kwargs={'text':'TOCK'})
scheduler.start()
print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))
try:
# This is here to simulate application activity (which keeps the main thread alive).
while True:
time.sleep(2)
except (KeyboardInterrupt, SystemExit):
# Not strictly necessary if daemonic mode is enabled but should be done if possible
scheduler.shutdown()
I've confirmed the code in the opening post works, just lacking scheduler.run(). Tested and it runs the scheduled event. So that is another valid answer.
>>> import sched
>>> import time as time_module
>>> def myfunc(): print("Working")
...
>>> scheduler = sched.scheduler(time_module.time, time_module.sleep)
>>> t = time_module.strptime('2020-01-11 13:36:00', '%Y-%m-%d %H:%M:%S')
>>> t = time_module.mktime(t)
>>> scheduler_e = scheduler.enterabs(t, 1, myfunc, ())
>>> scheduler.run()
Working
>>>
I ran into the same issue: I could not get absolute time events registered with sched.enterabs to be recognized by sched.run. sched.enter worked for me if I calculated a delay, but is awkward to use since I want jobs to run at specific times of day in particular time zones.
In my case, I found that the issue was that the default timefunc in the sched.scheduler initializer is not time.time (as in the example), but rather is time.monotonic. time.monotonic does not make any sense for "absolute" time schedules as, from the docs, "The reference point of the returned value is undefined, so that only the difference between the results of consecutive calls is valid."
The solution for me was to initialize the scheduler as
scheduler = sched.scheduler(time.time, time.sleep)
It is unclear whether your time_module.time is actually time.time or time.monotonic, but it works fine when I initialize it properly.
dateSTR = datetime.datetime.now().strftime("%H:%M:%S" )
if dateSTR == ("20:32:10"):
#do function
print(dateSTR)
else:
# do something useful till this time
time.sleep(1)
pass
Just looking for a Time of Day / Date event trigger:
as long as the date "string" is tied to an updated "time" string, it works as a simple TOD function. You can extend the string out to a date and time.
whether its lexicographical ordering or chronological order comparison,
as long as the string represents a point in time, the string will too.
someone kindly offered this link:
String Comparison Technique Used by Python
had a really hard time getting these answers to work how i needed it to,
but i got this working and its accurate to .01 seconds
from apscheduler.schedulers.background import BackgroundScheduler
sched = BackgroundScheduler()
sched.start()
def myjob():
print('job 1 done at: ' + str(dt.now())[:-3])
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2000)
job = sched.add_job(myjob, 'date', run_date=Future)
tested accuracy of timing with this code:
at first i did 2 second and 5 second delay, but wanted to test it with a more accurate measurement so i tried again with 2.55 second delay and 5.55 second delay
dt = datetime.datetime
Future = dt.now() + datetime.timedelta(milliseconds=2550)
Future2 = dt.now() + datetime.timedelta(milliseconds=5550)
def myjob1():
print('job 1 done at: ' + str(dt.now())[:-3])
def myjob2():
print('job 2 done at: ' + str(dt.now())[:-3])
print(' current time: ' + str(dt.now())[:-3])
print(' do job 1 at: ' + str(Future)[:-3] + '''
do job 2 at: ''' + str(Future2)[:-3])
job = sched.add_job(myjob1, 'date', run_date=Future)
job2 = sched.add_job(myjob2, 'date', run_date=Future2)
and got these results:
current time: 2020-12-10 19:50:44.632
do job 1 at: 2020-12-10 19:50:47.182
do job 2 at: 2020-12-10 19:50:50.182
job 1 done at: 2020-12-10 19:50:47.184
job 2 done at: 2020-12-10 19:50:50.183
accurate to .002 of a second with 1 test
but i did run a lot of tests and accuracy ranged from .002 to .011
never going under the 2.55 or 5.55 second delay
#everytime you print action_now it will check your current time and tell you should be done
import datetime
current_time = datetime.datetime.now()
current_time.hour
schedule = {
'8':'prep',
'9':'Note review',
'10':'code',
'11':'15 min teabreak ',
'12':'code',
'13':'Lunch Break',
'14':'Test',
'15':'Talk',
'16':'30 min for code ',
'17':'Free',
'18':'Help ',
'19':'watever',
'20':'watever',
'21':'watever',
'22':'watever'
}
action_now = schedule[str(current_time.hour)]
I have a script that is basically complete. I'd like to add some sort of a progress bar to it, instead of printing out each step as it passes by
is there anything that will let me do this.
setup a progress widget/counter/loop
give it a command function to increment
do some script
add the code to advance/increment the progress bar
do some more script
add the code to advance/increment the progress bar
do some more script
add the code to advance/increment the progress bar
do some more script
add the code to advance/increment the progress bar
also, can you please give me an example of some sort
I've looked at 3 or 4 different "progress bar" type libraries, and none give an example of doing it this way
all of the examples I seem to find want to do it by time or by byte size for downloading files
There is a number of progress bars in PIP, I recommend ezprogress if you run python3.
from ezprogress.progressbar import ProgressBar
import time
# Number of steps in your total script
steps_needed = 100
current_step = 0
# setup progress bar
pb = ProgressBar(steps_needed, bar_length=100)
pb.start()
# Do what your script wants
...
# Increment counter
current_step += 1
pb.update(current_step)
# Do what your script wants
...
# When you are done you can force the progress bar to finish
PB.finished()
The progress bar did not support turning off time estimation, however it is now possible in the newest version, just upgrade from PIP.
To turn off time estimation the progress bar just needs to be started with the parameter no_time=True like in the code below:
pb = ProgressBar(steps_needed, bar_length=100, no_time=True)
create your progressbar.py module
import sys
import copy
currentProgressCnt = 0
progressCntMax = 0 #
progressBarWidth = 50 # in chars
scaleFctr = 0.0
tasksToDo = []
class ProgressIndicator:
def showProgress(self):
global progressCntMax
global currentProgressCnt
cr = "\r"
progressChar = '#'
fillChar = '.'
progressBarDone = currentProgressCnt*progressChar*scaleFctr
progressBarRemain = fillChar*(progressCntMax - currentProgressCnt)*scaleFctr
percent = str(int((float(currentProgressCnt)/float(progressCntMax))*100)) + " % completed "
taskId = '(' + tasksToDo[currentProgressCnt - 1] + ') '
quote = str(currentProgressCnt) + '/' + str(progressCntMax) + ' '
sys.stdout.write(cr + progressBarDone + progressBarRemain + ' ' + percent + taskId + quote)
sys.stdout.flush()
if currentProgressCnt == progressCntMax:
print
def incProgress(self):
global currentProgressCnt
currentProgressCnt += 1
def setLastStep(self, size):
global progressCntMax, scaleFctr
progressCntMax = size
scaleFctr = progressBarWidth / progressCntMax
def setTaskList(self, taskList):
global tasksToDo
tasksToDo = copy.copy(taskList)
self.setLastStep(len(tasksToDo))
in main, use the ProgressIndicator class like this:
from progressbar import ProgressIndicator
import time
import datetime
#########################################
### MAIN ###
### SIMULATION ###
#########################################
# your procedure list you have to run
toDoList = ['proc1', 'proc2', 'proc3', 'proc1', 'proc4', 'proc5',
'proc6', 'proc7', 'proc21', 'proc32', 'proc43', 'proc51',
'proc4', 'proc65', 'proc76', 'proc87']
progressLine = ProgressIndicator() # create your indicator
progressLine.setTaskList(toDoList) # set params
# your main work
i = 0; lastTask = len(toDoList)
# log the start
startTime = str(datetime.datetime.now())
print ( startTime + " main started")
while i < lastTask:
# run your task list here
time.sleep(1) # simulating your toDoList[i]() run
i += 1
progressLine.incProgress() # use when task done, incrase progress
progressLine.showProgress() # use for update display
# work is done, log the end
endTime = str(datetime.datetime.now())
print ( endTime + " main finished")
I use a template of a python script (running on Raspberry Pi) to send sensor data (i2c) via WiFi to my PC. The problem is, the values are not static. If I start the Web application, it reads the data from the sensor only once. So, if I check the values from my PC, I can see it sent the data correctly, but they won't change.
How can I modify the script to refresh the i2c_output value, without starting the script over and over again?
Here is what I have tried so far:
import web
import sys, os
import smbus
import math
#
# Lot of initialisation... forget that part
#
accel_xout = read_word_2c(0x3b)
accel_yout = read_word_2c(0x3d)
accel_zout = read_word_2c(0x3f)
afs_sel = read_word_2c(0x28)
LSB_afs_sel = 16384.0
accel_xout_sc = accel_xout / LSB_afs_sel
accel_yout_sc = accel_yout / LSB_afs_sel
accel_zout_sc = accel_zout / LSB_afs_sel
i2c_output = str(accel_xout_sc) + str(accel_yout_sc) + str(accel_zout_sc)
urls = ( '/','Index',
)
class Index:
def GET(self):
return i2c_output
if __name__=="__main__":
app=web.application(urls,globals())
app.run()
Move the code that retrieves the sensor data into a method and invoke that method each time the index is called.
def get_sensor_output():
#
# Lot of initialisation... forget that part
#
accel_xout = read_word_2c(0x3b)
accel_yout = read_word_2c(0x3d)
accel_zout = read_word_2c(0x3f)
afs_sel = read_word_2c(0x28)
LSB_afs_sel = 16384.0
accel_xout_sc = accel_xout / LSB_afs_sel
accel_yout_sc = accel_yout / LSB_afs_sel
accel_zout_sc = accel_zout / LSB_afs_sel
i2c_output = str(accel_xout_sc) + str(accel_yout_sc) + str(accel_zout_sc)
return i2c_output
class Index:
def GET(self):
return get_sensor_output()
Note: You may want to implement some sort of cache depending on how often this get method is called. currently each call will retrieve the sensor data, which may or may not be an expensive operation that will drain the battery on your pi