Python, doing task 3 times a day while other stuff is happening? - python

So I have a task that occurs three times a day at a certain time that needs to be executed.
I've set up code that does this using a package called Schedule
https://pypi.python.org/pypi/schedule
What I like about this is I can say, run at 3:00AM every day, or something similar.
However, the issue is, I want my other code to be running at the same time, and not be stuck in the same loop that the Schedule is running in
So right now, it looks something like:
def archerPull():
#insert code for calling archer pull here
with open("LogsForStuffPull.txt", "a") as myfile:
myfile.write("time: " + time.ctime(time.time()))
#this is code for scheduling job to do every day
def schedulingTasks(firstTime, secondTime, thirdTime, fourthTime, fivthTime):
schedule.every().day.at(firstTime).do(archerPull)
schedule.every().day.at(secondTime).do(archerPull)
schedule.every().day.at(thirdTime).do(archerPull)
schedule.every().day.at(fourthTime).do(archerPull)
schedule.every().day.at(fivthTime).do(archerPull)
schedulingTasks("13:46", "13:47", "13:48", "13:49", "13:50")
while True:
schedule.run_pending()
time.sleep(1)
So as you can see, the loop will be True forever, and therefore run the scheduler everyday. But if I want to integrate other stuff with it, will it also be looped forever?
I want the tasks to be indivitual occuring (is asynchronous the word for it)
Please help me out, thanks!

Yeah, I figured this out the same day I asked this question
I used ap-scheduler to do this, my webapp in flask is running well while the backgrounds tasks I needed work great

Related

Reminder for a specific time in python

I need to write a python program that lets me set reminders for specific times, eg 'remember to take bins out at 2pm', but I can only work out setting a reminder for a certain length of time, not for a given time. I also need to be able to set multiple reminders for multiple times.
Any help would be much appreciated :)
This looks like a homework assignment, so you need to write the code yourself.
You know what time it is now. You know when 2pm is. How much time is there between now and 2pm? Sleep for that long.
Keep a list of all pending alarms. Find the earliest alarm. Remove it from the list. Sleep until that alarm happens. Repeat
You'll probably find Step 2 easier if you use an appropriate data structure like heapq or PriorityQueue. But if the number of alarms is small, a list should do just fine.
The following checks for any new reminders every seconds,
although, after reading Frank's answer, that would be a better solution,
and the best solution is to not use Python at all, and let the operating system manage this by creating a cron job or on Windows a scheduled task.
reminders = [
# Put all of your reminders here
('2021-11-16 02:44:00', 'Take out the garbage'),
('2021-11-17 04:22:00', 'Another reminder')
]
from datetime import datetime
import time
# For performance reasons it's best to perform whatever computations we can before we go into our infinite loop
# In this case let's calculate all of the timestamps
reminders2 = {datetime.fromisoformat(reminder[0]).timestamp(): reminder[1] for reminder in reminders}
while True:
now = time.time()
for timestamp, reminder_msg in reminders2.items():
if timestamp < now:
print(reminder_msg)
del reminders2[timestamp]
# we're not in any hurry, instead of worrying about the consequences of deleting something from the same dictionary we are iterating over
# we can just break and wait for the next go around of the while loop to finish checking the remaining reminders
break
time.sleep(1) # in seconds

Datetime object switches values during while loop interations

I'm seeing this weird issue in my Python code, and not quite sure what is going on. So quick backstory on the code, I've got a function that my UI script calls on startup that runs in a separate thread. This thread is intended to call a function in a separate module that checks to see if the stored timestamp in a database is older than x_value, and does stuff if that's true.
So here's the function (I can post more code later if needed, but this function is where the issue is happening):
def tableUpdateCheck(sensor_manager):
last_check = datetime.now()
print(f"last_check: {last_check.strftime('%m/%d/%Y_%H:%M:%S')}")
while RUN_TABLE_UPDATE_THREAD:
print(f"Now: {datetime.now().strftime('%H:%M:%S')}\tLast check time: {last_check.strftime('%H:%M:%S')}")
if datetime.now() - last_check > timedelta(seconds=60):
print('Running update table update check...')
sensor_manager.updateTables()
last_check = datetime.now()
time.sleep(5)
if __name__ == '__main__':
sensor_mgr = SensorManager()
table_thread = Thread(target=tableUpdateCheck, args=[sensor_mgr])
table_thread.start()
# Start webserver stuff
The resulting output looks like this:
Now: 17:49:13 Last check time: 17:49:13
Now: 17:49:18 Last check time: 17:49:10
Now: 17:49:23 Last check time: 17:49:13
Now: 17:49:28 Last check time: 17:49:10
Now: 17:49:33 Last check time: 17:49:13
Now: 17:49:38 Last check time: 17:49:10
And continues to do this until the if statement runs, and "last_check" goes back to fluctuating between two values.
Any thoughts on why this might be? It doesn't stop the overall program from ultimately doing what it's supposed to do, but it's super annoying.
So for anyone who might stumble across this in the future, it turns out that "app.run_server(debug=True)" in Flask apps causes an extra thread to run at start. I'm not 100% why this is the case, but that seems to be what's causing the problem I'm describing.
However, using a production Flask app, or simply setting the "debug" argument to False on a development server will not start the multiple threads, and this issue goes away.

