Python Forum
Help adding a loop inside a loop
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help adding a loop inside a loop
#21
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()
Reply
#22
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.
Output:
#New Reminder I scheduled for 1:00 PM 01:00 PM 2022-10-16 13:00:00 Reminders Popup: 2022-10-16 12:46:00: Test 4 Reminders Popup: 2022-10-16 12:46:00: T Reminders Popup: 2022-10-16 12:52:00: Test 4 Reminders Popup: 2022-10-16 12:52:00: Test 5 Reminders Popup: 2022-10-16 12:54:00: Test 6 Reminders Popup: 2022-10-16 12:54:00: Test 7 Reminders Popup: 2022-10-16 12:59:00: Test C Reminders Popup: 2022-10-16 13:00:00: Test B Reminders Popup: 2022-10-16 13:00:00: Test no Reminders Popup: 2022-10-16 13:00:00: Test 8 Reminders Popup: 2022-10-16 13:00:00: Test 3 Reminders Popup: 2022-10-16 13:00:00: Test A Reminders Popup: 2022-10-16 13:00:00: Test 1 Reminders Popup: 2022-10-16 13:00:00: Test D Reminders Popup: 2022-10-16 13:00:00: Test 9
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:
Error:
connection.execute("DELETE FROM reminders WHERE DateTimeStamp = ?",(scheduled_time)) ValueError: parameters are of unsupported type
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()
Reply
#23
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))
Reply
#24
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)
#------------------------------------------------------------------------------------------
Reply
#25
You fixed one of the two errors.
Reply
#26
What was the other error?
The Statement works because I tested it, and it deleted the reminder.
What could the other error be?
Reply
#27
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?
Reply
#28
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)
Reply
#29
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.
Reply
#30
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??
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  problem program runs in a loop jasserin 0 159 May-18-2024, 03:07 PM
Last Post: jasserin
  [SOLVED] Loop through directories and files one level down? Winfried 3 365 Apr-28-2024, 02:31 PM
Last Post: Gribouillis
  Loop through all files in a directory? Winfried 10 723 Apr-23-2024, 07:38 PM
Last Post: FortuneCoins
  for loop not executing Abendrot47 2 334 Apr-09-2024, 07:14 PM
Last Post: deanhystad
  Re Try loop for "net use..." failures tester_V 10 751 Mar-02-2024, 08:15 AM
Last Post: tester_V
  File loop curiously skipping files - FIXED mbk34 10 1,020 Feb-10-2024, 07:08 AM
Last Post: buran
  Optimise multiply for loop in Python KenBCN 4 573 Feb-06-2024, 06:48 PM
Last Post: Gribouillis
  Basic binary search algorithm - using a while loop Drone4four 1 460 Jan-22-2024, 06:34 PM
Last Post: deanhystad
  loop through csv format from weburl in python maddyad82 3 524 Jan-17-2024, 10:08 PM
Last Post: deanhystad
  Variable definitions inside loop / could be better? gugarciap 2 530 Jan-09-2024, 11:11 PM
Last Post: deanhystad

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020