Project 2: Habit Tracker: Using MongoDB in the habit tracker (+40, -21)

.env.example (+1, -0)

From: curriculum/section10/lectures/09_using_mongodb_in_project/end/.env.example

To: curriculum/section10/lectures/09_using_mongodb_in_project/end/.env.example

            
            new file mode 100644
index 0000000..9dead41
--- /dev/null
+++ b/curriculum/section10/lectures/09_using_mongodb_in_project/end/.env.example
@@ -0,0 +1 @@
+MONGODB_URI=
        

app.py (+8, -1)

From: curriculum/section10/lectures/09_using_mongodb_in_project/start/app.py

To: curriculum/section10/lectures/09_using_mongodb_in_project/end/app.py

            
            index 8c8ddb0..90af667 100644
--- a/curriculum/section10/lectures/09_using_mongodb_in_project/start/app.py
+++ b/curriculum/section10/lectures/09_using_mongodb_in_project/end/app.py
@@ -1,9 +1,16 @@
+import os
 from flask import Flask
 from routes import pages
+from pymongo import MongoClient
+from dotenv import load_dotenv
+
+load_dotenv()
 
 
 def create_app():
     app = Flask(__name__)
-    app.register_blueprint(pages)
+    client = MongoClient(os.environ.get("MONGODB_URI"))
+    app.db = client.get_default_database()
 
+    app.register_blueprint(pages)
     return app
        

routes.py (+27, -16)

From: curriculum/section10/lectures/09_using_mongodb_in_project/start/routes.py

To: curriculum/section10/lectures/09_using_mongodb_in_project/end/routes.py

            
            index be8e7c6..747b651 100644
--- a/curriculum/section10/lectures/09_using_mongodb_in_project/start/routes.py
+++ b/curriculum/section10/lectures/09_using_mongodb_in_project/end/routes.py
@@ -1,36 +1,45 @@
 import datetime
-from collections import defaultdict
-from flask import Blueprint, render_template, request, redirect, url_for
+import uuid
+from flask import Blueprint, request, redirect, url_for, render_template, current_app
 
 pages = Blueprint(
     "habits", __name__, template_folder="templates", static_folder="static"
 )
-habits = ["Test habit"]
-completions = defaultdict(list)
 
 
 @pages.context_processor
 def add_calc_date_range():
-    def date_range(start: datetime.date):
+    def date_range(start: datetime.datetime):
         dates = [start + datetime.timedelta(days=diff) for diff in range(-3, 4)]
         return dates
 
     return {"date_range": date_range}
 
 
+def today_at_midnight():
+    today = datetime.datetime.today()
+    return datetime.datetime(today.year, today.month, today.day)
+
+
 @pages.route("/")
 def index():
     date_str = request.args.get("date")
     if date_str:
-        selected_date = datetime.date.fromisoformat(date_str)
+        selected_date = datetime.datetime.fromisoformat(date_str)
     else:
-        selected_date = datetime.date.today()
+        selected_date = today_at_midnight()
+
+    habits_on_date = current_app.db.habits.find({"added": {"$lte": selected_date}})
+    completions = [
+        habit["habit"]
+        for habit in current_app.db.completions.find({"date": selected_date})
+    ]
 
     return render_template(
         "index.html",
-        habits=habits,
+        habits=habits_on_date,
         selected_date=selected_date,
-        completions=completions[selected_date],
+        completions=completions,
         title="Habit Tracker - Home",
     )
 
@@ -38,20 +47,22 @@ def index():
 @pages.route("/complete", methods=["POST"])
 def complete():
     date_string = request.form.get("date")
-    date = datetime.date.fromisoformat(date_string)
-    habit = request.form.get("habitName")
-    completions[date].append(habit)
+    date = datetime.datetime.fromisoformat(date_string)
+    habit = request.form.get("habitId")
+    current_app.db.completions.insert_one({"date": date, "habit": habit})
 
     return redirect(url_for(".index", date=date_string))
 
 
 @pages.route("/add", methods=["GET", "POST"])
 def add_habit():
+    today = today_at_midnight()
+
     if request.form:
-        habits.append(request.form.get("habit"))
+        current_app.db.habits.insert_one(
+            {"_id": uuid.uuid4().hex, "added": today, "name": request.form.get("habit")}
+        )
 
     return render_template(
-        "add_habit.html",
-        title="Habit Tracker - Add Habit",
-        selected_date=datetime.date.today(),
+        "add_habit.html", title="Habit Tracker - Add Habit", selected_date=today
     )
        

index.html (+4, -4)

From: curriculum/section10/lectures/09_using_mongodb_in_project/start/templates/index.html

To: curriculum/section10/lectures/09_using_mongodb_in_project/end/templates/index.html

            
            index 81e1478..7ad2953 100644
--- a/curriculum/section10/lectures/09_using_mongodb_in_project/start/templates/index.html
+++ b/curriculum/section10/lectures/09_using_mongodb_in_project/end/templates/index.html
@@ -3,11 +3,11 @@
 {% block main_content %}
     <section class="habit-list">
     {% for habit in habits %}
-        {% set completed = habit in completions %}
+        {% set completed = habit["_id"] in completions %}
         {% if completed %}
             <div class="habit completed">
                 <p class="habit__name">
-                    {{ habit }}
+                    {{ habit["name"] }}
                 </p>
                 <svg class="habit__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
                     <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
@@ -16,10 +16,10 @@
         {% else %}
             <div class="habit">
                 <form method="POST" class="habit__form" action="{{ url_for('habits.complete') }}">
-                <input type="hidden" id="habitName" name="habitName" value="{{ habit }}" />
+                <input type="hidden" id="habitId" name="habitId" value="{{ habit['_id'] }}" />
                 <input type="hidden" id="date" name="date" value="{{ selected_date }}" />
                 <button type="submit" class="habit__button">
-                        {{ habit }}
+                        {{ habit["name"] }}
                 </button>
                 </form>
             </div>