![]() |
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 - deanhystad - Oct-16-2022 You have two loops that read events from the database. One to get the reminder message and one to get the reminder time. You should GET the reminders message and scheduled times at the same time, just like I did in my example. One loop, not two. Using two loops is messing you up. First you get all your times, then you schedule your reminders. Because of this you schedule all your events to use the same time. The last time retrieved from the database. Why are you doing this? #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)Do you understand any of that code? This is the most convoluted scheme I've ever seen to get a value from a tuple. I don't understand your confusion about tuples. A tuple is just like a list, except it is immutable. If you want to get the value from the tuple, use indexing (scheduled_time[0]) or unpacking (*scheduled_time), just like you would do to get values from a list. When you fix your program to retrieve the reminder message and scheduled time together with a single query, unpacking will be the best choice. for message, sched_time in db.execute("SELECT Reminder, DateTime FROM Reminders"): schedule_reminder(message, sched_time)I no longer think Datetime is the right type for saving schedule time in the database. You need timestamps for scheduling and sqlite allows saving timestamps in their native format, unlike DateTime that must be converted to/from a string. Using timestamps for the schedule time also makes it possible to use queries to delete old reminders, or only retrieve reminders that occur during a specific time span. That would be a nice feature if you want to display a calendar of events. The only disadvantage of timestamps is they are not human readable. To input a schedule time you'll need to convert a string to a timestamp (strptime() to convert string to DateTime, timestamp() to convert DateTime to timestamp(). To display a schedule time you'll need to convert a timestamp to a string (.fromtimestamp() to convert timestamp to DateTime, strftime() to convert DateTime to string). import sqlite3 as sql from datetime import datetime import sched, time time_format = "%I:%M %p" def send_reminder(sched_time, message): """Print time> Message and scheduled time""" sched_time = datetime.fromtimestamp(sched_time).strftime(time_format) now = datetime.now().strftime(time_format) print(f"{now}> {message}\nScheduled: {sched_time}\n") def enter_reminders(): """Generate a list of reminders. End by entering a blank message""" while True: reminder = input("Message: ") if not reminder: break sched_time = datetime.strptime(input("HH:MM AM/PM: "), time_format) sched_time = datetime.now().replace( hour=sched_time.hour, minute=sched_time.minute, second=0) yield sched_time.timestamp(), reminder def save_reminders(reminders): """Make a table in the database to hold the reminders Table reminders Time a timestamp, Reminder a string """ db = sql.connect("test.db") db.execute("CREATE TABLE IF NOT EXISTS reminders(Time INTEGER, Reminder TEXT)") db.execute("DELETE FROM reminders") # For demonstration purposes only for reminder in reminders: db.execute( "INSERT INTO reminders (Time, Reminder) VALUES(?, ?)", reminder) db.commit() db.close() def schedule_reminders(schedule): """Get reminders from the database and add to the schedule.""" db = sql.connect("test.db") for sched_time, reminder in db.execute("SELECT Time, Reminder FROM reminders"): schedule.enterabs(sched_time, 1, send_reminder, (sched_time, reminder)) db.close() print("Enter reminders") save_reminders(enter_reminders()) print("\nScheduling reminders:") schedule = sched.scheduler(time.time) schedule_reminders(schedule) print("\nPlay reminders.") while not schedule.empty(): schedule.run() RE: Help adding a loop inside a loop - Extra - Oct-16-2022 Thanks for your help. I finally got it to work properly! I scheduled a couple test reminders and they all popped up at their scheduled time. Now all that's left to do is delete the old reminders, clean up the code, and test it/tie it in to my main code. This is my code (I used npyscreen as a GUI to set my reminders): # Working Test Version import npyscreen import sqlite3 from BX_Constants import (MainDatabase) import sched import time from datetime import datetime #Create schedule scheduler = sched.scheduler(time.time, time.sleep) class SetReminderTUI(npyscreen.NPSApp): def main(self): frame = npyscreen.Form(name = "Set Reminder",) Reminder = frame.add(npyscreen.TitleText, name = "Reminder:",) date = frame.add(npyscreen.TitleDateCombo, name = "Date:") AmPm = frame.add(npyscreen.TitleText, name = "AM/PM:",) time = frame.add(npyscreen.TitleSelectOne, max_height=5, value = [1,], name="Choose a Time", values = ["12:00","12:30","12:59","01:00","01:30" ,"02:00","02:30","03:00","03:30","04:00","04:30","05:00" ,"05:30","06:00","06:30","07:00","07:30" ,"08:00","08:30", "09:00","09:30","10:00","10:30" ,"11:00","11:30"], scroll_exit=True) # This lets the user interact with the Form. frame.edit() Reminder = Reminder.value SelectedTime = time.get_selected_objects() AmPm = AmPm.value Date = date.value #Convert SelectedTime from Tuple to List from itertools import chain SelectedTimeLst = list(chain.from_iterable(SelectedTime)) #Then Convert the List to a String SelectedTimeStr = "".join(str(x) for x in SelectedTimeLst) Time = SelectedTimeStr +" "+ AmPm from datetime import datetime dateTime = datetime.strptime(f"{Date} {Time}", "%Y-%m-%d %I:%M %p") print(Time) print(dateTime) #Save the Datetime as a timestamp in the database Scheduled_Time = dateTime.timestamp() #------------------------------------------ # Add Reminder to Database #------------------------------------------ #Connect to the database connection = sqlite3.connect(MainDatabase) cursor = connection.cursor() #Add the reminder to the reminders table cursor.execute("INSERT into Reminders (Reminder, DateTime) VALUES(?,?)",(Reminder,Scheduled_Time)) connection.commit() connection.close() #Close the connection #------------------------------------------ App = SetReminderTUI() App.run() def schedule_reminder(schedule): """Schedule a reminder""" connection = sqlite3.connect(MainDatabase) for reminder in connection.execute("SELECT Reminder, DateTime FROM reminders"): message, sched_time = reminder scheduler.enterabs(sched_time, 1, send_reminder, (message, sched_time)) connection.close() def send_reminder(Reminder, scheduled_time): """Function called when time to send reminder""" scheduled_timeStr = datetime .fromtimestamp(scheduled_time) print(f"Reminders Popup: {scheduled_timeStr}: {Reminder}") schedule_reminder(scheduler) while not scheduler.empty(): scheduler.run(False)Edit: How would I go about deleting old reminders? Tried the code below but I get this error: Even through my DateTimeStamp is stored as a REAL value (Float) in my Database, and scheduled_time is also a float#Display the Reminder def send_reminder(Reminder, scheduled_time): """Function called when time to send reminder""" #Convert the scheduled_time.timestamp() to a readable DateTime String scheduled_timeStr = datetime .fromtimestamp(scheduled_time) reminderPopup(Reminder) print(f"Reminders Popup: {scheduled_timeStr}: {Reminder}") #Delete reminder after it's been displayed connection = sqlite3.connect(MainDatabase) #Connect to Database connection.execute("DELETE FROM reminders WHERE DateTimeStamp = ?",(scheduled_time)) connection.close() RE: Help adding a loop inside a loop - deanhystad - Oct-17-2022 Your error has nothing to do with floats or REAL. Look carefully at the query. It contains two errors. connection.execute("DELETE FROM reminders WHERE DateTimeStamp = ?",(scheduled_time)) RE: Help adding a loop inside a loop - Extra - Oct-17-2022 Got it, thanks. connection.execute("DELETE FROM reminders WHERE DateTimeStamp = ?", (Scheduled_Time,)) connection.commit()One last thing tough; I'm having a bit of trouble implementing the scheduler into my main while true loop. When Main() is called, I created the scheduler and then it should run BAXTER() with schedule_reminder(scheduler) running in the background, not affecting BAXTER(). The problem I have is that the scheduler blocks BAXTER() and I can't run it properly. #------------------------------------------------------------------------------------------ # Run The Program #------------------------------------------------------------------------------------------ def Main(): #Create Schedule scheduler = sched.scheduler(time.time, time.sleep) StartupText() wishMe() speak("How may I be of service?") while True: #Run Reminders schedule_reminder(scheduler) BAXTER() while not scheduler.empty(): scheduler.run(False) #------------------------------------------------------------------------------------------How do I orient that portion of the code so I can run BAXTER() as intended with out the scheduler interfering/blocking it (Do I need threading??). Thanks again. Full Code: #------------------------------------------------------------------------------------- # Imports #------------------------------------------------------------------------------------- import json import random import datetime import operator import os import subprocess import time import sys import sched import webbrowser import requests from bs4 import BeautifulSoup import wikipedia import wolframalpha from BX_Intents import (greetings, farewell, thanks, noAnswer, youChoose) from BX_External_Functions import (autoTypeAnimation, StartupText, ShutdownText, UserInput, listen, speak, getRandomJoke, getFunFacts, setReminders, setTodo, terminateTimers, sendEmail, wishMe, setRenewal, ErrorMsg, offlineWarningMsg, schedule_reminder) # Print a warning msg if there is no internet to prevent pywhatkit # from crashing the program due to no connection try: import pywhatkit except: offlineWarningMsg() #------------------------------------------------------------------------------------- #------------------------------------------------------------------------------------- # Main #------------------------------------------------------------------------------------- def BAXTER(): command = UserInput() #Take user's input from terminal command=str(command).lower() #Convert user's input to lowercase #------------------------------------------------------------------------------------- # General Conversation (From Intents.py) #------------------------------------------------------------------------------------- #Greetings patterns, responses = greetings() if (command in patterns): results = (random.choice(responses)) autoTypeAnimation(results) speak(results) #Farewell patterns, responses = farewell() if (command in patterns): results = (random.choice(responses)) autoTypeAnimation(results) speak(results) #Thanks patterns, responses = thanks() if (command in patterns): results = (random.choice(responses)) autoTypeAnimation(results) speak(results) #No Response patterns, responses = noAnswer() if (command in patterns): results = (random.choice(responses)) autoTypeAnimation(results) speak(results) #------------------------- # Tell a Joke #------------------------- if ('joke' in command): try: joke = getRandomJoke() autoTypeAnimation(joke) speak(joke) except: ErrorMsg("get", "jokes") #------------------------- #------------------------- # Tell a Fun Fact #------------------------- if ('fact' in command): try: funFact = getFunFacts() autoTypeAnimation(funFact) speak(funFact) except: ErrorMsg("get", "fun facts") #------------------------- #------------------------------------------------------------------------------------- # Search Wikipedia (General Info) #------------------------------------------------------------------------------------- if ('weather' not in command): if ('who is' in command) or ('what is the' in command) or ('what is a' in command) or ("what is" in command): if ('time' not in command): if ('news' not in command): autoTypeAnimation('Searching Wikipedia...') speak('Searching...') command = command.replace("who is","") command = command.replace("what is the","") command = command.replace("what is a","") command = command.replace("what is","") try: results = wikipedia.summary(command, sentences = 2) autoTypeAnimation(results) speak(results) except: ErrorMsg("connect to", "Wikipedia") #------------------------------------------------------------------------------------- # Search Wolfram Alpha (Math/Conversions, Definitions) #------------------------------------------------------------------------------------- if ('news' not in command): if ('weather' in command) or ('calculate' in command) or ("what's" in command) or ('define' in command) or ("what" in command): autoTypeAnimation('Searching Wolfram Alpha...') speak('Searching...') command = command.replace("calculate","") command = command.replace("what's","") command = command.replace("define","") # Wolframalpha App Id appId = 'JH9XHR-W9J76L7H5A' try: # Wolfram Instance client = wolframalpha.Client(appId) res = client.query(''.join(command)) results = next(res.results).text autoTypeAnimation(results) speak(results) except: ErrorMsg("connect to", "Wolfram Alpha database") #------------------------------------------------------------------------------------- # Open Stuff on the Internet #------------------------------------------------------------------------------------- #Open Youtube Videos (Ex: 'Play __ on youtube') if ('youtube' in command): autoTypeAnimation("Launching Youtube...") speak('Launching Youtube') command = command.replace("youtube","") try: pywhatkit.playonyt(command) except: ErrorMsg("connect to", "Youtube") #Open Google Maps and Find The Location of A You Want if ('where is' in command): command = command.replace("where is","") autoTypeAnimation("Locating" + command + "...") speak('Locating' + command) webbrowser.open_new_tab("https://www.google.com/maps/place/" + command) #Search Stuff on Google if ('search' in command): command = command.replace("search", "") autoTypeAnimation("Searching" + command + " on Google") speak('Searching' + command) try: pywhatkit.search(command) except: ErrorMsg("connect to" , "Google") #Close Firefox if ('close firefox' in command): autoTypeAnimation("Terminating Firefox...") speak('Closing Firefox') command = command.replace("close firefox", "") browser = "firefox.exe" try: os.system("taskkill /f /im " + browser) except: ErrorMsg("close", "Firefox") #------------------------------------------------------------------------------------- # Open Stuff on the Computer #------------------------------------------------------------------------------------- #Open Windows Media Player and Auto Play the Playlist Called Music if ('play music' in command) or ('media player' in command) or ('drop the needle' in command): autoTypeAnimation("Launching music...") speak("Launching Music") command = command.replace("play music", "") command = command.replace("media player", "") command = command.replace("drop the needle", "") try: subprocess.Popen("C:\Program Files (x86)\Windows Media Player\wmplayer.exe /Playlist Music") except: ErrorMsg("open","Windows Media Player") #Close Windows Media Player if ('stop music' in command): autoTypeAnimation("Terminating music...") speak('Closing Music') command = command.replace("stop music", "") mediaPlayer = "wmplayer.exe" try: os.system("taskkill /f /im " + mediaPlayer) except: ErrorMsg("close", "Windows Media Player") #------------------------------------------------------------------------------------- # Set Reminders & Renewals #------------------------------------------------------------------------------------- if ('remind me' in command) or ('reminder' in command) or ('renew' in command): command = command.replace("remind me to", "") #If renew is mentioned in the command call the setRenewal Function if ('renew' in command): setRenewal() #Else, call the setReminders Function else: setReminders() #Call setReminders() from External Functions #------------------------------------------------------------------------------------- # Set ToDo #------------------------------------------------------------------------------------- if ('todo' in command): command = command.replace("add", "") command = command.replace("to the todo list", "") setTodo(command) #Call setTodo() from External Functions #------------------------------------------------------------------------------------- # Send E-Mails #------------------------------------------------------------------------------------- if ('email' in command): command = command.replace("email", "") sendEmail() #Call send E-Mail function fro External Functions #------------------------------------------------------------------------------------- # Stop Program/Script Command #------------------------------------------------------------------------------------- if ('stop' in command) or ('shutdown' in command) or ('quit' in command): speak("Shutting Down...") results = "Terminating program..." autoTypeAnimation(results) ShutdownText() terminateTimers() exit() #------------------------------------------------------------------------------------- #------------------------------------------------------------------------------------- #------------------------------------------------------------------------------------------ # Run The Program #------------------------------------------------------------------------------------------ def Main(): #Create Schedule scheduler = sched.scheduler(time.time, time.sleep) StartupText() wishMe() speak("How may I be of service?") while True: #Run Reminders schedule_reminder(scheduler) BAXTER() while not scheduler.empty(): scheduler.run(False) #------------------------------------------------------------------------------------------ RE: Help adding a loop inside a loop - deanhystad - Oct-17-2022 You fixed one of the two errors. RE: Help adding a loop inside a loop - Extra - Oct-17-2022 What was the other error? The Statement works because I tested it, and it deleted the reminder. What could the other error be? RE: Help adding a loop inside a loop - deanhystad - Oct-18-2022 Don't see how it could work. When you INSERT or SELECT the column is DateTime. When you DELETE the column is DateTimeStamp. Which is correct? RE: Help adding a loop inside a loop - deanhystad - Oct-18-2022 The code I post here are examples meant to demonstrate how to use a tool. The last example created a reminder database then immediately pulled all the reminders out of the database and scheduled them. Does that make any sense? Then it ran the schedule in a loop until the reminder schedule was empty. Does that make any sense if what you want is to have the scedule run without blocking the main loop that will run BAXTER? The example is a great demo because it runs all by itself and you can see how everything works in a couple of minutes, but it is a poor template for how you should use scheduler. Use the example to UNDERSTAND how the tools are being used. When you understand the tools, then you can apply them to solving your problem while True: #Run Reminders schedule_reminder(scheduler) # Does this belong in a loop? BAXTER() while not scheduler.empty(): # Do you want this loop? scheduler.run(False) RE: Help adding a loop inside a loop - deanhystad - Oct-18-2022 You don't want to do this either: #Convert SelectedTime from Tuple to List from itertools import chain SelectedTimeLst = list(chain.from_iterable(SelectedTime)) #Then Convert the List to a String SelectedTimeStr = "".join(str(x) for x in SelectedTimeLst)Instead use: SelectedTimeStr = SelectedTime[0]Using chain and join is silly. list(chain.from_iterable(("10:30",))) returns ["1", "0", ":", "3", "0"] and joining that returns "10:30". You do a bunch of extra operations just to get the value you started with. You also need to modify SetReminderTUI.main() to schedule your new reminder. You schedule the reminder in case it occurs during the current session. You save the reminder in the database in case it doesn't. RE: Help adding a loop inside a loop - Extra - Oct-19-2022 I'm still confused on how to run the scheduler with my main code without blocking it. I tried this, but it only triggers after I've entered a command, not when the code is idle (when I don't enter any commands) #------------------------------------------------------------------------------------------ # Run The Program #------------------------------------------------------------------------------------------ def Main(): scheduler = sched.scheduler(time.time, time.sleep) #Create Schedule schedule_reminder(scheduler) #Run Reminders StartupText() wishMe() speak("How may I be of service?") while True: scheduler.run(False) BAXTER() #------------------------------------------------------------------------------------------How would I get it to work/trigger regardless if the code is "idling" or not?? |