(window.webpackJsonp=window.webpackJsonp||[]).push([[119],{642:function(e,t,s){"use strict";s.r(t);var a=s(56),n=Object(a.a)({},(function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[s("h1",{attrs:{id:"using-mongodb-in-the-habit-tracker"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#using-mongodb-in-the-habit-tracker"}},[e._v("#")]),e._v(" Using MongoDB in the habit tracker")]),e._v(" "),s("p"),s("div",{staticClass:"table-of-contents"},[s("ul",[s("li",[s("a",{attrs:{href:"#in-this-video-tl-dr"}},[e._v("In this video... (TL;DR)")])]),s("li",[s("a",{attrs:{href:"#code-written-in-this-lecture"}},[e._v("Code written in this lecture")])]),s("li",[s("a",{attrs:{href:"#finished-code-changes"}},[e._v("Finished code changes")]),s("ul",[s("li",[s("a",{attrs:{href:"#in-app-py"}},[e._v("In app.py")])]),s("li",[s("a",{attrs:{href:"#in-routes-py"}},[e._v("In routes.py:")])]),s("li",[s("a",{attrs:{href:"#in-index-html"}},[e._v("In index.html:")])]),s("li",[s("a",{attrs:{href:"#environment-files"}},[e._v("Environment files")])])])])])]),s("p"),e._v(" "),s("h2",{attrs:{id:"in-this-video-tl-dr"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#in-this-video-tl-dr"}},[e._v("#")]),e._v(" In this video... (TL;DR)")]),e._v(" "),s("div",{staticClass:"custom-block tip"},[s("p",{staticClass:"custom-block-title"},[e._v("TIP")]),e._v(" "),s("p",[e._v("List of all code changes made in this lecture: "),s("a",{attrs:{href:"https://diff-store.com/diff/section10__09_using_mongodb_in_project",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://diff-store.com/diff/section10__09_using_mongodb_in_project"),s("OutboundLink")],1)])]),e._v(" "),s("p",[e._v("We'll store habit data in MongoDB: a unique "),s("code",[e._v("_id")]),e._v(" for each habit alongside the habit "),s("code",[e._v("name")]),e._v(".")]),e._v(" "),s("p",[e._v("MongoDB works well with Python's "),s("code",[e._v("datetime")]),e._v(" objects, so we'll use that instead of "),s("code",[e._v("date")]),e._v(". That does mean we need to do a little bit more work to handle the time aspect, but it's not much and it makes database interactions much easier.")]),e._v(" "),s("h2",{attrs:{id:"code-written-in-this-lecture"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#code-written-in-this-lecture"}},[e._v("#")]),e._v(" Code written in this lecture")]),e._v(" "),s("p",[e._v("Let's begin by adding our "),s("code",[e._v(".env")]),e._v(" file that will contain the MongoDB connection string. Make sure to provide your own connection string here:")]),e._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[e._v("MONGODB_URI=mongodb+srv://username:password@cluster0.cboqc.mongodb.net/tracker?retryWrites=true&w=majority\n")])])]),s("p",[e._v("I'll also create a "),s("code",[e._v(".env.example")]),e._v(" file to remind developers that they need to provide a "),s("code",[e._v(".env")]),e._v(" file:")]),e._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[e._v("MONGODB_URI=\n")])])]),s("p",[e._v("With this, we can then go to "),s("code",[e._v("app.py")]),e._v(" and make a few changes:")]),e._v(" "),s("ul",[s("li",[e._v("Import "),s("code",[e._v("os")]),e._v(" for handling environment variables.")]),e._v(" "),s("li",[e._v("Import "),s("code",[e._v("MongoClient")])]),e._v(" "),s("li",[e._v("Import "),s("code",[e._v("load_dotenv")]),e._v(" to load the "),s("code",[e._v(".env")]),e._v(" file")]),e._v(" "),s("li",[e._v("Use this in "),s("code",[e._v("create_app()")]),e._v(" to connect to MongoDB and store the "),s("code",[e._v("MongoClient")]),e._v(" in the app object.")])]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("--- app.py")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("+++ app.py")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -1,9 +1,16 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("import os\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from flask import Flask\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from routes import pages\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from pymongo import MongoClient\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from dotenv import load_dotenv\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("load_dotenv()\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def create_app():\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" app = Flask(__name__)\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' client = MongoClient(os.environ.get("MONGODB_URI"))\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" app.db = client.get_default_database()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" app.register_blueprint(pages)\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return app\n")])])])])]),s("p",[e._v("Most of the other changes are in "),s("code",[e._v("routes.py")]),e._v(".")]),e._v(" "),s("p",[e._v("Since we'll be using the database, we can get rid of "),s("code",[e._v("defaultdict")]),e._v(", and the "),s("code",[e._v("habits")]),e._v(" and "),s("code",[e._v("completions")]),e._v(" variables. We'll need to import "),s("code",[e._v("uuid")]),e._v(" and "),s("code",[e._v("current_app")]),e._v(" from Flask.")]),e._v(" "),s("p",[e._v("In MongoDB we'll have two collections: one for the habits, and one for the completions.")]),e._v(" "),s("p",[e._v("The "),s("code",[e._v("habits")]),e._v(" collection will have three fields:")]),e._v(" "),s("ul",[s("li",[s("code",[e._v("_id")]),e._v(", a unique UUID to identify each habit")]),e._v(" "),s("li",[s("code",[e._v("added")]),e._v(", a datetime field telling us when the habit was added to the database.")]),e._v(" "),s("li",[s("code",[e._v("name")]),e._v(", the name of the habit as typed by the user.")])]),e._v(" "),s("p",[e._v("The reason we're going to add an "),s("code",[e._v("added")]),e._v(" field is so we don't show habits as non-completed before they were even added to our database.")]),e._v(" "),s("p",[e._v("The "),s("code",[e._v("completions")]),e._v(" collection will also have three fields:")]),e._v(" "),s("ul",[s("li",[s("code",[e._v("_id")]),e._v(", a unique ID generated by MongoDB. We won't be using this, but MongoDB generates it for us if we don't provide it.")]),e._v(" "),s("li",[s("code",[e._v("date")]),e._v(", the date of the habit completion.")]),e._v(" "),s("li",[s("code",[e._v("habit")]),e._v(", the unique ID of the habit that was completed on this date.")])]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("import datetime\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from collections import defaultdict\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from flask import Blueprint, render_template, request, redirect, url_for\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("import uuid\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from flask import Blueprint, request, redirect, url_for, render_template, current_app\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("pages = Blueprint(\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "habits", __name__, template_folder="templates", static_folder="static"\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(")\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('habits = ["Test habit"]\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("completions = defaultdict(list)\n")])])])])]),s("p",[e._v("I'll go ahead and change the "),s("code",[e._v("date_range")]),e._v(" function to take in a "),s("code",[e._v("datetime")]),e._v(" object. I'll also create a "),s("code",[e._v("today_at_midnight")]),e._v(" function that returns a "),s("code",[e._v("datetime")]),e._v(" object with the time set to midnight. That's so we don't have to worry about the time aspect of things:")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("@pages.context_processor\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def add_calc_date_range():\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" def date_range(start: datetime.date):\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" def date_range(start: datetime.datetime):\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" dates = [start + datetime.timedelta(days=diff) for diff in range(-3, 4)]\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return dates\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' return {"date_range": date_range}\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def today_at_midnight():\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" today = datetime.datetime.today()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return datetime.datetime(today.year, today.month, today.day)\n")])]),e._v("\n")])])]),s("p",[e._v("Now let's handle adding new habits.")]),e._v(" "),s("p",[e._v("We want to include a unique ID for each habit, as well as when the habit was added to the database and the habit's name.")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('@pages.route("/add", methods=["GET", "POST"])\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def add_habit():\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" today = today_at_midnight()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if request.form:\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habits.append(request.form.get("habit"))\n')])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" current_app.db.habits.insert_one(\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' {"_id": uuid.uuid4().hex, "added": today, "name": request.form.get("habit")}\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" )\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return render_template(\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "add_habit.html",\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' title="Habit Tracker - Add Habit",\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date=datetime.date.today(),\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "add_habit.html", title="Habit Tracker - Add Habit", selected_date=today\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" )\n")])])])])]),s("p",[e._v("I'm using the "),s("code",[e._v("uuid")]),e._v(" module to create the unique ID.")]),e._v(" "),s("p",[e._v("Next up, we have to make a few changes to the "),s("code",[e._v("index")]),e._v(" route. We'll use "),s("code",[e._v("datetime")]),e._v(" where we previously used "),s("code",[e._v("date")]),e._v(", and we'll access the database for data instead of the variables we had previously:")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('@pages.route("/")\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def index():\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' date_str = request.args.get("date")\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if date_str:\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = datetime.date.fromisoformat(date_str)\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = datetime.datetime.fromisoformat(date_str)\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" else:\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = datetime.date.today()\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = today_at_midnight()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habits_on_date = current_app.db.habits.find({"added": {"$lte": selected_date}})\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions = [\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habit["habit"]\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' for habit in current_app.db.completions.find({"date": selected_date})\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ]\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return render_template(\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "index.html",\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" habits=habits,\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" habits=habits_on_date,\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date=selected_date,\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions=completions[selected_date],\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions=completions,\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' title="Habit Tracker - Home",\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" )\n")])])])])]),s("p",[e._v("Here I'm using MongoDB search filters, like "),s("code",[e._v('{"$lte": selected_date}')]),e._v(", to find those habits that were added today or earlier.")]),e._v(" "),s("p",[e._v("Then I'm querying the database for the completions on the "),s("code",[e._v("selected_date")]),e._v(", getting only the habit names back.")]),e._v(" "),s("p",[e._v("With that, we've got habits and completions, which we can then pass to our template. However, previously both our "),s("code",[e._v("habits")]),e._v(" and "),s("code",[e._v("completions")]),e._v(" lists were just strings.")]),e._v(" "),s("p",[e._v("Now, "),s("code",[e._v("habits")]),e._v(" contains dictionaries and "),s("code",[e._v("completions")]),e._v(" contains strings (the ID of each habit).")]),e._v(" "),s("p",[e._v("We have to make some small changes in our "),s("code",[e._v("index.html")]),e._v(" template to account for this:")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("--- C:\\Users\\jose\\Documents\\projects\\courses\\new-python-web\\curriculum\\section10\\lectures\\09_using_mongodb_in_project\\start\\templates\\index.html")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("+++ C:\\Users\\jose\\Documents\\projects\\courses\\new-python-web\\curriculum\\section10\\lectures\\09_using_mongodb_in_project\\end\\templates\\index.html")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -3,11 +3,11 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("{% block main_content %}\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% for habit in habits %}\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% set completed = habit in completions %}\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' {% set completed = habit["_id"] in completions %}\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% if completed %}\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('

\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {{ habit }}\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' {{ habit["name"] }}\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("

\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')])]),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -16,10 +16,10 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% else %}\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("
\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("
\n")])])])])]),s("p",[e._v("Note that "),s("code",[e._v("completions")]),e._v(" will contain habit IDs, so the form has to send habit IDs instead of habit names.")]),e._v(" "),s("p",[e._v("Let's deal with the "),s("code",[e._v("complete")]),e._v(" endpoint next:")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def complete():\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' date_string = request.form.get("date")\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" date = datetime.date.fromisoformat(date_string)\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habit = request.form.get("habitName")\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions[date].append(habit)\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" date = datetime.datetime.fromisoformat(date_string)\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habit = request.form.get("habitId")\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' current_app.db.completions.insert_one({"date": date, "habit": habit})\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' return redirect(url_for(".index", date=date_string))\n')])])])])]),s("p",[e._v("As you can see, not much going on there: we're getting the habit ID instead of the name, and inserting that along with the date into the database. Remember to also change your "),s("code",[e._v("datetime.date")]),e._v(" to "),s("code",[e._v("datetime.datetime")]),e._v(" since we're setting the time to midnight!")]),e._v(" "),s("h2",{attrs:{id:"finished-code-changes"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#finished-code-changes"}},[e._v("#")]),e._v(" Finished code changes")]),e._v(" "),s("h3",{attrs:{id:"in-app-py"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#in-app-py"}},[e._v("#")]),e._v(" In "),s("code",[e._v("app.py")])]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("--- app.py")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("+++ app.py")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -1,9 +1,16 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("import os\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from flask import Flask\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from routes import pages\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from pymongo import MongoClient\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from dotenv import load_dotenv\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("load_dotenv()\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def create_app():\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" app = Flask(__name__)\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' client = MongoClient(os.environ.get("MONGODB_URI"))\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" app.db = client.get_default_database()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" app.register_blueprint(pages)\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return app\n")])])])])]),s("h3",{attrs:{id:"in-routes-py"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#in-routes-py"}},[e._v("#")]),e._v(" In "),s("code",[e._v("routes.py")]),e._v(":")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("--- routes.py")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("+++ routes.py")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -1,36 +1,45 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("import datetime\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from collections import defaultdict\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from flask import Blueprint, render_template, request, redirect, url_for\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("import uuid\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("from flask import Blueprint, request, redirect, url_for, render_template, current_app\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("pages = Blueprint(\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "habits", __name__, template_folder="templates", static_folder="static"\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(")\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('habits = ["Test habit"]\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("completions = defaultdict(list)\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("@pages.context_processor\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def add_calc_date_range():\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" def date_range(start: datetime.date):\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" def date_range(start: datetime.datetime):\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" dates = [start + datetime.timedelta(days=diff) for diff in range(-3, 4)]\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return dates\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' return {"date_range": date_range}\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def today_at_midnight():\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" today = datetime.datetime.today()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return datetime.datetime(today.year, today.month, today.day)\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('@pages.route("/")\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def index():\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' date_str = request.args.get("date")\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if date_str:\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = datetime.date.fromisoformat(date_str)\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = datetime.datetime.fromisoformat(date_str)\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" else:\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = datetime.date.today()\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date = today_at_midnight()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habits_on_date = current_app.db.habits.find({"added": {"$lte": selected_date}})\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions = [\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habit["habit"]\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' for habit in current_app.db.completions.find({"date": selected_date})\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" ]\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return render_template(\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "index.html",\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" habits=habits,\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" habits=habits_on_date,\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date=selected_date,\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions=completions[selected_date],\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions=completions,\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' title="Habit Tracker - Home",\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" )\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -39,19 +48,21 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def complete():\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' date_string = request.form.get("date")\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" date = datetime.date.fromisoformat(date_string)\n")]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habit = request.form.get("habitName")\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" completions[date].append(habit)\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" date = datetime.datetime.fromisoformat(date_string)\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habit = request.form.get("habitId")\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' current_app.db.completions.insert_one({"date": date, "habit": habit})\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' return redirect(url_for(".index", date=date_string))\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('@pages.route("/add", methods=["GET", "POST"])\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("def add_habit():\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" today = today_at_midnight()\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" if request.form:\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' habits.append(request.form.get("habit"))\n')])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" current_app.db.habits.insert_one(\n")]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' {"_id": uuid.uuid4().hex, "added": today, "name": request.form.get("habit")}\n')]),s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" )\n")])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" return render_template(\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "add_habit.html",\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' title="Habit Tracker - Add Habit",\n')]),s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" selected_date=datetime.date.today(),\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' "add_habit.html", title="Habit Tracker - Add Habit", selected_date=today\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" )\n")])])])])]),s("h3",{attrs:{id:"in-index-html"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#in-index-html"}},[e._v("#")]),e._v(" In "),s("code",[e._v("index.html")]),e._v(":")]),e._v(" "),s("div",{staticClass:"language-diff extra-class"},[s("pre",{pre:!0,attrs:{class:"language-diff"}},[s("code",[s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("--- templates/index.html")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("+++ templates/index.html")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -3,11 +3,11 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("{% block main_content %}\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% for habit in habits %}\n")])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% set completed = habit in completions %}\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' {% set completed = habit["_id"] in completions %}\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% if completed %}\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('

\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {{ habit }}\n")])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' {{ habit["name"] }}\n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("

\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')])]),s("span",{pre:!0,attrs:{class:"token coord"}},[e._v("@@ -16,10 +16,10 @@")]),e._v("\n"),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(" {% else %}\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v('
\n')])]),s("span",{pre:!0,attrs:{class:"token deleted-sign deleted"}},[s("span",{pre:!0,attrs:{class:"token prefix deleted"}},[e._v("-")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')])]),s("span",{pre:!0,attrs:{class:"token inserted-sign inserted"}},[s("span",{pre:!0,attrs:{class:"token prefix inserted"}},[e._v("+")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')])]),s("span",{pre:!0,attrs:{class:"token unchanged"}},[s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n')]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v(' \n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("
\n")]),s("span",{pre:!0,attrs:{class:"token prefix unchanged"}},[e._v(" ")]),s("span",{pre:!0,attrs:{class:"token line"}},[e._v("
\n")])])])])]),s("h3",{attrs:{id:"environment-files"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#environment-files"}},[e._v("#")]),e._v(" Environment files")]),e._v(" "),s("p",[e._v("Added "),s("code",[e._v(".env")]),e._v(" and "),s("code",[e._v(".env.example")]),e._v(" with "),s("code",[e._v("MONGODB_URI")]),e._v(".")])])}),[],!1,null,null,null);t.default=n.exports}}]);