Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
My First App
#1
Hello Everyone,

I have created a habit tracking app that I would love to get some feedback on. This is my first real OOP project in Python. If anyone would be willing to review my app and give me some feedback, I would be extremely grateful. Thanks in advance!

Github URL: https://github.com/Bcopeland64/IU-Habit-Tracker-App
Apologies for not posting the code here. Please find the app in question below:

main.py file:

import click
from habit import HabitTracker, Habit
import questionary
from analytics import *
from db2 import HabitDB, HabitTracker

tracker = HabitTracker()



@click.group()
def cli():
    pass

@cli.command()
def create():
    name = questionary.text("Enter the habit name: ").ask()
    period = questionary.select("Enter the habit period:", choices=["daily", "weekly", "monthly"]).ask()
    tracker.create_habit(name, period)
    click.echo(f'Habit "{name}" with period "{period}" created successfully!')

@cli.command()
def delete():
    name = questionary.text("Enter the habit name: ").ask()
    tracker.delete_habit(name)
    click.echo(f'Habit "{name}" deleted successfully!')

@cli.command()
def list_habit_groups():
    habits = tracker.get_habits()
    click.echo('Current habits:')
    for habit in habits:
        click.echo(f'- {habit.name} ({habit.period})')

@cli.command()
def list_habit_groups_period():
    period = questionary.select("Enter the habit period:", choices=["daily", "weekly", "monthly"]).ask()
    habits = tracker.get_habits_by_period(period)
    click.echo(f'Current {period} habits:')
    for habit in habits:
        click.echo(f'- {habit.name}')

@cli.command()
def longest_streak():
    longest_streak_habit = tracker.get_longest_streak()
    if longest_streak_habit:
        click.echo(f'Habit with longest streak: {longest_streak_habit.name} ({longest_streak_habit.get_streak()})')
    else:
        click.echo('No habits with a streak.')

@cli.command()
def longest_streak_habit():
    name = questionary.text("Enter the habit name: ").ask()
    streak = tracker.get_longest_streak_by_habit(name)
    if streak:
        click.echo(f'Longest streak for habit "{name}": {streak}')
    else:
        click.echo(f'Habit "{name}" not found.')

@cli.command()
def mark():
    name = questionary.text("Enter the habit name: ").ask()
    tracker.mark_complete(name)
    click.echo(f'Habit "{name}" marked successfully!')

@cli.command()
def unmark():
    name = questionary.text("Enter the habit name: ").ask()
    tracker.mark_incomplete(name)
    click.echo(f'Habit "{name}" unmarked successfully!')

def main():
    while True:
        command = input('Enter a command (create, delete, list, list-period, longest-streak, longest-streak-habit, or exit): ')
        if command == 'create':
            name = input('Enter the habit name: ')
            period = input('Enter the habit period (daily or weekly): ')
            tracker.create_habit(name, period)
            click.echo(f'Habit "{name}" with period "{period}" created successfully!')
        elif command == 'delete':
            name = input('Enter the habit name: ')
            tracker.delete_habit(name)
            click.echo(f'Habit "{name}" deleted successfully!')
        elif command == 'habit_groups':
            habits = tracker.get_habits()
            click.echo('Current habits:')
            for habit in habits:
                click.echo(f'- {habit.name} ({habit.period})')
        elif command == 'habit_groups_period':
            period = input('Enter the habit period (daily or weekly): ')
            habits = tracker.get_habits_by_period(period)
            click.echo(f'Current {period} habits:')
            for habit in habits:
                click.echo(f'- {habit.name}')
        elif command == 'longest-streak':
            longest_streak_habit = tracker.get_longest_streak()
            if longest_streak_habit:
                click.echo(f'Habit with longest streak: {longest_streak_habit.name} ({longest_streak_habit.get_streak()})')
            else:
                click.echo('No habits with a streak.')
        elif command == 'longest-streak-habit':
            name = input('Enter the habit name: ')
            streak = tracker.get_longest_streak_by_habit(name)
            if streak:
                click.echo(f'Longest streak for habit "{name}": {streak}')
            else:
                click.echo(f'Habit "{name}" not found.')
        elif command == 'mark':
            name = input('Enter the habit name: ')
            tracker.mark_complete(name)
            click.echo(f'Habit "{name}" marked successfully!')
        elif command == 'unmark':
            name = input('Enter the habit name: ')
            tracker.mark_incomplete(name)
            click.echo(f'Habit "{name}" unmarked successfully!')
        elif command == 'exit':
            break

if __name__ == '__main__':
    cli()
    main()
habit.py file:

 
from datetime import datetime



class Habit:
    def __init__(self, name: str, period: str):
        self.name = name
        self.period = period
        self.created_at = datetime.now()
        self.completed_at = []
    
    def mark_complete(self):
        self.completed_at.append(datetime.now())
        
    def mark_incomplete(self):
        self.completed_at.pop()
    
    def get_streak(self):
        if not self.completed_at:
            return 0
        current_streak = 1
        for i in range(1, len(self.completed_at)):
            if self.completed_at[i] - self.completed_at[i-1] == self.period:
                current_streak += 1
            else:
                break
        return current_streak

