为了更好的为您提供服务, 云效 邀请您使用持续交付相关功能。云效结合ECS、EDAS等服务为您提供完备的发布、部署、测试全研发流程,大大提升您的研发效率. 了解更多>

提交 fdf5f8404f4c4cb5166305ac20bebae10a293157

作者 hillerliao
2 个父辈 195d65db ac54c7e0

fix conflict

1 1 .env
  2 +*.db
  3 +__pycache__
... ...
1 1 {
  2 +<<<<<<< HEAD
2 3 "python.pythonPath": "/home/lxf/miniconda3/bin/python"
  4 +=======
  5 + "python.pythonPath": "/home/lxf/.local/share/virtualenvs/watchlist-2jhocAV-/bin/python",
  6 + "python.linting.pylintEnabled": false,
  7 + "python.linting.enabled": false,
  8 + "python.linting.pep8Enabled": true
  9 +>>>>>>> ac54c7e0acf9a516d3b7884b1a41cf59eee6358d
3 10 }
4 11 \ No newline at end of file
... ...
... ... @@ -5,11 +5,14 @@ verify_ssl = true
5 5  
6 6 [dev-packages]
7 7 pylint = "*"
  8 +autopep8 = "*"
8 9  
9 10 [packages]
10 11 flask = "*"
11 12 python-dotenv = "*"
12   -faker = "*"
  13 +flask-sqlalchemy = "*"
  14 +flask-shell-ipython = "*"
  15 +flask-login = "*"