What's the most efficient way to pause during a loop in python?

I have a script which loops through the elements of a list. Each element is used to query an API. However, the API has a query limit (only 500 queries are permitted over a 24 hour period). I am currently managing this through a counter in a loop, which resets for each "chunk" of 500 elements and would pause the loop for a day. Is there a better way to do this?
counter = 0
for query in queries:
if counter < 500:
counter = counter + 1
api = ApiClient(api_key='secretkey')
data = api.get(q=query)
print(data)
safequery = ''.join(e for e in query if e.isalnum())
datafilename = "{} {}.txt".format(safequery,todaysdate)
with open(datafilename, 'w') as outfile:
json.dump(data, outfile)
else:
print('sleepy time')
time.sleep(86400)
counter = 0
time.sleep(86400) is asking for problems, and also makes your CPU work for nothing. If something happens during those 86400 seconds and the script crashes, nothing will restart it.
The much better option would be to save the current page/chunk somewhere (raw text file, json, DB, doesn't really matter), then load it before making the next requests.
Then you can put your script in an Operating System level/managed task scheduler (for example, cron for Unix or Task Scheduler for Windows) and run it daily.
time.sleep() is a good solution, but you can also make Python ask for input when you want to continue. That's primitive, I know.
if counter % 500 == 0: # make the counter start at 1
val = input("\nContinue? [y/n]: ")
if val == 'y':
pass # manually unpause the looping, whenever you want
elif val == 'n':
break # interrupt for loop
I would tackle this by creating a script that when ran will get the next 500 and then terminate. You might want to output a text file to store where you are up to in this sequence.
I would then schedule this script to run every 24 hours with windows task scheduler (on windows)
This means you are not having a process running doing nothing.
sleep()
should only be used for small time intervals.
I think you could make this code as a python script and execute in a batch file.
catch this batch file and schedule into a task manager to run every day at 2:00 pm for example...
usually i have a python script server that runs my robots and things that i need to do automatically.
An if else statement with a sleep is probably as simple as it gets; however it's not efficient since the process will still be alive and doing nothing for 86400 seconds.
You could look into creating a cron job to run your code one a day at the same time

Python script schedule to run on day 1 of each month at 2:00 am

I have a script that runs every 30 minutes but there is a section I just want to run on the first of the month at 2:00 am. I am using schedule in Python and I can't figure out how to set it for day 1 of the month.
month doesn't seem to be in the defined parameters of schedule to do something like schedule.every().month.at("02:00").do(job2)
Any suggestions? I am using python 2.7
Simplified code:
from safe_schedule import SafeScheduler
import time
def job():
print "I'm working...",
return
def scheduler():
# Schedule every30min routines
print 'Starting Scheduler'
scheduler = SafeScheduler()
scheduler.every(30).minutes.do(job)
#scheduler.every().month.at("02:00").do(job2)
while True:
scheduler.run_pending()
time.sleep(1)
if __name__ == '__main__':
scheduler()
The main contributor of the library discourages this sort of thing, see https://github.com/dbader/schedule/issues/73#issuecomment-167758653.
Yet, if one insists, one can schedule a daily job but run it only if it's the 1st of the month.
from datetime import date
from safe_schedule import SafeScheduler
def job2():
if date.today().day != 1:
return
# actual job body
scheduler = SafeScheduler()
scheduler.every().day.at("02:00").do(job2)
Another alternative is described in one of the issue comments https://github.com/dbader/schedule/issues/73#issuecomment-356769023.
EDIT :
They mention something about this in many issues of the project but a clean solution doesn't seem to exist yet. Also the whole project doesn't seem to be active anymore so I don't think this feature will be implemented anytime soon.
IMO you need to check manually if the current day is the first day of the month in your job in order to do this.
ORIGINAL ANSWER (which is wrong):
I've looked at the documentation and you are right, there isn't any mention of month:
https://schedule.readthedocs.io/en/stable/api.html#schedule.Scheduler
But there is an issue opened on the project that mentions just that:
https://github.com/dbader/schedule/issues/73
You can do this:
scheduler.every().day.if(lambda d,t: d.day == 1 and t.hour == 2).do(x)
I didn't see any mention of Windows Task Scheduler, here, but that could definitely work for you, if you are on Windows, of course.
https://www.digitalcitizen.life/how-create-task-basic-task-wizard

How to transfer a value from a function in one script to another script without re-running the function (python)?

I'm really new to programming in general and very inexperienced, and I'm learning python as I think it's more simple than other languages. Anyway, I'm trying to use Flask-Ask with ngrok to program an Alexa skill to check data online (which changes a couple of times per hour). The script takes four different numbers (from a different URL) and organizes it into a dictionary, and uses Selenium and phantomjs to access the data.
Obviously, this exceeds the 8-10 second maximum runtime for an intent before Alexa decides that it's taken too long and returns an error message (I know its timing out as ngrok and the python log would show if an actual error occurred, and it invariably occurs after 8-10 seconds even though after 8-10 seconds it should be in the middle of the script). I've read that I could just reprompt it, but I don't know how and that would only give me 8-10 more seconds, and the script usually takes about 25 seconds just to get the data from the internet (and then maybe a second to turn it into a dictionary).
I tried putting the getData function right after the intent that runs when the Alexa skill is first invoked, but it only runs when I initialize my local server and just holds the data for every new Alexa session. Because the data changes frequently, I want it to perform the function every time I start a new session for the skill with Alexa.
So, I decided just to outsource the function that actually gets the data to another script, and make that other script run constantly in a loop. Here's the code I used.
import time
def getData():
username = '' #username hidden for anonymity
password = '' #password hidden for anonymity
browser = webdriver.PhantomJS(executable_path='/usr/local/bin/phantomjs')
browser.get("https://gradebook.com") #actual website name changed
browser.find_element_by_name("username").clear()
browser.find_element_by_name("username").send_keys(username)
browser.find_element_by_name("password").clear()
browser.find_element_by_name("password").send_keys(password)
browser.find_element_by_name("password").send_keys(Keys.RETURN)
global currentgrades
currentgrades = []
gradeids = ['2018202', '2018185', '2018223', '2018626', '2018473', '2018871', '2018886']
for x in range(0, len(gradeids)):
try:
gradeurl = "https://www.gradebook.com/grades/"
browser.get(gradeurl)
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:3]
if grade[2] != "%":
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:4]
if grade[1] == "%":
grade = browser.find_element_by_id("currentStudentGrade[]").get_attribute('innerHTML').encode('utf8')[0:1]
currentgrades.append(grade)
except Exception:
currentgrades.append('No assignments found')
continue
dictionary = {"class1": currentgrades[0], "class2": currentgrades[1], "class3": currentgrades[2], "class4": currentgrades[3], "class5": currentgrades[4], "class6": currentgrades[5], "class7": currentgrades[6]}
return dictionary
def run():
dictionary = getData()
time.sleep(60)
That script runs constantly and does what I want, but then in my other script, I don't know how to just call the dictionary variable. When I use
from getdata.py import dictionary
in the Flask-ask script it just runs the loop and constantly gets the data. I just want the Flask-ask script to take the variable defined in the "run" function and then use it without running any of the actual scripts defined in the getdata script, which have already run and gotten the correct data. If it matters, both scripts are running in Terminal on a MacBook.
Is there any way to do what I'm asking about, or are there any easier workarounds? Any and all help is appreciated!
It sounds like you want to import the function, so you can run it; rather than importing the dictionary.
try deleting the run function and then in your other script
from getdata import getData
Then each time you write getData() it will run your code and get a new up-to-date dictionary.
Is this what you were asking about?
This issue has been resolved.
As for the original question, I didn't figure out how to make it just import the dictionary instead of first running the function to generate the dictionary. Furthermore, I realized there had to be a more practical solution than constantly running a script like that, and even then not getting brand new data.
My solution was to make the script that gets the data start running at the same time as the launch function. Here was the final script for the first intent (the rest of it remained the same):
#ask.intent("start_skill")
def start_skill():
welcome_message = 'What is the password?'
thread = threading.Thread(target=getData, args=())
thread.daemon = True
thread.start()
return question(welcome_message)
def getData():
#script to get data here
#other intents and rest of script here
By design, the skill requested a numeric passcode to make sure I was the one using it before it was willing to read the data (which was probably pointless, but this skill is at least as much for my own educational reasons as for practical reasons, so, for the extra practice, I wanted this to have as many features as I could possibly justify). So, by the time you would actually be able to ask for the data, the script to get the data will have finished running (I have tested this and it seems to work without fail).

Categories