class HabitTracker:
    def __init__(self):
        self.habits = []
    
    def create_habit(self, name: str, period: str):
        new_habit = Habit(name, period)
        self.habits.append(new_habit)
    
    def delete_habit(self, name: str):
        for i, habit in enumerate(self.habits):
            if habit.name == name:
                del self.habits[i]
                break
    
    def get_habits(self):
        return self.habits
    
    def get_habits_by_period(self, period: str):
        return [habit for habit in self.habits if habit.period == period]
    
    def get_longest_streak(self):
        longest_streak = 0
        longest_streak_habit = None
        for habit in self.habits:
            streak = habit.get_streak()
            if streak > longest_streak:
                longest_streak = streak
                longest_streak_habit = habit
        return longest_streak_habit
    
    def get_longest_streak_by_habit(self, name: str):
        for habit in self.habits:
            if habit.name == name:
                return habit.get_streak()
        return 0
    
    def mark_complete(self, name: str):
        for habit in self.habits:
            if habit.name == name:
                habit.mark_complete()
                break
            
    def mark_incomplete(self, name: str):
        for i, completed_at in enumerate(self.completed_at):
            if self.name == name:
                del self.completed_at[i] 
            
    def relationships(self):
        for habit in self.habits:
            print(habit.name, habit.get_streak())
 
db.py file:

 
import sqlite3
import datetime
from habit import Habit, HabitTracker


class HabitDB:
    def establish_a_connection(self):
        self.conn = sqlite3.connect('habits.db')
        self.cursor = self.conn.cursor()

    # Create habits table if it does not exist
        self.cursor.execute('''
                CREATE TABLE IF NOT EXISTS habits (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                period TEXT NOT NULL,
                created_at DATETIME NOT NULL
            )''')

    # Create completions table if it does not exist 
        self.cursor.execute(''' 
                CREATE TABLE IF NOT EXISTS completions ( 
                habit_id INTEGER NOT NULL, 
                completed_at DATETIME NOT NULL, 
                FOREIGN KEY(habit_id) REFERENCES habits(id) ON 
                DELETE CASCADE  //added ON DELETE CASCADE to delete the related entries in completions when a row in habits is deleted 
            )''')
        self.conn.commit()

    def create_habit(self, name: str, period: str):
        self.cursor.execute(
            'INSERT INTO habits (name, period, created_at) VALUES (?, ?, ?)',
            (name, period, datetime.now())
        )
        self.conn.commit()

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

    def mark_complete(self, name: str):
        self.cursor.execute(
            'SELECT id FROM habits WHERE name=?',
            (name,)
        )
        habit_id = self.cursor.fetchone()[0]
        self.cursor.execute(
            'INSERT INTO completions (habit_id, completed_at) VALUES (?, ?)',
            (habit_id, datetime.now())
        )
        self.conn.commit()

    def mark_incomplete(self, name: str):
        self.cursor.execute(
            'SELECT id FROM habits WHERE name=?',
            (name,)
        )
        habit_id = self.cursor.fetchone()[0]
        self.cursor.execute(
            'DELETE FROM completions WHERE habit_id=? ORDER BY completed_at DESC LIMIT 1',
            (habit_id,)
        )
        self.conn.commit()

    
    def get_habits(self):
        self.cursor.execute('SELECT * FROM habits')
        rows = self.cursor.fetchall()
        habits = []
        for row in rows:
            id, name, period, createdat = row
            self.cursor.execute(
            'SELECT completedat FROM completions WHERE habitid=?',
                (id,))
            completedatrows = self.cursor.fetchall()
            completedat = [row[0] for row in completedatrows]
            habits.append(Habit(id, name, period, createdat, completedat))
        return habits


    def get_habits_by_period(self, period: str):
        self.cursor.execute('SELECT * FROM habits WHERE period=?', (period,))
        rows = self.cursor.fetchall()
        habits = []
        for row in rows:
            id, name, _, created_at = row
            self.cursor.execute(
                'SELECT completed_at FROM completions WHERE habit_id=?',
                (id,)
            )
            completed_at_rows = self.cursor.fetchall()
            
class HabitTracker:
    def __init__(self):
        self.habits = []
        self.db = HabitDB()
        self.db.establish_a_connection()

    def create_habit(self, name: str, period: str):
        self.db.create_habit(name, period)

    def delete_habit(self, name: str):
        self.db.delete_habit(name)

    def get_habits(self):
        return self.db.get_habits()

    def get_habits_by_period(self, period: str):
        return self.db.get_habits_by_period(period)

    def get_longest_streak(self):
        longest_streak = 0
        longest_streak_habit = None
        for habit in self.habits:
            streak = habit.get_streak()
            if streak > longest_streak:
                longest_streak = streak
                longest_streak_habit = habit
        return longest_streak_habit

    def get_longest_streak_by_habit(self, name: str):
        for habit in self.habits:
            if habit.name == name:
                return habit.get_streak()
        return 0

    def mark_complete(self, name: str):
        self.db.mark_complete(name)

    def mark_incomplete(self, name: str):
        self.db.mark_incomplete(name)

    def get_longest_streak_by_habit(self, name: str):
        for habit in self.habits:
            if habit.name == name:
                return habit.get_streak()
        return 0

    def mark_complete(self, name: str):
        self.db.mark_complete(name)

    def mark_incomplete(self, name: str):
        self.db.mark_incomplete(name) 
Larz60+ write Feb-13-2023, 05:35 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply


Messages In This Thread
My First App - by BCopeland64 - Feb-13-2023, 09:18 AM
RE: My First App - by buran - Feb-15-2023, 11:57 AM
RE: My First App - by BCopeland64 - Feb-15-2023, 05:46 PM
RE: My First App - by buran - Feb-15-2023, 07:21 PM
RE: My First App - by BCopeland64 - Feb-16-2023, 09:20 AM
RE: My First App - by buran - Feb-16-2023, 11:20 AM
RE: My First App - by BCopeland64 - Feb-16-2023, 11:52 AM
RE: My First App - by BCopeland64 - Feb-16-2023, 08:20 PM

Forum Jump:

User Panel Messages

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