![]() |
Help adding a loop inside a loop - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Help adding a loop inside a loop (/thread-38413.html) |
RE: Help adding a loop inside a loop - Extra - Oct-10-2022 I think I understand your example. It prints the reminder 5sec from the time you started the program. Correct? But I'm still confused on how to make my reminders work where they execute on their set dates & times. When the reminder's date & time == the currentdate & currenTime I want to trigger the reminderPopup(), but how would the scheduler know when the reminder's date/time == the current date/time? import sched import time import sqlite3 from BX_External_Functions import checkForReminders, reminderPopup from BX_Constants import MainDatabase, currentdate, currentTime #Create schedule scheduler = sched.scheduler(time.time, time.sleep) #Read in reminders from database # Connect to the database connection = sqlite3.connect(MainDatabase) cursor = connection.cursor() # Get the Reminder & it's scheduled Date & Time cursor.execute("SELECT Reminder, Date, Time FROM Reminders") Results = cursor.fetchall() connection.commit() connection.close() #For each reminder, add an event to the schedule (enter/enterabs) for row in Results: print(row) scheduler.enterabs() #How would I add the reminder as an event that triggers on it's set date/time? #If a new reminder is created, add it to the database & add an event to the schedule #Inside Baxter's while True loop, call schedule.run(blocking=False) print ('Checking for reminders...') scheduler.run(False) RE: Help adding a loop inside a loop - deanhystad - Oct-10-2022 Did you run my example? What it does is pretty obvious when you run the example. The scheduler knows when to run an event because you pass the scheduled time as one of the arguments to schedule.enter() or schedule.enterabs(). enter(delta) schedules the event to run when the clock reaches current_time + delta. enterabs(abs_time) schedules the event to run when the clock reaches abs_time. You don't need to call cursor.commit() unless you did something that changes the database. SELECT does not change the database. You don't need to use fetchall(). Instead of this: connection = sqlite3.connect(MainDatabase) cursor = connection.cursor() # Get the Reminder & it's scheduled Date & Time cursor.execute("SELECT Reminder, Date, Time FROM Reminders") Results = cursor.fetchall() connection.commit() connection.close() #For each reminder, add an event to the schedule (enter/enterabs) for row in Results: print(row)Do this: connection = sqlite3.connect(MainDatabase) cursor = connection.cursor() # Get the Reminder & it's scheduled Date & Time for row in cursor.execute("SELECT Reminder, Date, Time FROM Reminders") print(row) connection.close()You really don't even need the cursor. connection = sqlite3.connect(MainDatabase) # Get the Reminder & it's scheduled Date & Time for row in connection.execute("SELECT Reminder, Date, Time FROM Reminders"): print(row) connection.close()Why are you saving the schedule time as Date, Time instead of DateTime? I don't know what you have for Date or Time, so in the code below I use a magical function that combines date and time to give you datetime. def send_reminder(message, scheduled_time): """Function called when time to send reminder""" print(f"{scheduled_time}: {message} def schedule_reminder(reminder, date, time): """Schedule a reminder""" scheduled_time = date_and_time_to_datetime(date, time) schedule.enterabs(schedule_time.timestamp(), send_reminder, arguments=(reminder, scheduled_time)) ... #somewhere in the startup portion of the main code # Schedule all the reminders schedule = sched.scheduler(time.time, time.sleep) connection = sqlite3.connect(MainDatabase) for reminder in connection.execute("SELECT Reminder, Date, Time FROM Reminders"): schedule_reminder(*reminder) connection.close() ... # somewhere later in your main code while True: schedule.run(False) # do other stuff RE: Help adding a loop inside a loop - Extra - Oct-10-2022 Thanks, that helped clean up the SQL part of the code, but I'm still confused on the scheduler part of the code. So this function takes in the reminder & it's scheduled date and time, then it calls the send_reminder function but I don't get what the schedule_time.timestamp() does? Quote:def schedule_reminder(reminder, date, time): """Schedule a reminder""" scheduled_time = date_and_time_to_datetime(date, time) schedule.enterabs(schedule_time.timestamp(), send_reminder, arguments=(reminder, scheduled_time)) And this is what I have for getting the current date & time by the way. #-------------------------------------------------- # Current Date & Time #-------------------------------------------------- import datetime currentdate = datetime.date.today() from datetime import datetime timeNow = datetime.now() currentTime = timeNow.strftime("%I:%M %p") #--------------------------------------------------And this is what I have for my reminders:
RE: Help adding a loop inside a loop - deanhystad - Oct-11-2022 scheduler.enterabs() takes a time argument. https://docs.python.org/3/library/sched.html Quote:scheduler.enterabs(time, priority, action, argument=(), kwargs={})I tried using datetime.now as the timefunc, but got an error when calling scheduler.run() I decided to use time.time, as is shown in the docuentation and all examples If you use time.time as the timefunc for the scheduler, the time for the enter() and enterabs() must be compatible with the value returned by time.time(). According to the documentation for time.time():https://docs.python.org/3/library/time.html Quote:time.time() → floatdatetime.timestamp() is an object method that converts a datetime object into an int that is compatible with time.time(). You should really save the reminder time as a datetime object. You can display the information any way you like but store it in a way that makes it easy to use. Having separate date and time columns makes it difficult to do things like sort your reminders by time or delete old reminders. Here I have to recombine the two values to get the datetime object that I need. def schedule_reminder(reminder, date, time): """Schedule a reminder""" dt = datetime.strptime(f"{date} {time}", "%Y-%m-%d %I:%M %p") schedule.enterabs( time=schedule_time.timestamp(), priority=1, # I previously forgot that you must specify a priority action=send_reminder, arguments=(reminder, scheduled_time)) RE: Help adding a loop inside a loop - Extra - Oct-15-2022 First off, I just want to say thanks a lot for your time & patience in helping me to understand this, I really appreciate it. So I got it to run, but there are still a couple of things I'm not sure about. (1) Why do all the reminders popup when they haven't reached their scheduled/set time yet? Ex: If you look at the output, the current time was 11:23 AM but all the reminders popped up even though they were set at 11:25, 11:28, & 11:30. Is there a way to get it so nothing would have popped up until 11:25 (Then only the "Test 1" reminder should pop up because it's scheduled for 11:25, not the rest of them)? (2) How do I get the AM/PM to display? I know the "%p" is supposed to do it in: "%Y-%m-%d %I:%M %p" but it doesn't show.Thanks again. My Output from (Test.py -> The code shown below): The code I have now (Test.py -> Not link to my main code yet):import sched import time import sqlite3 from BX_External_Functions import checkForReminders, reminderPopup from BX_Constants import MainDatabase, currentdate, currentTime #Create schedule scheduler = sched.scheduler(time.time, time.sleep) #Read in reminders from database #For each reminder, add an event to the schedule (enter/enterabs) connection = sqlite3.connect(MainDatabase) for row in connection.execute("SELECT Reminder, DateTime FROM Reminders"): print("Reminders in Database: ", row) connection.close() print("\n") connection = sqlite3.connect(MainDatabase) for row in connection.execute("SELECT DateTime FROM Reminders"): scheduled_time = row print("Scheduled Time: ", scheduled_time) connection.close() import datetime currentdate = datetime.date.today() from datetime import datetime timeNow = datetime.now() currentTime = timeNow.strftime("%I:%M %p") Current_DateTime = datetime.strptime(f"{currentdate} {currentTime}", "%Y-%m-%d %I:%M %p") print("\nCurrent Date Time: ", Current_DateTime ,"\n") # #If a new reminder is created, add it to the database & add an event to the schedule # scheduler.enterabs() # #Inside Baxter's while True loop, call schedule.run(blocking=False) # print ('Checking for reminders...') # scheduler.run(False) def schedule_reminder(reminder): """Schedule a reminder""" scheduler.enterabs( time=Current_DateTime.timestamp(), priority=1, action=send_reminder, argument=(reminder, scheduled_time)) def send_reminder(Reminder, scheduled_time): """Function called when time to send reminder""" print(f"Reminders Popup: {scheduled_time}: {Reminder}") #Read in reminders from database connection = sqlite3.connect(MainDatabase) for reminder in connection.execute("SELECT Reminder FROM Reminders"): schedule_reminder(*reminder) connection.close() # somewhere later in your main code while True: scheduler.run(False) # do other stuffAnd this is how my Reminders Table/Database is set up: def createBXDatabase(): #Create a database (BX_Database.db) connection = sqlite3.connect("BX_Database.db") cursor = connection.cursor() #-------------------------------------------- # Reminders Table #-------------------------------------------- remindersTable = """CREATE TABLE IF NOT EXISTS Reminders (ID INTEGER PRIMARY KEY AUTOINCREMENT, Reminder TEXT, DateTime TEXT, DateAdded datetime default current_timestamp);""" #Execute the creation of the table cursor.execute(remindersTable) #Commit the changes connection.commit() #Close the connection connection.close() RE: Help adding a loop inside a loop - deanhystad - Oct-15-2022 You scheduled all your events for the same time (Current_DateTime.timestamp()). When you print a datetime object it uses the default datetime format. If you want a specific format you need to use datetime.strftime(format_str) with an appropriate format str. import sched import time from datetime import datetime, timedelta time_format = "%I:%M:%S %p" def send_reminder(message): print(f"{datetime.now().strftime(message)}> {message}") def schedule_reminder(scheduled_time): """Schedule a reminder""" schedule.enterabs( time=scheduled_time.timestamp(), priority=1, action=send_reminder, argument=(scheduled_time.strftime(time_format),)) schedule = sched.scheduler(time.time) now = datetime.now() for delay in (0.1, 0.2, 0.5, 1): schedule_reminder(now + timedelta(minutes=delay)) while not schedule.empty(): schedule.run()
RE: Help adding a loop inside a loop - Extra - Oct-15-2022 If I do time=scheduled_time.timestamp(), (like I did in the code below)I get this error because the Scheduled_time is being taken from the SQLite database.Is there a way to get around this error? How do I convert those tuple objects to a datetime object (is that even possible)? connection = sqlite3.connect(MainDatabase) for row in connection.execute("SELECT DateTime FROM Reminders"): scheduled_time = row print("Scheduled Time: ", scheduled_time) connection.close() def schedule_reminder(reminder): """Schedule a reminder""" scheduler.enterabs( time=scheduled_time.timestamp(), priority=1, action=send_reminder, argument=(reminder, scheduled_time)) RE: Help adding a loop inside a loop - deanhystad - Oct-15-2022 After playing with this a bit, I don't think datetime is the best way to save a datetime object. Following my advice that save values in their most useful format, I wrote an example that saves time as a timestamp. The scheduler uses timestamps. The databases can store timestamps, but DateTime objects have to be converted to and from strings. And using DateTime forces two applications (the one that saves the events and the one that sends the events) to agree on DateTime format used. This example writes reminders to a database, retrieves them from the database, schedules the reminders, and sends the reminders. The main change needed to make this work with your code is that your events are entered using a datetime string. You'll need to convert that to a datetime object, and then use the datetime object to get a timestamp. import sqlite3 as sql from datetime import datetime import sched, time time_format = "%I:%M:%S %p" def send_reminder(sched_time, message): """Just pring the message and the scheduled time (and current time)""" print(message) print(datetime.now().strftime(time_format)) print(datetime.fromtimestamp(sched_time).strftime(time_format)) def save_reminders(reminders): """Make a table in the database to hold the reminders""" now = int(time.time()) db = sql.connect("test.db") db.execute("CREATE TABLE IF NOT EXISTS reminders(message TEXT, time INTEGER)") db.execute("DELETE FROM reminders") for reminder, seconds in reminders: db.execute( "INSERT INTO reminders (message, time) VALUES(?, ?)", (reminder, now + seconds)) db.commit() db.close() def schedule_reminders(schedule): """Get reminders from the database and add to the schedule.""" db = sql.connect("test.db") for reminder in db.execute("SELECT * FROM reminders"): message, sched_time = reminder schedule.enterabs(sched_time, 1, send_reminder, (sched_time, message)) db.close() # Make a database of reminders that run 5, 10 and 15 seconds from now. save_reminders(( ("This is the first message", 5), ("This message is 5 seconds after the first", 10), ("This message is 15 seconds after the first", 20)) ) schedule = sched.scheduler(time.time) schedule_reminders(schedule) while not schedule.empty(): schedule.run() RE: Help adding a loop inside a loop - Extra - Oct-15-2022 I tried converting the DateTime String from the Reminders Table to a DateTime object by doing this: connection = sqlite3.connect(MainDatabase) for row in connection.execute("SELECT DateTime FROM Reminders"): scheduled_time = row print("Scheduled Time: ", scheduled_time) #Convert scheduled_time from Tuple to List from itertools import chain DateTimeList = list(chain.from_iterable(scheduled_time)) #Then Convert the List to a String DateTimeStr = "".join(str(x) for x in DateTimeList) print("Date Time String:", DateTimeStr) format = "%Y-%m-%d %I:%M %p" scheduled_time2 = datetime.strptime(DateTimeStr, format) connection.close()But I get this error: Is that not the right format? It's year-month-date hour-minute Am/PmWhat is the proper format for this? RE: Help adding a loop inside a loop - Extra - Oct-16-2022 Edit: Did this: format = "%Y-%m-%d %H:%M:%S" scheduled_time2 = datetime.strptime(DateTimeStr, format)No format error but I get this output: Which is weird because the Reminder Popup says all 4 reminders are scheduled for 8:19 but they're not (only Test 4 was)import sched import time import sqlite3 from tokenize import Double from BX_External_Functions import checkForReminders, reminderPopup from BX_Constants import MainDatabase, currentdate, currentTime from datetime import datetime #Create schedule scheduler = sched.scheduler(time.time, time.sleep) #Read in reminders from database #For each reminder, add an event to the schedule (enter/enterabs) connection = sqlite3.connect(MainDatabase) for row in connection.execute("SELECT Reminder, DateTime FROM Reminders"): print("Reminders in Database: ", row) connection.close() print("\n") connection = sqlite3.connect(MainDatabase) for row in connection.execute("SELECT DateTime FROM Reminders"): scheduled_time = row print("Scheduled Time: ", scheduled_time) #---------- #Convert scheduled_time from Tuple to List from itertools import chain DateTimeList = list(chain.from_iterable(scheduled_time)) #Then Convert the List to a String DateTimeStr = "".join(str(x) for x in DateTimeList) print("DateTime String:", DateTimeStr) format = "%Y-%m-%d %H:%M:%S" scheduled_time2 = datetime.strptime(DateTimeStr, format) #---------- connection.close() import datetime currentdate = datetime.date.today() from datetime import datetime timeNow = datetime.now() currentTime = timeNow.strftime("%I:%M %p") Current_DateTime = datetime.strptime(f"{currentdate} {currentTime}", "%Y-%m-%d %I:%M %p") print("\nCurrent Date Time: ", Current_DateTime ,"\n") # #If a new reminder is created, add it to the database & add an event to the schedule # scheduler.enterabs() # #Inside Baxter's while True loop, call schedule.run(blocking=False) # print ('Checking for reminders...') # scheduler.run(False) def schedule_reminder(reminder): """Schedule a reminder""" scheduler.enterabs( time=scheduled_time2.timestamp(), priority=1, action=send_reminder, argument=(reminder, scheduled_time)) def send_reminder(Reminder, scheduled_time): """Function called when time to send reminder""" print(f"Reminders Popup: {scheduled_time}: {Reminder}") #Read in reminders from database connection = sqlite3.connect(MainDatabase) for reminder in connection.execute("SELECT Reminder FROM Reminders"): schedule_reminder(*reminder) connection.close() # somewhere later in your main code while True: scheduler.run(False) # do other stuff |