SQLite3 Database Errors

Hello everyone,

I wanted to ask a brief question regarding SQLite databases. I have the following interface file:

import datetime
import questionary
from db import HabitDB
from habit import Habit


def main():
    """This is the main entry point of the program"""
    while True:
        choices = [
            {'name': 'Create a habit', 'value': create_habit},
            {'name': 'List habits', 'value': list_habits},
            {'name': 'Mark habit complete', 'value': mark_complete},
            {'name': 'Mark habit incomplete', 'value': mark_incomplete},
            {'name': 'Delete a habit', 'value': delete_habit},
            {'name': 'Help', 'value': help},
            {'name': 'List commands', 'value': list_commands},
            {'name': 'Get streak', 'value': get_streak},
            {'name': 'Exit', 'value': exit}
        ]
        choice = questionary.select('What would you like to do?', choices=choices).ask()

        if choice:
            choice()

def create_habit():
    """This function creates a habit"""
    name = questionary.text('Enter the name of the habit:').ask()
    frequency = questionary.select('How often should this habit be completed?', choices=['daily', 'weekly', 'monthly']).ask()
    completed = False
    habit = HabitDB(name, frequency)
    habit.save()
    print(f'Habit "{name}" with frequency "{frequency}" created successfully!')

def list_habits():
    """This function lists all habits"""
    habit = HabitDB()
    habits = habit.list_all()
    for habit in habits:
        print(f'{habit.name} ({habit.frequency} days) - {"Complete" if habit.completed else "Incomplete"}')

def mark_complete():
    """This function marks a habit as complete"""
    habit = HabitDB()
    habits = habit.list_all()
    habit_choices = [{'name': habit.name, 'value': habit} for habit in habits]
    habit = questionary.select('Which habit would you like to mark as complete?', choices=habit_choices).ask()
    habit.mark_complete()
    print(f'Habit "{habit.name}" marked as complete!')

def mark_incomplete():
    """This function marks a habit as incomplete"""
    habits = habit.list_all()
    habit_choices = [{'name': habit.name, 'value': habit} for habit in habits]
    habit = questionary.select('Which habit would you like to mark as incomplete?', choices=habit_choices).ask()
    habit.mark_incomplete()
    print(f'Habit "{habit.name}" marked as incomplete!')

def delete_habit():
    """This function deletes a habit"""
    habits = habit.list_all()
    habit_choices = [{'name': habit.name, 'value': habit} for habit in habits]
    habit = questionary.select('Which habit would you like to delete?', choices=habit_choices).ask()
    habit.delete()
    print(f'Habit "{habit.name}" deleted successfully!')
    
def help():
    """This function displays the help menu"""
    print('''
    This is a habit tracker app. You can use it to create habits, mark them as complete, and delete them.
    ''')
    
def list_commands():
    """This function lists all commands"""
    print('''
    create_habit - Create a habit
    list_habits - List all habits
    mark_complete - Mark a habit as complete
    mark_incomplete - Mark a habit as incomplete
    delete_habit - Delete a habit
    help - Display the help menu
    list_commands - List all commands
    exit - Exit the app
    ''')
    
def get_streak(habit):
    """This function gets the streak of a habit"""
    streak = 0
    for day in range(1, 31):
        date = datetime.date.today() - datetime.timedelta(days=day)
        if date in habit.completed:
            streak += 1
        else:
            break
    return streak

if __name__ == '__main__':
    main()

When I run it, I get the following error:

Traceback (most recent call last):
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/main.py", line 98, in <module>
    main()
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/main.py", line 24, in main
    choice()
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/main.py", line 37, in list_habits
    habit = HabitDB()
