Final Assignment

Your task is to create simple web application which will track your sport activity.

Assignment steps

  1. Create GitHub repository for your project
  2. Install locally on your laptop Flask

Řešení

  1. Create project structure for Flask application

Řešení

Content of config.py

Řešení

  1. Integrate Travis CI/CD with your GitHub project

Content of .travis.yml

Řešení

Content of tests/test_math.py:

Řešení

Content of app/__init__.py:

Řešení

Content of activity.py

Řešení

Content of app/models.py

Řešení

Content of test/test_user_model.py:

Řešení

Content of app/auth/__init.py:

Řešení

Content of app/auth/views.py:

Řešení

Content of app/auth/forms.py:

Řešení

Content of app/templates/base.html:

Řešení

Content of apps/templates/auth/login.html

Řešení

  1. Create test case for first function (route)
  2. Create first function
  3. Create basic application that will do following:
    • Allow users to log-in
    • Use Templates
    • Use Web Forms
    • Use SQLite or PostreSQL/MySQL
    • Error Handling
    • Use Bootstrap 4
    • Use logging to the console and log file
    • Email Support (sending notification about new sport activity)
    • Allow user to add new kind of sport (like running)
    • Allow user to add new activity and assign it to the sport type
    • Display last ten activities on Dashboard
{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/tieto-ostrava-jaro:regular-expressions:2",
      "title": "Final Assignment",
      "html": "\n          \n    \n\n    <h2>Final Assignment</h2>\n<p>Your task is to create simple web application which will track your sport activity.</p>\n<h3>Assignment steps</h3>\n<ol>\n<li>Create GitHub repository for your project</li>\n<li>Install locally on your laptop Flask</li>\n</ol>\n<div class=\"solution\" id=\"solution-0\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/0/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span>python -m venv venv\n<span class=\"nb\">source</span> venv/bin/activate\npip install flask\n</pre></div>\n    </div>\n</div><ol>\n<li>Create project structure for Flask application</li>\n</ol>\n<div class=\"solution\" id=\"solution-1\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/1/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"p\">|</span>-activity\n  <span class=\"p\">|</span>-app/\n    <span class=\"p\">|</span>-templates/\n    <span class=\"p\">|</span>-static/\n    <span class=\"p\">|</span>-main/\n      <span class=\"p\">|</span>-__init__.py\n      <span class=\"p\">|</span>-errors.py\n      <span class=\"p\">|</span>-forms.py\n      <span class=\"p\">|</span>-views.py\n    <span class=\"p\">|</span>-__init__.py\n    <span class=\"p\">|</span>-email.py\n    <span class=\"p\">|</span>-models.py\n  <span class=\"p\">|</span>-migrations/\n  <span class=\"p\">|</span>-tests/\n    <span class=\"p\">|</span>-__init__.py\n    <span class=\"p\">|</span>-test*.py\n  <span class=\"p\">|</span>-venv/\n  <span class=\"p\">|</span>-requirements.txt\n  <span class=\"p\">|</span>-config.py\n  <span class=\"p\">|</span>-activity.py\n\n\nmkdir <span class=\"o\">{</span>app,migrations,tests<span class=\"o\">}</span>\nmkdir app/<span class=\"o\">{</span>templates,static,main<span class=\"o\">}</span>\ntouch requirements.txt\ntouch config.py\ntouch activity.py\ntouch app/__init__.py\ntouch app/main/__init__.py\n</pre></div>\n    </div>\n</div><p>Content of config.py</p>\n<div class=\"solution\" id=\"solution-2\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/2/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">os</span>\n<span class=\"n\">basedir</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">path</span><span class=\"o\">.</span><span class=\"n\">abspath</span><span class=\"p\">(</span><span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">path</span><span class=\"o\">.</span><span class=\"n\">dirname</span><span class=\"p\">(</span><span class=\"vm\">__file__</span><span class=\"p\">))</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">Config</span><span class=\"p\">:</span>\n    <span class=\"n\">SECRET_KEY</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;SECRET_KEY&apos;</span><span class=\"p\">)</span> <span class=\"ow\">or</span> <span class=\"s1\">&apos;hard to guess string&apos;</span>\n    <span class=\"n\">MAIL_SERVER</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;MAIL_SERVER&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;smtp.googlemail.com&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">MAIL_PORT</span> <span class=\"o\">=</span> <span class=\"nb\">int</span><span class=\"p\">(</span><span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;MAIL_PORT&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;587&apos;</span><span class=\"p\">))</span>\n    <span class=\"n\">MAIL_USE_TLS</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;MAIL_USE_TLS&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;true&apos;</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">lower</span><span class=\"p\">()</span> <span class=\"ow\">in</span> \\\n        <span class=\"p\">[</span><span class=\"s1\">&apos;true&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;on&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;1&apos;</span><span class=\"p\">]</span>\n    <span class=\"n\">MAIL_USERNAME</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;MAIL_USERNAME&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">MAIL_PASSWORD</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;MAIL_PASSWORD&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">ACTIVITY_MAIL_SUBJECT_PREFIX</span> <span class=\"o\">=</span> <span class=\"s1\">&apos;[Activity]&apos;</span>\n    <span class=\"n\">ACTIVITY_MAIL_SENDER</span> <span class=\"o\">=</span> <span class=\"s1\">&apos;Activity Admin &lt;activity@example.com&gt;&apos;</span>\n    <span class=\"n\">ACTIVITY_ADMIN</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;ACTIVITY_ADMIN&apos;</span><span class=\"p\">)</span>\n    <span class=\"n\">SQLALCHEMY_TRACK_MODIFICATIONS</span> <span class=\"o\">=</span> <span class=\"bp\">False</span>\n\n    <span class=\"nd\">@staticmethod</span>\n    <span class=\"k\">def</span> <span class=\"nf\">init_app</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">):</span>\n        <span class=\"k\">pass</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">DevelopmentConfig</span><span class=\"p\">(</span><span class=\"n\">Config</span><span class=\"p\">):</span>\n    <span class=\"n\">DEBUG</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n    <span class=\"n\">SQLALCHEMY_DATABASE_URI</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;DEV_DATABASE_URL&apos;</span><span class=\"p\">)</span> <span class=\"ow\">or</span> \\\n        <span class=\"s1\">&apos;sqlite:///&apos;</span> <span class=\"o\">+</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">path</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"n\">basedir</span><span class=\"p\">,</span> <span class=\"s1\">&apos;data-dev.sqlite&apos;</span><span class=\"p\">)</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">TestingConfig</span><span class=\"p\">(</span><span class=\"n\">Config</span><span class=\"p\">):</span>\n    <span class=\"n\">TESTING</span> <span class=\"o\">=</span> <span class=\"bp\">True</span>\n    <span class=\"n\">SQLALCHEMY_DATABASE_URI</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;TEST_DATABASE_URL&apos;</span><span class=\"p\">)</span> <span class=\"ow\">or</span> \\\n        <span class=\"s1\">&apos;sqlite://&apos;</span>\n\n<span class=\"k\">class</span> <span class=\"nc\">ProductionConfig</span><span class=\"p\">(</span><span class=\"n\">Config</span><span class=\"p\">):</span>\n    <span class=\"n\">SQLALCHEMY_DATABASE_URI</span> <span class=\"o\">=</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">environ</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"s1\">&apos;DATABASE_URL&apos;</span><span class=\"p\">)</span> <span class=\"ow\">or</span> \\\n        <span class=\"s1\">&apos;sqlite:///&apos;</span> <span class=\"o\">+</span> <span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">path</span><span class=\"o\">.</span><span class=\"n\">join</span><span class=\"p\">(</span><span class=\"n\">basedir</span><span class=\"p\">,</span> <span class=\"s1\">&apos;data.sqlite&apos;</span><span class=\"p\">)</span>\n\n<span class=\"n\">config</span> <span class=\"o\">=</span> <span class=\"p\">{</span>\n    <span class=\"s1\">&apos;development&apos;</span><span class=\"p\">:</span> <span class=\"n\">DevelopmentConfig</span><span class=\"p\">,</span>\n    <span class=\"s1\">&apos;testing&apos;</span><span class=\"p\">:</span> <span class=\"n\">TestingConfig</span><span class=\"p\">,</span>\n    <span class=\"s1\">&apos;production&apos;</span><span class=\"p\">:</span> <span class=\"n\">ProductionConfig</span><span class=\"p\">,</span>\n\n    <span class=\"s1\">&apos;default&apos;</span><span class=\"p\">:</span> <span class=\"n\">DevelopmentConfig</span>\n<span class=\"p\">}</span>\n</pre></div>\n    </div>\n</div><ol>\n<li>Integrate Travis CI/CD with your GitHub project</li>\n</ol>\n<p>Content of .travis.yml</p>\n<div class=\"solution\" id=\"solution-3\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/3/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span>dist: xenial   <span class=\"c1\"># required for Python &gt;= 3.7</span>\nlanguage: python\npython:\n  - <span class=\"s2\">&quot;3.7&quot;</span>\n<span class=\"c1\"># command to install dependencies</span>\ninstall:\n  - pip install -r requirements.txt\n<span class=\"c1\"># command to run tests</span>\nscript:\n  - pytest\n</pre></div>\n    </div>\n</div><p>Content of tests/test_math.py:</p>\n<div class=\"solution\" id=\"solution-4\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/4/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span>import math\n\ndef test_sqrt<span class=\"o\">()</span>:\n   <span class=\"nv\">num</span> <span class=\"o\">=</span> <span class=\"m\">25</span>\n   assert math.sqrt<span class=\"o\">(</span>num<span class=\"o\">)</span> <span class=\"o\">==</span> <span class=\"m\">5</span>\n</pre></div>\n    </div>\n</div><p>Content of app/__init__.py:</p>\n<div class=\"solution\" id=\"solution-5\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/5/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">from</span> <span class=\"nn\">flask</span> <span class=\"kn\">import</span> <span class=\"n\">Flask</span><span class=\"p\">,</span> <span class=\"n\">render_template</span>\n<span class=\"kn\">from</span> <span class=\"nn\">flask_bootstrap</span> <span class=\"kn\">import</span> <span class=\"n\">Bootstrap</span>\n<span class=\"kn\">from</span> <span class=\"nn\">flask_mail</span> <span class=\"kn\">import</span> <span class=\"n\">Mail</span>\n<span class=\"kn\">from</span> <span class=\"nn\">flask_moment</span> <span class=\"kn\">import</span> <span class=\"n\">Moment</span>\n<span class=\"kn\">from</span> <span class=\"nn\">flask_sqlalchemy</span> <span class=\"kn\">import</span> <span class=\"n\">SQLAlchemy</span>\n<span class=\"kn\">from</span> <span class=\"nn\">config</span> <span class=\"kn\">import</span> <span class=\"n\">config</span>\n\n<span class=\"n\">bootstrap</span> <span class=\"o\">=</span> <span class=\"n\">Bootstrap</span><span class=\"p\">()</span>\n<span class=\"n\">mail</span> <span class=\"o\">=</span> <span class=\"n\">Mail</span><span class=\"p\">()</span>\n<span class=\"n\">moment</span> <span class=\"o\">=</span> <span class=\"n\">Moment</span><span class=\"p\">()</span>\n<span class=\"n\">db</span> <span class=\"o\">=</span> <span class=\"n\">SQLAlchemy</span><span class=\"p\">()</span>\n\n<span class=\"k\">def</span> <span class=\"nf\">create_app</span><span class=\"p\">(</span><span class=\"n\">config_name</span><span class=\"p\">):</span>\n    <span class=\"n\">app</span> <span class=\"o\">=</span> <span class=\"n\">Flask</span><span class=\"p\">(</span><span class=\"vm\">__name__</span><span class=\"p\">)</span>\n    <span class=\"n\">app</span><span class=\"o\">.</span><span class=\"n\">config</span><span class=\"o\">.</span><span class=\"n\">from_object</span><span class=\"p\">(</span><span class=\"n\">config</span><span class=\"p\">[</span><span class=\"n\">config_name</span><span class=\"p\">])</span>\n    <span class=\"n\">config</span><span class=\"p\">[</span><span class=\"n\">config_name</span><span class=\"p\">]</span><span class=\"o\">.</span><span class=\"n\">init_app</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">)</span>\n\n    <span class=\"n\">bootstrap</span><span class=\"o\">.</span><span class=\"n\">init_app</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">)</span>\n    <span class=\"n\">mail</span><span class=\"o\">.</span><span class=\"n\">init_app</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">)</span>\n    <span class=\"n\">moment</span><span class=\"o\">.</span><span class=\"n\">init_app</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">)</span>\n    <span class=\"n\">db</span><span class=\"o\">.</span><span class=\"n\">init_app</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">)</span>\n\n    <span class=\"c1\"># attach routes and custom error pages here</span>\n\n    <span class=\"k\">return</span> <span class=\"n\">app</span>\n</pre></div>\n    </div>\n</div><p>Content of activity.py</p>\n<div class=\"solution\" id=\"solution-6\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/6/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"kn\">import</span> <span class=\"nn\">os</span>\n<span class=\"kn\">import</span> <span class=\"nn\">click</span>\n<span class=\"kn\">from</span> <span class=\"nn\">flask_migrate</span> <span class=\"kn\">import</span> <span class=\"n\">Migrate</span>\n<span class=\"kn\">from</span> <span class=\"nn\">app</span> <span class=\"kn\">import</span> <span class=\"n\">create_app</span><span class=\"p\">,</span> <span class=\"n\">db</span>\n\n<span class=\"nd\">@pytest.fixture</span>\n<span class=\"n\">app</span> <span class=\"o\">=</span> <span class=\"n\">create_app</span><span class=\"p\">(</span><span class=\"n\">os</span><span class=\"o\">.</span><span class=\"n\">getenv</span><span class=\"p\">(</span><span class=\"s1\">&apos;FLASK_CONFIG&apos;</span><span class=\"p\">)</span> <span class=\"ow\">or</span> <span class=\"s1\">&apos;default&apos;</span><span class=\"p\">)</span>\n<span class=\"n\">migrate</span> <span class=\"o\">=</span> <span class=\"n\">Migrate</span><span class=\"p\">(</span><span class=\"n\">app</span><span class=\"p\">,</span> <span class=\"n\">db</span><span class=\"p\">)</span>\n\n<span class=\"nd\">@app.shell_context_processor</span>\n<span class=\"k\">def</span> <span class=\"nf\">make_shell_context</span><span class=\"p\">():</span>\n    <span class=\"k\">return</span> <span class=\"nb\">dict</span><span class=\"p\">(</span><span class=\"n\">db</span><span class=\"o\">=</span><span class=\"n\">db</span><span class=\"p\">)</span>\n\n<span class=\"nd\">@app.route</span><span class=\"p\">(</span><span class=\"s1\">&apos;/&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">def</span> <span class=\"nf\">hello</span><span class=\"p\">():</span>\n    <span class=\"k\">return</span> <span class=\"s2\">&quot;Hello World!&quot;</span>\n</pre></div>\n    </div>\n</div><p>Content of app/models.py</p>\n<div class=\"solution\" id=\"solution-7\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/7/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <p>from werkzeug.security import generate_password_hash, check_password_hash\nfrom . import db</p>\n<p>class Role(db.Model):\n    <strong>tablename</strong> = &apos;roles&apos;\n    id = db.Column(db.Integer, primary_key=True)\n    name = db.Column(db.String(64), unique=True)\n    users = db.relationship(&apos;User&apos;, backref=&apos;role&apos;, lazy=&apos;dynamic&apos;)</p>\n<div class=\"highlight\"><pre><code>def __repr__(self):\n    return &apos;&lt;Role %r&gt;&apos; % self.name\n\n\n</code></pre></div><p>class User(db.Model):\n    <strong>tablename</strong> = &apos;users&apos;\n    id = db.Column(db.Integer, primary_key=True)\n    username = db.Column(db.String(64), unique=True, index=True)\n    role_id = db.Column(db.Integer, db.ForeignKey(&apos;roles.id&apos;))\n    password_hash = db.Column(db.String(128))</p>\n<div class=\"highlight\"><pre><code>@property\ndef password(self):\n    raise AttributeError(&apos;password is not a readable attribute&apos;)\n\n@password.setter\ndef password(self, password):\n    self.password_hash = generate_password_hash(password)\n\ndef verify_password(self, password):\n    return check_password_hash(self.password_hash, password)\n\ndef __repr__(self):\n    return &apos;&lt;User %r&gt;&apos; % self.username</code></pre></div>\n    </div>\n</div><p>Content of test/test_user_model.py:</p>\n<div class=\"solution\" id=\"solution-8\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/8/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <p>import unittest\nfrom app.models import User</p>\n<p>class UserModelTestCase(unittest.TestCase):\n    def test_password_setter(self):\n        u = User(password = &apos;cat&apos;)\n        self.assertTrue(u.password_hash is not None)</p>\n<div class=\"highlight\"><pre><code>def test_no_password_getter(self):\n    u = User(password = &apos;cat&apos;)\n    with self.assertRaises(AttributeError):\n        u.password\n\ndef test_password_verification(self):\n    u = User(password = &apos;cat&apos;)\n    self.assertTrue(u.verify_password(&apos;cat&apos;))\n    self.assertFalse(u.verify_password(&apos;dog&apos;))\n\ndef test_password_salts_are_random(self):\n    u = User(password=&apos;cat&apos;)\n    u2 = User(password=&apos;cat&apos;)\n    self.assertTrue(u.password_hash != u2.password_hash)</code></pre></div>\n    </div>\n</div><p>Content of app/auth/__init.py:</p>\n<div class=\"solution\" id=\"solution-9\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/9/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <p>from flask import Blueprint</p>\n<p>auth = Blueprint(&apos;auth&apos;, <strong>name</strong>)</p>\n<p>from . import views</p>\n    </div>\n</div><p>Content of app/auth/views.py:</p>\n<div class=\"solution\" id=\"solution-10\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/10/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <p>from flask import render_template\nfrom . import auth</p>\n<p>@auth.route(&apos;/login&apos;)\ndef login():\n    return render_template(&apos;auth/login.html&apos;)</p>\n    </div>\n</div><p>Content of app/auth/forms.py:</p>\n<div class=\"solution\" id=\"solution-11\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/11/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <p>from flask_wtf import FlaskForm\nfrom wtforms import StringField, PasswordField, BooleanField, SubmitField\nfrom wtforms.validators import DataRequired, Length, Email</p>\n<p>class LoginForm(FlaskForm):\n    email = StringField(&apos;Email&apos;, validators=[DataRequired(), Length(1, 64),\n                                             Email()])\n    password = PasswordField(&apos;Password&apos;, validators=[DataRequired()])\n    remember_me = BooleanField(&apos;Keep me logged in&apos;)\n    submit = SubmitField(&apos;Log In&apos;)</p>\n    </div>\n</div><p>Content of app/templates/base.html:</p>\n<div class=\"solution\" id=\"solution-12\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/12/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"x\">&lt;ul class=&quot;nav navbar-nav navbar-right&quot;&gt;</span>\n<span class=\"x\">    </span><span class=\"cp\">{%</span> <span class=\"k\">if</span> <span class=\"nv\">current_user.is_authenticated</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"x\">    &lt;li&gt;&lt;a href=&quot;</span><span class=\"cp\">{{</span> <span class=\"nv\">url_for</span><span class=\"o\">(</span><span class=\"s1\">&apos;auth.logout&apos;</span><span class=\"o\">)</span> <span class=\"cp\">}}</span><span class=\"x\">&quot;&gt;Log Out&lt;/a&gt;&lt;/li&gt;</span>\n<span class=\"x\">    </span><span class=\"cp\">{%</span> <span class=\"k\">else</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"x\">    &lt;li&gt;&lt;a href=&quot;</span><span class=\"cp\">{{</span> <span class=\"nv\">url_for</span><span class=\"o\">(</span><span class=\"s1\">&apos;auth.login&apos;</span><span class=\"o\">)</span> <span class=\"cp\">}}</span><span class=\"x\">&quot;&gt;Log In&lt;/a&gt;&lt;/li&gt;</span>\n<span class=\"x\">    </span><span class=\"cp\">{%</span> <span class=\"k\">endif</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"x\">&lt;/ul&gt;</span>\n</pre></div>\n    </div>\n</div><p>Content of apps/templates/auth/login.html</p>\n<div class=\"solution\" id=\"solution-13\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/tieto-ostrava-jaro/beginners/tieto-final-assignment/index/solutions/13/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <div class=\"highlight\"><pre><span></span><span class=\"cp\">{%</span> <span class=\"k\">extends</span> <span class=\"s2\">&quot;base.html&quot;</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"cp\">{%</span> <span class=\"k\">import</span> <span class=\"s2\">&quot;bootstrap/wtf.html&quot;</span> <span class=\"k\">as</span> <span class=\"nv\">wtf</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n\n<span class=\"cp\">{%</span> <span class=\"k\">block</span> <span class=\"nv\">title</span> <span class=\"cp\">%}</span><span class=\"x\">Flasky - Login</span><span class=\"cp\">{%</span> <span class=\"k\">endblock</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n\n<span class=\"cp\">{%</span> <span class=\"k\">block</span> <span class=\"nv\">page_content</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"x\">&lt;div class=&quot;page-header&quot;&gt;</span>\n<span class=\"x\">    &lt;h1&gt;Login&lt;/h1&gt;</span>\n<span class=\"x\">&lt;/div&gt;</span>\n<span class=\"x\">&lt;div class=&quot;col-md-4&quot;&gt;</span>\n<span class=\"x\">    </span><span class=\"cp\">{{</span> <span class=\"nv\">wtf.quick_form</span><span class=\"o\">(</span><span class=\"nv\">form</span><span class=\"o\">)</span> <span class=\"cp\">}}</span><span class=\"x\"></span>\n<span class=\"x\">&lt;/div&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endblock</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n\n<span class=\"cp\">{%</span> <span class=\"k\">endfilter</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n\n<span class=\"cp\">{%</span> <span class=\"k\">filter</span> <span class=\"nf\">solution</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"cp\">{%</span> <span class=\"k\">extends</span> <span class=\"s2\">&quot;base.html&quot;</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n\n<span class=\"cp\">{%</span> <span class=\"k\">block</span> <span class=\"nv\">title</span> <span class=\"cp\">%}</span><span class=\"x\">Flasky</span><span class=\"cp\">{%</span> <span class=\"k\">endblock</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n\n<span class=\"cp\">{%</span> <span class=\"k\">block</span> <span class=\"nv\">page_content</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n<span class=\"x\">&lt;div class=&quot;page-header&quot;&gt;</span>\n<span class=\"x\">    &lt;h1&gt;Hello, </span><span class=\"cp\">{%</span> <span class=\"k\">if</span> <span class=\"nv\">current_user.is_authenticated</span> <span class=\"cp\">%}{{</span> <span class=\"nv\">current_user.username</span> <span class=\"cp\">}}{%</span> <span class=\"k\">else</span> <span class=\"cp\">%}</span><span class=\"x\">Stranger</span><span class=\"cp\">{%</span> <span class=\"k\">endif</span> <span class=\"cp\">%}</span><span class=\"x\">!&lt;/h1&gt;</span>\n<span class=\"x\">&lt;/div&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endblock</span> <span class=\"cp\">%}</span><span class=\"x\"></span>\n</pre></div>\n    </div>\n</div><ol>\n<li>Create test case for first function (route)</li>\n<li>Create first function</li>\n<li>Create basic application that will do following:<ul>\n<li>Allow users to log-in</li>\n<li>Use Templates</li>\n<li>Use   Web Forms</li>\n<li>Use SQLite or PostreSQL/MySQL</li>\n<li>Error Handling</li>\n<li>Use Bootstrap 4</li>\n<li>Use logging to the console and log file</li>\n<li>Email Support (sending notification about new sport activity)</li>\n<li>Allow user to add new kind of sport (like running)</li>\n<li>Allow user to add new activity and assign it to the sport type</li>\n<li>Display last ten activities on Dashboard</li>\n</ul>\n</li>\n</ol>\n\n\n        "
    }
  }
}