13 16  
14 17 [requires]
15 18 python_version = "3.7"
... ...
1 1 {
2 2 "_meta": {
3 3 "hash": {
4   - "sha256": "621b21fc59f3dab7492b01b74ce82efa40e438e375fb54d70562a5ccd78e8981"
  4 + "sha256": "4e0d397d76269e55a8bebb969fcc9bf765b033ee972d6353d7b7c468dbb57a96"
5 5 },
6 6 "pipfile-spec": 6,
7 7 "requires": {
... ... @@ -16,6 +16,13 @@
16 16 ]
17 17 },
18 18 "default": {
  19 + "backcall": {
  20 + "hashes": [
  21 + "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
  22 + "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
  23 + ],
  24 + "version": "==0.1.0"
  25 + },
19 26 "click": {
20 27 "hashes": [
21 28 "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
... ... @@ -23,21 +30,47 @@
23 30 ],
24 31 "version": "==7.0"
25 32 },
26   - "faker": {
  33 + "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52",
  34 + "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6"
  35 + ],
  36 + "index": "pypi",
  37 + "version": "==1.1.1"
  38 + },
27 39 "hashes": [
28   - "sha256:45cc9cca3de8beba5a2da3bd82a6e5544f53da1a702645c8485f682366c15026",
29   - "sha256:a6459ff518d1fc6ee2238a7209e6c899517872c7e1115510279033ffe6fe8ef3"
  40 + "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec"
30 41 ],
31 42 "index": "pypi",
32   - "version": "==2.0.2"
  43 + "version": "==0.4.1"
33 44 },
34   - "flask": {
  45 + "flask-shell-ipython": {
35 46 "hashes": [
36   - "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52",
37   - "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6"
  47 + "sha256:f212b4fad6831edf652799c719cd05fd0716edfaa5506eb41ff9ef09109890d3",
  48 + "sha256:fb3b390f4dc03d7a960c62c5b51ce4deca19ceff77e4db3d4670012adc529ebd"
38 49 ],
39 50 "index": "pypi",
40   - "version": "==1.1.1"
  51 + "version": "==0.4.1"
  52 + },
  53 + "flask-sqlalchemy": {
  54 + "hashes": [
  55 + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327",
  56 + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d"
  57 + ],
  58 + "index": "pypi",
  59 + "version": "==2.4.1"
  60 + },
  61 + "ipython": {
  62 + "hashes": [
  63 + "sha256:c4ab005921641e40a68e405e286e7a1fcc464497e14d81b6914b4fd95e5dee9b",
  64 + "sha256:dd76831f065f17bddd7eaa5c781f5ea32de5ef217592cf019e34043b56895aa1"
  65 + ],
  66 + "version": "==7.8.0"
  67 + },
  68 + "ipython-genutils": {
  69 + "hashes": [
  70 + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
  71 + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
  72 + ],
  73 + "version": "==0.2.0"
41 74 },
42 75 "itsdangerous": {
43 76 "hashes": [
... ... @@ -46,6 +79,13 @@
46 79 ],
47 80 "version": "==1.1.0"
48 81 },
  82 + "jedi": {
  83 + "hashes": [
  84 + "sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27",
  85 + "sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"
  86 + ],
  87 + "version": "==0.15.1"
  88 + },
49 89 "jinja2": {
50 90 "hashes": [
51 91 "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
... ... @@ -86,12 +126,49 @@
86 126 ],
87 127 "version": "==1.1.1"
88 128 },
89   - "python-dateutil": {
  129 + "parso": {
90 130 "hashes": [
91   - "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
92   - "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
  131 + "sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc",
  132 + "sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"
93 133 ],
94   - "version": "==2.8.0"
  134 + "version": "==0.5.1"
  135 + },
  136 + "pexpect": {
  137 + "hashes": [
  138 + "sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
  139 + "sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
  140 + ],
  141 + "markers": "sys_platform != 'win32'",
  142 + "version": "==4.7.0"
  143 + },
  144 + "pickleshare": {
  145 + "hashes": [
  146 + "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
  147 + "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
  148 + ],
  149 + "version": "==0.7.5"
  150 + },
  151 + "prompt-toolkit": {
  152 + "hashes": [
  153 + "sha256:46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4",
  154 + "sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31",
  155 + "sha256:f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db"
  156 + ],
  157 + "version": "==2.0.10"
  158 + },
  159 + "ptyprocess": {
  160 + "hashes": [
  161 + "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
  162 + "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
  163 + ],
  164 + "version": "==0.6.0"
  165 + },
  166 + "pygments": {
  167 + "hashes": [
  168 + "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127",
  169 + "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"
  170 + ],
  171 + "version": "==2.4.2"
95 172 },
96 173 "python-dotenv": {
97 174 "hashes": [
... ... @@ -108,12 +185,25 @@
108 185 ],
109 186 "version": "==1.12.0"
110 187 },
111   - "text-unidecode": {
  188 + "sqlalchemy": {
  189 + "hashes": [
  190 + "sha256:2f8ff566a4d3a92246d367f2e9cd6ed3edeef670dcd6dda6dfdc9efed88bcd80"
  191 + ],
  192 + "version": "==1.3.8"
  193 + },
  194 + "traitlets": {
  195 + "hashes": [
  196 + "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44",
  197 + "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"
  198 + ],
  199 + "version": "==4.3.3"
  200 + },
  201 + "wcwidth": {
112 202 "hashes": [
113   - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
114   - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
  203 + "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
  204 + "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
115 205 ],
116   - "version": "==1.3"
  206 + "version": "==0.1.7"
117 207 },
118 208 "werkzeug": {
119 209 "hashes": [
... ... @@ -126,18 +216,17 @@
126 216 "develop": {
127 217 "astroid": {
128 218 "hashes": [
129   - "sha256:9b3f17b0550f82e28a6776a4e5222441f48e523b0773df4bc505bb6b7c2093b7",
130   - "sha256:c7e2e5773d87ccc00d01c273e439386f4d6d63cce61317a79ccce5880162f9fb"
  219 + "sha256:98c665ad84d10b18318c5ab7c3d203fe11714cbad2a4aef4f44651f415392754",
  220 + "sha256:b7546ffdedbf7abcfbff93cd1de9e9980b1ef744852689decc5aeada324238c6"
131 221 ],
132   - "version": "==2.3.0"
  222 + "version": "==2.3.1"
133 223 },
134   - "colorama": {
  224 + "autopep8": {
135 225 "hashes": [
136   - "sha256:05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d",
137   - "sha256:f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"
  226 + "sha256:4d8eec30cc81bc5617dbf1218201d770dc35629363547f17577c61683ccfb3ee"
138 227 ],
139   - "markers": "sys_platform == 'win32'",
140   - "version": "==0.4.1"
  228 + "index": "pypi",
  229 + "version": "==1.4.4"
141 230 },
142 231 "isort": {
143 232 "hashes": [
... ... @@ -176,13 +265,20 @@
176 265 ],
177 266 "version": "==0.6.1"
178 267 },
  268 + "pycodestyle": {
  269 + "hashes": [
  270 + "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
  271 + "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
  272 + ],
  273 + "version": "==2.5.0"
  274 + },
179 275 "pylint": {
180 276 "hashes": [
181   - "sha256:2d64b4b8fa044480b1a49d47535da53557f8f426b8c5bd6a23beb65e905101a1",
182   - "sha256:6cbd124a1a5ed1fd3f3fed4178a6c2ba166862ea0dac6ab2ff8d9f0998b13e5c"
  277 + "sha256:7edbae11476c2182708063ac387a8f97c760d9cfe36a5ede0ca996f90cf346c8",
  278 + "sha256:844ce067788028c1a35086a5c66bc5e599ddd851841c41d6ee1623b36774d9f2"
183 279 ],
184 280 "index": "pypi",
185   - "version": "==2.4.1"
  281 + "version": "==2.4.2"
186 282 },
187 283 "six": {
188 284 "hashes": [
... ... @@ -209,7 +305,7 @@
209 305 "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
210 306 "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
211 307 ],
212   - "markers": "implementation_name == 'cpython' and python_version >= '3.7' and python_version < '3.8'",
  308 + "markers": "implementation_name == 'cpython' and python_version < '3.8'",
213 309 "version": "==1.4.0"
214 310 },
215 311 "wrapt": {
... ...
1   -from flask import Flask, url_for, render_template
  1 +import os
  2 +import sys
  3 +
  4 +import click
  5 +from flask import Flask, render_template, request, redirect, url_for, flash
  6 +from flask_sqlalchemy import SQLAlchemy
  7 +from werkzeug.security import generate_password_hash, check_password_hash
  8 +from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
  9 +
  10 +WIN = sys.platform.startswith('win')
  11 +if WIN:
  12 + prefix = 'sqlite:///'
  13 +else:
  14 + prefix = 'sqlite:////'
  15 +
  16 +
2 17 app = Flask(__name__)
3 18  
4   -@app.route('/')
  19 +app.config['SQLALCHEMY_DATABASE_URI'] = prefix + \
  20 + os.path.join(app.root_path, 'data.db')
  21 +app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
  22 +app.config['SECRET_KEY'] = 'dev'
  23 +
  24 +db = SQLAlchemy(app)
  25 +
  26 +login_manager = LoginManager(app)
  27 +login_manager.login_view = 'login'
  28 +
  29 +
  30 +class User(db.Model, UserMixin):
  31 + id = db.Column(db.Integer, primary_key=True)
  32 + name = db.Column(db.String(20))
  33 + username = db.Column(db.String(20))
  34 + password_hash = db.Column(db.String(128))
  35 +
  36 + def set_passord(self, password):
  37 + self.password_hash = generate_password_hash(password)
  38 +
  39 + def validate_password(self, password):
  40 + return check_password_hash(self.password_hash, password)
  41 +
  42 +
  43 +
  44 +class Movie(db.Model):
  45 + id = db.Column(db.Integer, primary_key=True)
  46 + title = db.Column(db.String(60))
  47 + year = db.Column(db.String(4))
  48 +
  49 +
  50 +@app.cli.command()
  51 +@click.option('--drop', is_flag=True, help='Create after drop.')
  52 +def initdb(drop):
  53 + """Initialize the database"""
  54 + if drop:
  55 + db.drop_all()
  56 + db.create_all()
  57 + click.echo('Initialized database')
  58 +
  59 +
  60 +@app.cli.command()
  61 +def forge():
  62 + """Generate fake data."""
  63 + db.create_all()
  64 +
  65 + name = 'Grey Li'
  66 + movies = [
  67 + {'title': 'My Neighbor Totoro', 'year': '1988'},
  68 + {'title': 'Dead Poets Society', 'year': '1989'},
  69 + {'title': 'A Perfect World', 'year': '1993'},
  70 + {'title': 'Leon', 'year': '1994'},
  71 + {'title': 'Mahjong', 'year': '1996'},
  72 + {'title': 'Swallowtail Butterfly', 'year': '1996'},
  73 + {'title': 'King of Comedy', 'year': '1999'},
  74 + {'title': 'Devils on the Doorstep', 'year': '1999'},
  75 + {'title': 'WALL-E', 'year': '2008'},
  76 + {'title': 'The Pork of Music', 'year': '2012'},
  77 + ]
  78 +
  79 + user = User(name=name)
  80 + db.session.add(user)
  81 + for m in movies:
  82 + movie = Movie(title=m['title'], year=m['year'])
  83 + db.session.add(movie)
  84 +
  85 + db.session.commit()
  86 + click.echo('Done.')
  87 +
  88 +@app.cli.command()
  89 +@click.option('--username', prompt=True, help='The username used to login.')
  90 +@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True, help='The password used to login.')
  91 +def admin(username, password):
  92 + """Create user."""
  93 + db.create_all()
  94 +
  95 + user = User.query.first()
  96 + if user is not None:
  97 + click.echo('Updating user...')
  98 + user.username = username
  99 + user.set_passord(password)
  100 + else:
  101 + click.echo('Creating user...')
  102 + user = User(username=username, name='Admin')
  103 + user.set_passord(password)
  104 + db.session.add(user)
  105 +
  106 + db.session.commit()
  107 + click.echo('Done.')
  108 +
  109 +@app.context_processor
  110 +def inject_user():
  111 + user = User.query.first()
  112 + return dict(user=user)
  113 +
  114 +
  115 +@app.errorhandler(404)
  116 +def page_not_found(e):
  117 + return render_template('404.html'), 404
  118 +
  119 +@app.errorhandler(400)
  120 +def bad_request(e):
  121 + return render_template('400.html'), 400
  122 +
  123 +@app.errorhandler(500)
  124 +def internal_server_error(e):
  125 + return render_template('500.html'), 500
  126 +
  127 +@login_manager.user_loader
  128 +def load_user(user_id):
  129 + user = User.query.get(int(user_id))
  130 + return user
  131 +
  132 +@app.route('/', methods=['GET', 'POST'])
5 133 def index():
6   - return render_template('index.html', name=name, movies=movies)
7   -
8   -@app.route('/user/<name>')
9   -def user_page(name):
10   - return 'User: %s' % name
11   -
12   -@app.route('/test')
13   -def test_url_for():
14   - print(url_for('hello'))
15   - print(url_for('user_page', name='李雪'))
16   - print(url_for('hello', num=2))
17   -
18   - return 'Test page'
19   -
20   -
21   -name = 'Hiller Liao'
22   -
23   -movies = [
24   - {'title': 'My Neighbor Totoro', 'year': '1988'},
25   - {'title': 'Dead Poets Society', 'year': '1989'},
26   - {'title': 'A Perfect World', 'year': '1993'},
27   - {'title': 'Leon', 'year': '1994'},
28   - {'title': 'Mahjong', 'year': '1996'},
29   - {'title': 'Swallowtail Butterfly', 'year': '1996'},
30   - {'title': 'King of Comedy', 'year': '1999'},
31   - {'title': 'Devils on the Doorstep', 'year': '1999'},
32   - {'title': 'WALL-E', 'year': '2008'},
33   - {'title': 'The Pork of Music', 'year': '2012'},
34   -]
35 134 \ No newline at end of file
  135 + if request.method == 'POST':
  136 + if not current_user.is_authenticated:
  137 + return redirect(url_for('index'))
  138 + title = request.form.get('title')
  139 + year = request.form.get('year')
  140 + if not title or not year or len(year) > 4 or len(title) > 60:
  141 + flash('Invalid input.')
  142 + return redirect(url_for('index'))
  143 + movie = Movie(title=title, year=year)
  144 + db.session.add(movie)
  145 + db.session.commit()
  146 + flash('Item created.')
  147 + return redirect(url_for('index'))
  148 +
  149 + movies = Movie.query.all()
  150 + return render_template('index.html', movies=movies)
  151 +
  152 +@app.route('/movie/edit/<int:movie_id>', methods=['GET', 'POST'])
  153 +@login_required # 登录保护
  154 +def edit(movie_id):
  155 + movie = Movie.query.get_or_404(movie_id)
  156 +
  157 + if request.method == 'POST':
  158 + title = request.form['title']
  159 + year = request.form['year']
  160 +
  161 + if not title or not year or len(year) > 4 or len(title) > 60:
  162 + flash('Invalid input.')
  163 + return redirect(url_for('edit', movie_id=movie_id))
  164 +
  165 + movie.title = title
  166 + movie.year = year
  167 + db.session.commit()
  168 + flash('Item updated.')
  169 + return redirect(url_for('index'))
  170 +
  171 + return render_template('edit.html', movie=movie)
  172 +
  173 +@app.route('/movie/delete/<int:movie_id>', methods=['POST'])
  174 +@login_required
  175 +def delete(movie_id):
  176 + movie = Movie.query.get_or_404(movie_id)
  177 + db.session.delete(movie)
  178 + db.session.commit()
  179 + flash('Item deleted.')
  180 + return redirect(url_for('index'))
  181 +
  182 +@app.route('/login', methods=['GET', 'POST'])
  183 +def login():
  184 + if request.method == 'POST':
  185 + username = request.form['username']
  186 + password = request.form['password']
  187 +
  188 + if not username or not password:
  189 + flash('Invalid input.')
  190 + return redirect(url_for('login'))
  191 +
  192 + user = User.query.first()
  193 + if username == user.username and user.validate_password(password):
  194 + login_user(user)
  195 + flash('Login success.')
  196 + return redirect(url_for('index'))
  197 +
  198 + flash('Invalid username or password.')
  199 + return redirect(url_for('login'))
  200 +
  201 + return render_template('login.html')
  202 +
  203 +@app.route('/logout')
  204 +@login_required
  205 +def logout():
  206 + logout_user()
  207 + flash('Goodbye.')
  208 + return redirect(url_for('index'))
  209 +
  210 +@app.route('/settings', methods=['GET', 'POST'])
  211 +@login_required
  212 +def settings():
  213 + if request.method == 'POST':
  214 + name = request.form['name']
  215 +
  216 + if not name or len(name) > 20:
  217 + flash('Invalid input.')
  218 + return redirect(url_for('settings'))
  219 +
  220 + current_user.name = name
  221 + db.session.commit()
  222 + flash('Settings updated.')
  223 + return redirect(url_for('index'))
  224 +
  225 + return render_template('settings.html')
36 226 \ No newline at end of file
... ...
  1 +body {
  2 + margin: auto;
  3 + max-width: 580px;
  4 + font-size: 14px;
  5 + font-family: Arial, Helvetica, sans-serif;
  6 +}
  7 +
  8 +footer {
  9 + color: #888;
  10 + margin-top: 15px;
  11 + text-align: center;
  12 + padding: 10px;
  13 +}
  14 +
  15 +.avatar {
  16 + width: 40px;
  17 +}
  18 +
  19 +.movie-list {
  20 + list-style-type: none;
  21 + padding: 0;
  22 + margin-bottom: 10px;
  23 + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
  24 +}
  25 +
  26 +.movie-list li {
  27 + padding: 12px 14px;
  28 + border-bottom: 1px solid #ddd;
  29 +}
  30 +
  31 +.movie-list li:last-child {
  32 + border-bottom: none;
  33 +}
  34 +
  35 +.movie-list li:hover {
  36 + background-color: #f8f9fa;
  37 +}
  38 +
  39 +.totoro {
  40 + display: block;
  41 + margin: 0 auto;
  42 + height: 100px;
  43 +}
  44 +
  45 +nav ul {
  46 + list-style-type: none;
  47 + margin: 0;
  48 + padding: 0;
  49 + overflow: hidden;
  50 + background-color: #333;
  51 +}
  52 +
  53 +nav li {
  54 + float: left;
  55 +}
  56 +
  57 +nav li a {
  58 + display: block;
  59 + color: white;
  60 + text-align: center;
  61 + padding: 8px 12px;
  62 + text-decoration: none;
  63 +}
  64 +
  65 +nav li a:hover {
  66 + background-color: #111;
  67 +}
  68 +
  69 +.float-right {
  70 + float: right;
  71 +}
  72 +
  73 +.imdb {
  74 + font-size: 12px;
  75 + font-weight: bold;
  76 + color: black;
  77 + text-decoration: none;
  78 + background: #F5C518;
  79 + border-radius: 5px;
  80 + padding: 3px 5px;
  81 +}
  82 +
  83 +input[type=submit] {
  84 + font-family: inherit;
  85 +}
  86 +
  87 +input[type=text] {
  88 + border: 1px solid #ddd;
  89 +}
  90 +
  91 +input[name=year] {
  92 + width: 50px;
  93 +}
  94 +
  95 +.btn {
  96 + font-size: 12px;
  97 + padding: 3px 5px;
  98 + text-decoration: none;
  99 + cursor: pointer;
  100 + background-color: white;
  101 + color: black;
  102 + border: 1px solid #555555;
  103 + border-radius: 5px;
  104 +}
  105 +
  106 +.btn:hover {
  107 + text-decoration: none;
  108 + background-color: black;
  109 + color: white;
  110 + border: 1px solid black;
  111 +}
  112 +
  113 +.alert {
  114 + position: relative;
  115 + padding: 7px;
  116 + margin: 7px 0;
  117 + border: 1px solid transparent;
  118 + color: #004085;
  119 + background-color: #cce5ff;
  120 + border-color: #b8daff;
  121 + border-radius: 5px;
  122 +}
  123 +
  124 +.inline-form {
  125 + display: inline;
  126 +}
0 127 \ No newline at end of file
... ...
  1 +{% extends 'base.html' %}
  2 +
  3 +{% block content %}
  4 +<ul class="movie-list">
  5 + <li>
  6 + Bad Request - 400
  7 + <span class="float-right">
  8 + <a href="{{ url_for('index') }}">Go Back</a>
  9 + </span>
  10 + </li>
  11 +</ul>
  12 +{% endblock %}
0 13 \ No newline at end of file
... ...
  1 +{% extends 'base.html' %}
  2 +
  3 +{% block content %}
  4 +
  5 +<ul class="movie-list">
  6 + <li>
  7 + Page Not Found - 404
  8 + <span class="float-right">
  9 + <a href="{{ url_for('index') }}">Go Back</a>
  10 + </span>
  11 + </li>
  12 +</ul>
  13 +
  14 +{% endblock %}
0 15 \ No newline at end of file
... ...
  1 +{% extends 'base.html' %}
  2 +
  3 +{% block content %}
  4 +<ul class="movie-list">
  5 + <li>
  6 + Bad Request - 500
  7 + <span class="float-right">
  8 + <a href="{{ url_for('index') }}">Go Back</a>
  9 + </span>
  10 +
  11 + </li>
  12 +</ul>
  13 +{% endblock %}
0 14 \ No newline at end of file
... ...
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 +
  4 +<head>
  5 + {% block head %}
  6 + <meta charset="utf-8">
  7 + <meta name="viewport" content="width=device-width,initial-scale=1.0">
  8 + <title>{{ user.name }}'s Watchlist</title>
  9 + <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
  10 + <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" type="text/css">
  11 + {% endblock %}
  12 +</head>
  13 +
  14 +<body>
  15 + {% for message in get_flashed_messages() %}
  16 + <div class="alert">{{ message }}</div>
  17 + {% endfor %}
  18 + <h2>
  19 + <img alt="Avatar" class="avatar" src="{{ url_for('static', filename='images/avatar.png') }}">
  20 + {{ user.name }}'s Watchlist'
  21 + </h2>
  22 + <nav>
  23 + <ul>
  24 + <li><a href="{{ url_for('index')}}">Home</a></li>
  25 + {% if current_user.is_authenticated %}
  26 + <li><a href="{{ url_for('settings')}}">Settings</a></li>
  27 + <li><a href="{{ url_for('logout')}}">Logout</a></li>
  28 + {% else %}
  29 + <li><a href="{{ url_for('login') }}">Login</a></li>
  30 + {% endif %}
  31 + </ul>
  32 + </nav>
  33 + {% block content %}{% endblock%}
  34 + <footer>
  35 + <small>&copy; 2018 <a href="http://www.helloflask.com/tutorial">HellFlask</a></small>
  36 + </footer>
  37 +</body>
  38 +
  39 +</html>
0 40 \ No newline at end of file
... ...
  1 +{% extends 'base.html' %}
  2 +
  3 +{% block content %}
  4 +<h3>Edit item</h3>
  5 +<form method="post">
  6 + Name <input type="text" name="title" required autocomplete="off" value="{{ movie.title }}">
  7 + Year <input type="text" name="year" required autocomplete="off" value="{{ movie.year }}">
  8 + <input type="submit" name="submit" class="btn" value="Upate">
  9 +
  10 +</form>
  11 +{% endblock %}
0 12 \ No newline at end of file
... ...
... ... @@ -18,4 +18,36 @@
18 18 <small>&copy; 2018 <a href="http://helloflask.com/tutorial">HelloFlask</a></small>
19 19 </footer>
20 20 </body>
21   -</html>
22 21 \ No newline at end of file
  22 +</html>
  23 +{% extends 'base.html' %}
  24 +
  25 +{% block content %}
  26 +<p>{{ movies|length }} Titles</p>
  27 +
  28 +{% if current_user.is_authenticated %}
  29 +<form method="post">
  30 + Name <input type="text" name="title" autocomplete="off" required>
  31 + Year <input type="text" name="year" autocomplete="off" required>
  32 + <input type="submit" name="submit" class="btn" value="Add">
  33 +</form>
  34 +{% endif %}
  35 +
  36 +<ul class="movie-list">
  37 + {% for movie in movies %}
  38 + <li>{{ movie.title }} - {{ movie.year }}
  39 + <span class="float-right">
  40 + {% if current_user.is_authenticated %}
  41 + <a class="btn" href="{{ url_for('edit', movie_id=movie.id) }}">Edit</a>
  42 + <form class="inline-form" method="post" action="{{ url_for('delete', movie_id=movie.id) }}">
  43 + <input type="submit" name="delete" value="Delete" class="btn" onclick="return confirm('Are you sure?')">
  44 + </form>
  45 + {% endif %}
  46 + <a class="imdb" href="https://www.imdb.com/find?q={{ movie.title }}" target="_blank"
  47 + title="Find this movie on IMDb">IMDb</a>
  48 + </span>
  49 + </li>
  50 + {% endfor %}
  51 +</ul>
  52 +
  53 +<img alt="Walking Totoro" class="totoro" src="{{ url_for('static', filename='images/totoro.gif') }}">
  54 +{% endblock %}
... ...
  1 +{% extends 'base.html' %}
  2 +
  3 +{% block content %}
  4 +<h3>Login</h3>
  5 +<form method="post">
  6 + Username<br>
  7 + <input type="text" name="username" required>
  8 + <p>
  9 + Password<br>
  10 + <input type="text" name="password" required>
  11 + </p>
  12 + <input type="submit" name="submit" value="submit" class="btn">
  13 +</form>
  14 +{% endblock %}
0 15 \ No newline at end of file
... ...
  1 +{% extends 'base.html' %}
  2 +
  3 +{% block content %}
  4 +<h3>Settings</h3>
  5 +<form>
  6 + <input type="text" autocomplete="off" name="name" required value="{{ current_user.name }}">
  7 + <input type="submit" class="btn" name="submit" value="Save">
  8 +</form>
  9 +{% endblock %}
0 10 \ No newline at end of file
... ...