TypeError: __init__() missing 2 required positional arguments: 'name' and 'frequency'```

I know that I am missing two positional arguments, however those arguments are supposed to be provided by the Questionary choices. Any ideas on how I may fix this issue? Thanks for any advice!

Hello, I think that I may see your problem. You are trying to run a typical className.__init__(), where you have to include parameters. But, the names of your required parameters appear to be name and frequency. And in their place, you have put your own varables called name and frequency.

That may be confusing the Python interpreter (it may think that you’re trying to do something like name=variableName, but without the variableName), so I would try this fix:

Replace create_habit() with this:

def create_habit():
    """This function creates a habit"""
    nameVar = questionary.text('Enter the name of the habit:').ask()
    frequencyVar = questionary.select('How often should this habit be completed?', choices=['daily', 'weekly', 'monthly']).ask()
    completed = False
    habit = HabitDB(name=nameVar, frequency=frequencyVar)
    habit.save()
    print(f'Habit "{nameVar}" with frequency "{frequencyVar}" created successfully!')

Feel free to edit the variables nameVar and frequencyVar.

Hope this helps!

Thanks for your solution. I implemented your changes and got the following errors:

Exception ignored in: <function HabitDB.__del__ at 0x7fa4c1039820>
Traceback (most recent call last):
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/db.py", line 48, in __del__
    self.conn.close()
AttributeError: 'HabitDB' object has no attribute 'conn'
Traceback (most recent call last):
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/main.py", line 98, in <module>
    main()
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/main.py", line 24, in main
    choice()
  File "/home/brandon/IU International University/Object-Oriented and Functional Programing with Python/Habit Tracker v3/main.py", line 31, in create_habit
    habit = HabitDB(name, frequency)
TypeError: __init__() takes 2 positional arguments but 3 were given

This is based off my db.py file which looks like this:

import sqlite3
from datetime import datetime, timedelta


class Habit:
    def __init__(self, name="", frequency="", completed=[]):
        self.name = name
        self.frequency = frequency
        self.completed = []

    def mark_complete(self):
        today = datetime.today().date()
        self.completed.append(today)

    def is_complete(self):
        if self.frequency == 'daily':
            return datetime.today().date() in self.completed
        elif self.frequency == 'weekly':
            start_of_week = (datetime.today().date() - timedelta(days=datetime.today().date().weekday()))
            end_of_week = start_of_week + timedelta(days=6)
            for day in range((end_of_week - start_of_week).days + 1):
                date = start_of_week + timedelta(days=day)
                if date in self.completed:
                    return True
            return False
        elif self.frequency == 'monthly':
            today = datetime.today().date()
            if today.day >= 28:
                end_of_month = today.replace(day=28) + timedelta(days=4)
                for day in range((end_of_month - today).days + 1):
                    date = today + timedelta(days=day)
                    if date in self.completed:
                        return True
                return False
            else:
                return today.replace(day=1) in self.completed


class HabitDB:
    def __init__(self, db_file):
        self.conn = sqlite3.connect(db_file)
        self.cursor = self.conn.cursor()

        self.cursor.execute('''CREATE TABLE IF NOT EXISTS habits
                          (name TEXT PRIMARY KEY, frequency TEXT, completed TEXT)''')

    def __del__(self):
        self.conn.close()

    def add_habit(self, habit):
        self.cursor.execute('INSERT INTO habits (name, frequency, completed) VALUES (?, ?, ?)',
                            (habit.name, habit.frequency, str(habit.completed)))
        self.conn.commit()

    def delete_habit(self, habit_name):
        self.cursor.execute('DELETE FROM habits WHERE name = ?', (habit_name,))
        self.conn.commit()

    def update_habit(self, habit):
        self.cursor.execute('UPDATE habits SET completed = ? WHERE name = ?', (str(habit.completed), habit.name))
        self.conn.commit()

    def get_all_habits(self):
        self.cursor.execute('SELECT name, frequency, completed FROM habits')
        rows = self.cursor.fetchall()
        habits = []
        for row in rows:
            habit = Habit(row[0], row[1])
            habit.completed = [datetime.date.fromisoformat(date_str) for date_str in row[2].split(',')]
            habits.append(habit)
        return habits

    def list_by_frequency(self, frequency):
        self.cursor.execute('SELECT name FROM habits WHERE frequency = ?', (frequency,))
        return [row[0] for row in self.cursor.fetchall()]

    def get_streak(self, habit_name):
        streak = 0
        today = datetime.today().date()
        for day in range((today - timedelta(days=30)).days, (today - timedelta(days=1)).days + 1):
            date = today - timedelta(days=day)
            habit = self.get_habit(habit_name)
            if habit and date in habit.completed:
                streak += 1
            else:
                break
        return streak

    def get_habit(self, habit_name):
        self.cursor.execute('SELECT name, frequency, completed FROM habits WHERE name = ?', (habit_name,))
        row = self.cursor.fetchone()
        if row is None:
            return
        
    def save(self):
        self.conn.commit()
        
    def is_complete(self, habit_name):
        habit = self.get_habit(habit_name)
        if habit:
            return habit.is_complete()
        return False

This database integration has plagued me for a while now with little errors that keep popping up everytime I try to fix bugs. I really do appreciate your help!

Hello,

I may have a fix for nearly all of the issues with your code. But in your main Python file, I don’t really know what you were trying to do with your Get Streaks function, so you’ll have to fix that one by yourself. For the rest of it, I believe that I undersatand what you’re trying to do, and I’ve fixed it (both of the programs).

main.py

import db
import questionary

def main():
    """This is the main entry point of the program"""
    while True:
        choices = [
            {'name': 'Create a habit', 'value': create_habit},
            {'name': 'List habits', 'value': list_habits},
            {'name': 'Mark habit complete', 'value': mark_complete},
            {'name': 'Mark habit incomplete', 'value': mark_incomplete},
            {'name': 'Delete a habit', 'value': delete_habit},
            {'name': 'Help', 'value': help},
            {'name': 'List commands', 'value': list_commands},
            {'name': 'Get streak', 'value': get_streak},
            {'name': 'Exit', 'value': exit}
        ]
        choice = questionary.select('What would you like to do?', choices=choices).ask()

        if choice:
            choice()

def create_habit():
    """This function creates a habit"""
    nameVar = questionary.text('Enter the name of the habit:').ask()
    frequencyVar = questionary.select('How often should this habit be completed?', choices=['daily', 'weekly', 'monthly']).ask()
    new_habit=db.create_habit(name=nameVar,frequency=frequencyVar)
    print(f'Habit "{nameVar}" with frequency "{frequencyVar}" created successfully!')

def list_habits():
    habits=db.list_habits()
    for habit in habits:
        # in habit, 1st value is the habit name
        # 2nd value is a string showing if the habit is complete
        #
        #please note that Python has some problems with the nested dictionaries, which is why the `habits[habit]["key"]` thing is needed
        print(f'{habits[habit]["name"]} ({habits[habit]["frequency"]}) - {"Complete" if habits[habit]["completed_bool"] else "Incomplete"}')

def mark_complete():
    """This function marks a habit as complete"""
    habits = db.list_habits()
    #please note that Python has some problems with the nested dictionaries, which is why the `habits[habit]["key"]` thing is needed
    habit_choices = [{'name': habits[habit]["name"], 'value': habits[habit]["name"]} for habit in habits]
    habit = questionary.select('Which habit would you like to mark as complete?', choices=habit_choices).ask()
    db.Habit(name=habit).mark_complete()
    print(f'Habit "{habit}" marked as complete!')

def mark_incomplete():
    """This function marks a habit as incomplete"""
    habits = db.list_habits()
    #please note that Python has some problems with the nested dictionaries, which is why the `habits[habit]["key"]` thing is needed
    habit_choices = [{'name': habits[habit]["name"], 'value': habits[habit]["name"]} for habit in habits]
    habit = questionary.select('Which habit would you like to mark as incomplete?', choices=habit_choices).ask()
    db.Habit(name=habit).mark_incomplete()
    print(f'Habit "{habit}" marked as complete!')

def delete_habit():
    """This function deletes a habit"""
    habits = db.list_habits()
    #please note that Python has some problems with the nested dictionaries, which is why the `habits[habit]["key"]` thing is needed
    habit_choices = [{'name': habits[habit]["name"], 'value': habits[habit]["name"]} for habit in habits]
    habit = questionary.select('Which habit would you like to delete?', choices=habit_choices).ask()
    db.Habit(name=habit).delete_habit()
    print(f'Habit "{habit}" deleted successfully!')

def help():
    """This function displays the help menu"""
    print('''
    This is a habit tracker app. You can use it to create habits, mark them as complete, and delete them.
    ''')

def list_commands():
    """This function lists all commands"""
    print('''
    create_habit - Create a habit
    list_habits - List all habits
    mark_complete - Mark a habit as complete
    mark_incomplete - Mark a habit as incomplete
    delete_habit - Delete a habit
    help - Display the help menu
    list_commands - List all commands
    exit - Exit the app
    ''')

def get_streak(habit):
    """This function gets the streak of a habit"""
    streak = 0
    for day in range(1, 31):
        date = datetime.date.today() - datetime.timedelta(days=day)
        if date in habit.completed:
            streak += 1
        else:
            break
    return streak

if __name__ == "__main__":
    main()

db.py

import sqlite3 as sql
import datetime as dt

def create_habit(name:str,frequency:str,db_filename="main.db"):
    """
    If your database file is not to be named `"main.db"`, input the name of the file manually.
    """
    conn=sql.connect(db_filename)
    cursor=conn.cursor()
    cursor.execute("""
    CREATE TABLE IF NOT EXISTS "habits"(
        "name" TEXT NOT NULL UNIQUE,
        "frequency" TEXT NOT NULL,
        "created_at" TEXT NOT NULL,
        "completed_at" TEXT
    );
    """)
    created_at=str(dt.datetime.now())
    insert_values=(name,frequency,created_at)
    cursor.execute(f"INSERT INTO \"habits\" (\"name\",\"frequency\",\"created_at\") VALUES(?,?,?)",insert_values)
    conn.commit()

def list_habits(db_filename="main.db"):
    """
    The default filename for this function is `"main.db"`. If you need to connect to a different database,
    make sure to change the parameter.

    This will return a dict value such as follows:

    ```
    {
        {
            "name":"habit name",
            "frequency":"whatever was chosen",
            "completed":False,
            "completed_bool":False
        },
        {
            "name":"habit name",
            "frequency":"whatever was chosen",
            "completed":"a very nice time and date as a string",
            "completed_bool":True
        }
    }
    ```
    """
    conn=sql.connect(db_filename)
    cursor=conn.cursor()
    returned_lists=cursor.execute("SELECT \"name\",\"frequency\" FROM \"habits\"").fetchall()
    return_value={}
    #returned_lists will return like as below (each tuple represents a row)
    # [("name",frequency),("name",frequency)]
    for row in returned_lists:
        #the name should've returned in the first place in the list
        nameVar=row[0]
        frequencyVar=row[1]
        #check if the task is complete
        is_complete=Habit(name=nameVar).is_complete()
        #process a value for complete_bool if it's not False
        if is_complete==False:
            complete_bool=False
        if is_complete!=False:
            complete_bool=True
        #add the selected name to the final list
        return_value.update({
            nameVar:{
                "name":nameVar,
                "frequency":frequencyVar,
                "complete":is_complete,
                "completed_bool":complete_bool
            }})
    return return_value

class Habit:
    def __init__(self,name:str,db_filename="main.db"):
        """
        Input the name of the habit

        If your database file is not to be named `"main.db"`, input the name of the file manually.
        """
        self.name=name
        self.conn=sql.connect(db_filename)
        self.cursor=self.conn.cursor()
    def is_complete(self):
        """
        This will return a string or a boolean value

        - a string containing a date and time: The task has been completed

        - `False`: The task has not been completed
        """
        completed_at=self.cursor.execute(f"SELECT completed_at FROM habits WHERE name=\"{self.name}\"").fetchall()
        #completed_at should return as [("a specific date and time")] or as [(None)]
        completed_at=completed_at[0][0]
        #if it was a Nonetype
        if completed_at==None:
            return False
        else:
            #if it gets here, the habit was marked as complete
            return completed_at
    def mark_complete(self):
        """
        This will mark a habit as complete.
        """
        current_datetime_string=str(dt.datetime.now())
        #the insert values are a preparation for the SQLite insert
        insert_values=(current_datetime_string,self.name)
        self.cursor.execute("UPDATE habits SET completed_at=? WHERE name=?",insert_values)
        self.conn.commit()
    def mark_incomplete(self):
        """
        This will mark a habit as incomplete.
        """
        #the insert values are a preparation for the SQLite insert
        insert_values=(None,self.name)
        self.cursor.execute("UPDATE habits SET completed_at=? WHERE name=?",insert_values)
        self.conn.commit()
    def delete_habit(self):
        """
        This will delete a habit
        """
        #the insert values are a preparation for the SQLite insert
        #I don't know why, but putting the comma at the end of the insert_values tuple fixes some sort of issue with SQLite.
        #please don't remove the comma.
        insert_values=(self.name,)
        self.cursor.execute("DELETE FROM habits WHERE name=?",insert_values)
        self.conn.commit()

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.