├── Lesson01 ├── Activity01 │ └── Activity01.postman_collection.json ├── Activity02 │ ├── Activity02.postman_collection.json │ ├── basic-api │ │ ├── app.py │ │ └── requirements.txt │ └── cmd.txt ├── Exercise01 │ ├── app.py │ └── requirements.txt ├── Exercise02 │ ├── app.py │ └── requirements.txt ├── Exercise03 │ └── cmd.txt └── Exercise04 │ └── Exercise04.postman_collection.json ├── Lesson02 ├── Activity03 │ └── Activity03.postman_collection.json ├── Activity04 │ ├── Activity04.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── models │ │ ├── __init__.py │ │ └── recipe.py │ │ ├── requirements.txt │ │ └── resources │ │ ├── __init__.py │ │ └── recipe.py ├── Exercise05 │ └── requirements.txt ├── Exercise06 │ ├── models │ │ ├── __init__.py │ │ └── recipe.py │ └── requirements.txt ├── Exercise07 │ ├── models │ │ ├── __init__.py │ │ └── recipe.py │ ├── requirements.txt │ └── resources │ │ ├── __init__.py │ │ └── recipe.py ├── Exercise08 │ ├── models │ │ ├── __init__.py │ │ └── recipe.py │ ├── requirements.txt │ └── resources │ │ ├── __init__.py │ │ └── recipe.py ├── Exercise09 │ ├── models │ │ ├── __init__.py │ │ └── recipe.py │ ├── requirements.txt │ └── resources │ │ ├── __init__.py │ │ └── recipe.py ├── Exercise10 │ ├── app.py │ ├── models │ │ ├── __init__.py │ │ └── recipe.py │ ├── requirements.txt │ └── resources │ │ ├── __init__.py │ │ └── recipe.py ├── Exercise11 │ └── cmd.txt ├── Exercise12 │ └── cmd.txt ├── Exercise13 │ └── cmd.txt ├── Exercise14 │ └── cmd.txt ├── Exercise15 │ └── cmd.txt ├── Exercise16 │ └── cmd.txt └── Exercise17 │ └── cmd.txt ├── Lesson03 ├── Activity05 │ └── script.txt ├── Activity06 │ ├── cmd.txt │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ └── a1f16c58bf8b_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ └── utils.py ├── Exercise19 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ └── resources │ │ ├── __init__.py │ │ └── recipe.py ├── Exercise20 │ └── cmd.txt ├── Exercise21 │ └── script.txt ├── Exercise22 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ └── utils.py └── Exercise23 │ └── Exercise23.postman_collection.json ├── Lesson04 ├── Activity07 │ ├── Activity07.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ └── utils.py ├── Exercise24 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ └── utils.py ├── Exercise25 │ └── Exercise25.postman_collection.json ├── Exercise26 │ ├── Exercise26.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ └── utils.py ├── Exercise27 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ └── utils.py ├── Exercise28 │ └── Exercise28.postman_collection.json ├── Exercise29 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ └── utils.py ├── Exercise30 │ └── Exercise30.postman_collection.json ├── Exercise31 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ └── utils.py └── Exercise32 │ └── Exercise32.postman_collection.json ├── Lesson05 ├── Activity08 │ ├── Activity08.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ │ └── utils.py ├── Exercise33 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ └── user.py │ └── utils.py ├── Exercise34 │ └── Exercise34.postman_collection.json ├── Exercise35 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ └── utils.py ├── Exercise36 │ └── Exercise36.postman_collection.json ├── Exercise37 │ ├── Exercise37.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ │ └── utils.py ├── Exercise38 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ └── utils.py └── Exercise39 │ └── Exercise39.postman_collection.json ├── Lesson06 ├── Activity09 │ └── Activity09.postman_collection.json ├── Activity10 │ ├── Activity10.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ │ └── utils.py ├── Exercise41 │ ├── script.txt │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ │ └── utils.py ├── Exercise42 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ └── utils.py ├── Exercise43 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ └── 6089a861042f_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ └── utils.py └── Exercise44 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ ├── README │ ├── alembic.ini │ ├── env.py │ ├── script.py.mako │ └── versions │ │ └── 6089a861042f_.py │ ├── models │ ├── __init__.py │ ├── recipe.py │ └── user.py │ ├── requirements.txt │ ├── resources │ ├── __init__.py │ ├── recipe.py │ ├── token.py │ └── user.py │ ├── schemas │ ├── recipe.py │ └── user.py │ └── utils.py ├── Lesson07 ├── Activity11 │ ├── script.txt │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ │ └── utils.py ├── Activity12 │ └── Activity12.postman_collection.json ├── Exercise45 │ ├── script.txt │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ └── 824c92be2806_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ │ └── utils.py ├── Exercise46 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ └── 824c92be2806_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ └── default-avatar.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise47 │ └── Exercise47.postman_collection.json ├── Exercise48 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ └── 824c92be2806_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ └── default-avatar.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py └── Exercise49 │ └── Exercise49.postman_collection.json ├── Lesson08 ├── Activity13 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Activity14 │ └── Activity14.postman_collection.json ├── Activity15 │ ├── Activity15.postman_collection.json │ ├── cmd.txt │ ├── script.txt │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ │ └── utils.py ├── Exercise50 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise51 │ ├── Exercise51.postman_collection.json │ └── script.txt ├── Exercise52 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise53 │ └── Exercise53.postman_collection.json ├── Exercise54 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py └── Exercise55 │ └── Exercise55.postman_collection.json ├── Lesson09 ├── Activity16 │ └── Activity16.postman_collection.json ├── Activity17 │ ├── Activity17.postman_collection.json │ └── smilecook │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ │ └── utils.py ├── Exercise56 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise57 │ └── Exercise57.postman_collection.json ├── Exercise58 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise59 │ └── Exercise59.postman_collection.json ├── Exercise60 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise61 │ └── Exercise61.postman_collection.json └── Exercise62 │ ├── Exercise62.postman_collection.json │ └── smilecook │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ ├── README │ ├── alembic.ini │ ├── env.py │ ├── script.py.mako │ └── versions │ │ ├── 6089a861042f_.py │ │ ├── 7d7d6df19aa8_.py │ │ ├── 824c92be2806_.py │ │ └── e047704cf548_.py │ ├── models │ ├── __init__.py │ ├── recipe.py │ └── user.py │ ├── requirements.txt │ ├── resources │ ├── __init__.py │ ├── recipe.py │ ├── token.py │ └── user.py │ ├── schemas │ ├── pagination.py │ ├── recipe.py │ └── user.py │ ├── static │ └── images │ │ └── assets │ │ ├── default-avatar.jpg │ │ └── default-recipe-cover.jpg │ ├── templates │ └── email │ │ └── confirmation.html │ └── utils.py ├── Lesson10 ├── Activity18 │ └── Activity18.postman_collection.json ├── Exercise63 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise64 │ ├── app.py │ ├── config.py │ ├── extensions.py │ ├── mailgun.py │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ ├── requirements.txt │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ └── utils.py ├── Exercise67 │ └── script.txt ├── Exercise68 │ ├── cmd.txt │ └── smilecook │ │ ├── Procfile │ │ ├── app.py │ │ ├── config.py │ │ ├── extensions.py │ │ ├── mailgun.py │ │ ├── main.py │ │ ├── migrations │ │ ├── README │ │ ├── alembic.ini │ │ ├── env.py │ │ ├── script.py.mako │ │ └── versions │ │ │ ├── 6089a861042f_.py │ │ │ ├── 7d7d6df19aa8_.py │ │ │ ├── 824c92be2806_.py │ │ │ └── e047704cf548_.py │ │ ├── models │ │ ├── __init__.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── requirements.txt │ │ ├── resources │ │ ├── __init__.py │ │ ├── recipe.py │ │ ├── token.py │ │ └── user.py │ │ ├── schemas │ │ ├── pagination.py │ │ ├── recipe.py │ │ └── user.py │ │ ├── static │ │ └── images │ │ │ └── assets │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ │ ├── templates │ │ └── email │ │ │ └── confirmation.html │ │ └── utils.py ├── Exercise70 │ └── Exercise70.postman_collection.json └── Frontend │ ├── cmd.txt │ └── smilecook-vuejs │ ├── .browserslistrc │ ├── .editorconfig │ ├── .eslintrc.js │ ├── Procfile │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── favicon.ico │ └── index.html │ ├── server.js │ ├── src │ ├── App.ts │ ├── App.vue │ ├── api.ts │ ├── assets │ │ ├── default-avatar.jpg │ │ ├── default-recipe-cover.jpg │ │ └── logo.png │ ├── auth.ts │ ├── components │ │ ├── AlertComponent.vue │ │ ├── AuthorComponent.vue │ │ ├── AuthorFilterComponent.vue │ │ ├── DialogComponent.vue │ │ ├── FilterComponent.vue │ │ ├── OperatorComponent.vue │ │ ├── ProfileComponent.vue │ │ ├── RecipeItemComponent.vue │ │ └── UploadAvatarComponent.vue │ ├── main.ts │ ├── request-handler.ts │ ├── response.ts │ ├── router.ts │ ├── shims-tsx.d.ts │ ├── shims-vue.d.ts │ ├── static │ │ └── images │ │ │ ├── default-avatar.jpg │ │ │ └── default-recipe-cover.jpg │ ├── store │ │ ├── aciton-types.ts │ │ ├── index.ts │ │ ├── modules │ │ │ ├── recipe.ts │ │ │ └── user.ts │ │ └── mutation-types.ts │ ├── utils.ts │ ├── validation.ts │ └── views │ │ ├── account │ │ ├── login-view.ts │ │ ├── login-view.vue │ │ ├── register-view.ts │ │ └── register-view.vue │ │ ├── author │ │ ├── my-recipe-list-view.ts │ │ ├── my-recipe-list-view.vue │ │ ├── recipe-panel-view.ts │ │ └── recipe-panel-view.vue │ │ ├── general │ │ ├── internal-server-error-view.ts │ │ ├── internal-server-error-view.vue │ │ ├── not-found-view.ts │ │ └── not-found-view.vue │ │ ├── home.ts │ │ ├── home.vue │ │ └── recipe │ │ ├── author-recipe-list-view.ts │ │ ├── author-recipe-list-view.vue │ │ ├── recipe-detail-view.ts │ │ ├── recipe-detail-view.vue │ │ ├── recipe-list-view.ts │ │ └── recipe-list-view.vue │ ├── tsconfig.json │ ├── vue.config.js │ └── yarn.lock └── README.md /Lesson01/Activity02/basic-api/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson01/Activity02/cmd.txt: -------------------------------------------------------------------------------- 1 | http DELETE localhost:5000/recipes/1 2 | curl -i -X DELETE localhost:5000/recipes/1 -------------------------------------------------------------------------------- /Lesson01/Exercise01/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | 3 | app = Flask(__name__) 4 | 5 | 6 | @app.route("/") 7 | def hello(): 8 | return "Hello World!" 9 | 10 | 11 | if __name__ == "__main__": 12 | app.run() -------------------------------------------------------------------------------- /Lesson01/Exercise01/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson01/Exercise02/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson01/Exercise03/cmd.txt: -------------------------------------------------------------------------------- 1 | http GET localhost:5000/recipes 2 | curl -i -X GET localhost:5000/recipes 3 | 4 | http POST localhost:5000/recipes name="Cheese Pizza" description="This is a lovely cheese pizza" 5 | curl -i -X POST localhost:5000/recipes -H "Content-Type: application/json" -d '{"name":"Cheese Pizza", "description":"This is a lovely cheese pizza"}' 6 | 7 | http GET localhost:5000/recipes 8 | curl -i -X GET localhost:5000/recipes 9 | 10 | http PUT localhost:5000/recipes/3 name="Lovely Cheese Pizza" description="This is a lovely cheese pizza recipe." 11 | curl -i -X PUT localhost:5000/recipes/3 -H "Content-Type: application/json" -d '{"name":"Lovely Cheese Pizza", "description":"This is a lovely cheese pizza recipe."}' 12 | 13 | http GET localhost:5000/recipes/3 14 | curl -i -X GET localhost:5000/recipes/3 15 | 16 | http GET localhost:5000/recipes/101 17 | curl -i -X GET localhost:5000/recipes/101 -------------------------------------------------------------------------------- /Lesson02/Activity04/smilecook/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_restful import Api 3 | 4 | from resources.recipe import RecipeListResource, RecipeResource, RecipePublishResource 5 | 6 | app = Flask(__name__) 7 | api = Api(app) 8 | 9 | api.add_resource(RecipeListResource, '/recipes') 10 | api.add_resource(RecipeResource, '/recipes/') 11 | api.add_resource(RecipePublishResource, '/recipes//publish') 12 | 13 | if __name__ == '__main__': 14 | app.run(port=5000, debug=True) 15 | -------------------------------------------------------------------------------- /Lesson02/Activity04/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Activity04/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson02/Activity04/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson02/Activity04/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Activity04/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise05/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | 5 | -------------------------------------------------------------------------------- /Lesson02/Exercise06/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise06/models/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise06/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | 5 | -------------------------------------------------------------------------------- /Lesson02/Exercise07/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise07/models/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise07/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | 5 | -------------------------------------------------------------------------------- /Lesson02/Exercise07/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise07/resources/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise08/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise08/models/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise08/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson02/Exercise08/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise08/resources/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise09/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise09/models/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise09/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson02/Exercise09/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise09/resources/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise10/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_restful import Api 3 | 4 | from resources.recipe import RecipeListResource, RecipeResource, RecipePublishResource 5 | 6 | app = Flask(__name__) 7 | api = Api(app) 8 | 9 | api.add_resource(RecipeListResource, '/recipes') 10 | api.add_resource(RecipeResource, '/recipes/') 11 | api.add_resource(RecipePublishResource, '/recipes//publish') 12 | 13 | if __name__ == '__main__': 14 | app.run(port=5000, debug=True) 15 | -------------------------------------------------------------------------------- /Lesson02/Exercise10/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise10/models/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise10/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 -------------------------------------------------------------------------------- /Lesson02/Exercise10/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson02/Exercise10/resources/__init__.py -------------------------------------------------------------------------------- /Lesson02/Exercise11/cmd.txt: -------------------------------------------------------------------------------- 1 | http POST localhost:5000/recipes name="Cheese Pizza" description="This is a lovely cheese pizza" num_of_servings:=2 cook_time:=30 directions="This is how you make it" 2 | curl -i -X POST localhost:5000/recipes -H "Content-Type: application/json" -d '{"name":"Cheese Pizza", "description":"This is a lovely cheese pizza", "num_of_servings":2, "cook_time":30, "directions":"This is how you make it" }' -------------------------------------------------------------------------------- /Lesson02/Exercise12/cmd.txt: -------------------------------------------------------------------------------- 1 | http POST localhost:5000/recipes name="Tomato Pasta" description="This is a lovely tomato pasta recipe" num_of_servings:=3 cook_time:=20 directions="This is how you make it" 2 | curl -i -X POST localhost:5000/recipes -H "Content-Type: application/json" -d '{"name":"Tomato Pasta", "description":"This is a lovely tomato pasta recipe", "num_of_servings":3, "cook_time":20, "directions":"This is how you make it"}' -------------------------------------------------------------------------------- /Lesson02/Exercise13/cmd.txt: -------------------------------------------------------------------------------- 1 | http GET localhost:5000/recipes 2 | curl -i -X GET localhost:5000/recipes -------------------------------------------------------------------------------- /Lesson02/Exercise14/cmd.txt: -------------------------------------------------------------------------------- 1 | http PUT localhost:5000/recipes/1/publish 2 | curl -i -X PUT localhost:5000/recipes/1/publish 3 | 4 | http GET localhost:5000/recipes 5 | curl -i -X GET localhost:5000/recipes -------------------------------------------------------------------------------- /Lesson02/Exercise15/cmd.txt: -------------------------------------------------------------------------------- 1 | http DELETE localhost:5000/recipes 2 | curl -i -X DELETE localhost:5000/recipes -------------------------------------------------------------------------------- /Lesson02/Exercise16/cmd.txt: -------------------------------------------------------------------------------- 1 | http PUT localhost:5000/recipes/1 name="Lovely Cheese Pizza" description="This is a lovely cheese pizza recipe" num_of_servings:=3 cook_time:=60 directions="This is how you make it" 2 | curl -i -X PUT localhost:5000/recipes/1 -H "Content-Type: application/json" -d '{"name":"Lovely Cheese Pizza", "description":"This is a lovely cheese pizza recipe", "num_of_servings":3, "cook_time":60, "directions":"This is how you make it"}' -------------------------------------------------------------------------------- /Lesson02/Exercise17/cmd.txt: -------------------------------------------------------------------------------- 1 | http GET localhost:5000/recipes/1 2 | curl -i -X GET localhost:5000/recipes/1 -------------------------------------------------------------------------------- /Lesson03/Activity05/script.txt: -------------------------------------------------------------------------------- 1 | from app import * 2 | from models.user import User 3 | from models.recipe import Recipe 4 | app = create_app() 5 | 6 | user = User(username='peter', email='peter@gmail.com', password='WkQa') 7 | db.session.add(user) 8 | db.session.commit() 9 | 10 | pizza = Recipe(name='Cheese Pizza', description='This is a lovely cheese pizza recipe', num_of_servings=2, cook_time=30, directions='This is how you make it', user_id=user.id) 11 | db.session.add(pizza) 12 | db.session.commit() 13 | 14 | pasta = Recipe(name='Tomato Pasta', description='This is a lovely tomato pasta recipe', num_of_servings=3, cook_time=20, directions='This is how you make it', user_id=user.id) 15 | db.session.add(pasta) 16 | db.session.commit() 17 | -------------------------------------------------------------------------------- /Lesson03/Activity06/cmd.txt: -------------------------------------------------------------------------------- 1 | flask db migrate 2 | flask db upgrade 3 | 4 | flask db downgrade -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/migrations/versions/a1f16c58bf8b_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: a1f16c58bf8b 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-03 19:07:38.109420 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'a1f16c58bf8b' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('bio', sa.String(), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'bio') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson03/Activity06/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson03/Activity06/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson03/Activity06/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson03/Exercise19/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | -------------------------------------------------------------------------------- /Lesson03/Exercise19/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | -------------------------------------------------------------------------------- /Lesson03/Exercise19/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson03/Exercise19/models/__init__.py -------------------------------------------------------------------------------- /Lesson03/Exercise19/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 -------------------------------------------------------------------------------- /Lesson03/Exercise19/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson03/Exercise19/resources/__init__.py -------------------------------------------------------------------------------- /Lesson03/Exercise20/cmd.txt: -------------------------------------------------------------------------------- 1 | flask db init 2 | flask db migrate 3 | flask db upgrade 4 | -------------------------------------------------------------------------------- /Lesson03/Exercise22/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | -------------------------------------------------------------------------------- /Lesson03/Exercise22/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | -------------------------------------------------------------------------------- /Lesson03/Exercise22/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson03/Exercise22/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson03/Exercise22/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson03/Exercise22/models/__init__.py -------------------------------------------------------------------------------- /Lesson03/Exercise22/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 -------------------------------------------------------------------------------- /Lesson03/Exercise22/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson03/Exercise22/resources/__init__.py -------------------------------------------------------------------------------- /Lesson03/Exercise22/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Activity07/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Activity07/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson04/Activity07/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson04/Exercise24/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | -------------------------------------------------------------------------------- /Lesson04/Exercise24/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson04/Exercise24/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson04/Exercise24/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson04/Exercise24/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise24/models/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise24/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 -------------------------------------------------------------------------------- /Lesson04/Exercise24/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise24/resources/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise24/resources/token.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | from flask import request 3 | from flask_restful import Resource 4 | from flask_jwt_extended import create_access_token 5 | 6 | from utils import check_password 7 | from models.user import User 8 | 9 | 10 | class TokenResource(Resource): 11 | 12 | def post(self): 13 | 14 | json_data = request.get_json() 15 | 16 | email = json_data.get('email') 17 | password = json_data.get('password') 18 | 19 | user = User.get_by_email(email=email) 20 | 21 | if not user or not check_password(password, user.password): 22 | return {'message': 'username or password is incorrect'}, HTTPStatus.UNAUTHORIZED 23 | 24 | access_token = create_access_token(identity=user.id) 25 | 26 | return {'access_token': access_token}, HTTPStatus.OK 27 | -------------------------------------------------------------------------------- /Lesson04/Exercise24/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise26/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise26/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/resources/token.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | from flask import request 3 | from flask_restful import Resource 4 | from flask_jwt_extended import create_access_token 5 | 6 | from utils import check_password 7 | from models.user import User 8 | 9 | 10 | class TokenResource(Resource): 11 | 12 | def post(self): 13 | 14 | json_data = request.get_json() 15 | 16 | email = json_data.get('email') 17 | password = json_data.get('password') 18 | 19 | user = User.get_by_email(email=email) 20 | 21 | if not user or not check_password(password, user.password): 22 | return {'message': 'username or password is incorrect'}, HTTPStatus.UNAUTHORIZED 23 | 24 | access_token = create_access_token(identity=user.id) 25 | 26 | return {'access_token': access_token}, HTTPStatus.OK 27 | -------------------------------------------------------------------------------- /Lesson04/Exercise26/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson04/Exercise27/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | -------------------------------------------------------------------------------- /Lesson04/Exercise27/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson04/Exercise27/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson04/Exercise27/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson04/Exercise27/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise27/models/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise27/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 -------------------------------------------------------------------------------- /Lesson04/Exercise27/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise27/resources/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise27/resources/token.py: -------------------------------------------------------------------------------- 1 | from http import HTTPStatus 2 | from flask import request 3 | from flask_restful import Resource 4 | from flask_jwt_extended import create_access_token 5 | 6 | from utils import check_password 7 | from models.user import User 8 | 9 | 10 | class TokenResource(Resource): 11 | 12 | def post(self): 13 | 14 | json_data = request.get_json() 15 | 16 | email = json_data.get('email') 17 | password = json_data.get('password') 18 | 19 | user = User.get_by_email(email=email) 20 | 21 | if not user or not check_password(password, user.password): 22 | return {'message': 'username or password is incorrect'}, HTTPStatus.UNAUTHORIZED 23 | 24 | access_token = create_access_token(identity=user.id) 25 | 26 | return {'access_token': access_token}, HTTPStatus.OK 27 | -------------------------------------------------------------------------------- /Lesson04/Exercise27/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson04/Exercise29/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | -------------------------------------------------------------------------------- /Lesson04/Exercise29/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson04/Exercise29/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson04/Exercise29/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson04/Exercise29/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise29/models/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise29/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 -------------------------------------------------------------------------------- /Lesson04/Exercise29/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise29/resources/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise29/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson04/Exercise31/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson04/Exercise31/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson04/Exercise31/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson04/Exercise31/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson04/Exercise31/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise31/models/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise31/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 -------------------------------------------------------------------------------- /Lesson04/Exercise31/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson04/Exercise31/resources/__init__.py -------------------------------------------------------------------------------- /Lesson04/Exercise31/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Activity08/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Activity08/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson05/Activity08/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise33/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson05/Exercise33/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson05/Exercise33/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson05/Exercise33/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson05/Exercise33/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise33/models/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise33/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise33/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise33/resources/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise33/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson05/Exercise33/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise35/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson05/Exercise35/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson05/Exercise35/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson05/Exercise35/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson05/Exercise35/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise35/models/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise35/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise35/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise35/resources/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise35/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson05/Exercise35/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise37/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise37/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson05/Exercise37/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson05/Exercise38/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson05/Exercise38/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson05/Exercise38/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson05/Exercise38/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson05/Exercise38/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise38/models/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise38/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 -------------------------------------------------------------------------------- /Lesson05/Exercise38/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson05/Exercise38/resources/__init__.py -------------------------------------------------------------------------------- /Lesson05/Exercise38/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson05/Exercise38/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Activity10/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Activity10/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson06/Activity10/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | from itsdangerous import URLSafeTimedSerializer 3 | 4 | from flask import current_app 5 | 6 | 7 | def hash_password(password): 8 | return pbkdf2_sha256.hash(password) 9 | 10 | 11 | def check_password(password, hashed): 12 | return pbkdf2_sha256.verify(password, hashed) 13 | 14 | 15 | def generate_token(email, salt=None): 16 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 17 | return serializer.dumps(email, salt=salt) 18 | 19 | 20 | def verify_token(token, max_age=(30 * 60), salt=None): 21 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 22 | try: 23 | email = serializer.loads(token, max_age=max_age, salt=salt) 24 | except: 25 | return False 26 | 27 | return email 28 | -------------------------------------------------------------------------------- /Lesson06/Exercise41/script.txt: -------------------------------------------------------------------------------- 1 | from mailgun import MailgunApi 2 | 3 | mailgun = MailgunApi(domain='your_domain.mailgun.org', 4 | api_key='your_mailgun_api_key') 5 | 6 | mailgun.send_email(to='smilecook.api@gmail.com', 7 | subject='Hello', 8 | text='Testing some Mailgun awesomeness!') -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise41/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise41/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson06/Exercise41/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | 3 | 4 | def hash_password(password): 5 | return pbkdf2_sha256.hash(password) 6 | 7 | 8 | def check_password(password, hashed): 9 | return pbkdf2_sha256.verify(password, hashed) 10 | -------------------------------------------------------------------------------- /Lesson06/Exercise42/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson06/Exercise42/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson06/Exercise42/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson06/Exercise42/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson06/Exercise42/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson06/Exercise42/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise42/models/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise42/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 -------------------------------------------------------------------------------- /Lesson06/Exercise42/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise42/resources/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise42/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson06/Exercise42/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | from itsdangerous import URLSafeTimedSerializer 3 | 4 | from flask import current_app 5 | 6 | 7 | def hash_password(password): 8 | return pbkdf2_sha256.hash(password) 9 | 10 | 11 | def check_password(password, hashed): 12 | return pbkdf2_sha256.verify(password, hashed) 13 | 14 | 15 | def generate_token(email, salt=None): 16 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 17 | return serializer.dumps(email, salt=salt) 18 | 19 | 20 | def verify_token(token, max_age=(30 * 60), salt=None): 21 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 22 | try: 23 | email = serializer.loads(token, max_age=max_age, salt=salt) 24 | except: 25 | return False 26 | 27 | return email 28 | -------------------------------------------------------------------------------- /Lesson06/Exercise43/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson06/Exercise43/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson06/Exercise43/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson06/Exercise43/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson06/Exercise43/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson06/Exercise43/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise43/models/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise43/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 -------------------------------------------------------------------------------- /Lesson06/Exercise43/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise43/resources/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise43/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson06/Exercise43/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | from itsdangerous import URLSafeTimedSerializer 3 | 4 | from flask import current_app 5 | 6 | 7 | def hash_password(password): 8 | return pbkdf2_sha256.hash(password) 9 | 10 | 11 | def check_password(password, hashed): 12 | return pbkdf2_sha256.verify(password, hashed) 13 | 14 | 15 | def generate_token(email, salt=None): 16 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 17 | return serializer.dumps(email, salt=salt) 18 | 19 | 20 | def verify_token(token, max_age=(30 * 60), salt=None): 21 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 22 | try: 23 | email = serializer.loads(token, max_age=max_age, salt=salt) 24 | except: 25 | return False 26 | 27 | return email 28 | -------------------------------------------------------------------------------- /Lesson06/Exercise44/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson06/Exercise44/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson06/Exercise44/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson06/Exercise44/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson06/Exercise44/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson06/Exercise44/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise44/models/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise44/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 -------------------------------------------------------------------------------- /Lesson06/Exercise44/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson06/Exercise44/resources/__init__.py -------------------------------------------------------------------------------- /Lesson06/Exercise44/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson06/Exercise44/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | from itsdangerous import URLSafeTimedSerializer 3 | 4 | from flask import current_app 5 | 6 | 7 | def hash_password(password): 8 | return pbkdf2_sha256.hash(password) 9 | 10 | 11 | def check_password(password, hashed): 12 | return pbkdf2_sha256.verify(password, hashed) 13 | 14 | 15 | def generate_token(email, salt=None): 16 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 17 | return serializer.dumps(email, salt=salt) 18 | 19 | 20 | def verify_token(token, max_age=(30 * 60), salt=None): 21 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 22 | try: 23 | email = serializer.loads(token, max_age=max_age, salt=salt) 24 | except: 25 | return False 26 | 27 | return email 28 | -------------------------------------------------------------------------------- /Lesson07/Activity11/script.txt: -------------------------------------------------------------------------------- 1 | flask db migrate 2 | flask db upgrade -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Activity11/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Activity11/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Activity11/smilecook/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson07/Activity11/smilecook/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Activity11/smilecook/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson07/Exercise45/script.txt: -------------------------------------------------------------------------------- 1 | flask db migrate 2 | flask db upgrade -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | 4 | db = SQLAlchemy() 5 | jwt = JWTManager() 6 | -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise45/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise45/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/schemas/user.py: -------------------------------------------------------------------------------- 1 | from marshmallow import Schema, fields 2 | 3 | from utils import hash_password 4 | 5 | 6 | class UserSchema(Schema): 7 | class Meta: 8 | ordered = True 9 | 10 | id = fields.Int(dump_only=True) 11 | username = fields.String(required=True) 12 | email = fields.Email(required=True) 13 | password = fields.Method(required=True, deserialize='load_password') 14 | 15 | created_at = fields.DateTime(dump_only=True) 16 | updated_at = fields.DateTime(dump_only=True) 17 | 18 | def load_password(self, value): 19 | return hash_password(value) 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson07/Exercise45/smilecook/utils.py: -------------------------------------------------------------------------------- 1 | from passlib.hash import pbkdf2_sha256 2 | from itsdangerous import URLSafeTimedSerializer 3 | 4 | from flask import current_app 5 | 6 | 7 | def hash_password(password): 8 | return pbkdf2_sha256.hash(password) 9 | 10 | 11 | def check_password(password, hashed): 12 | return pbkdf2_sha256.verify(password, hashed) 13 | 14 | 15 | def generate_token(email, salt=None): 16 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 17 | return serializer.dumps(email, salt=salt) 18 | 19 | 20 | def verify_token(token, max_age=(30 * 60), salt=None): 21 | serializer = URLSafeTimedSerializer(current_app.config.get('SECRET_KEY')) 22 | try: 23 | email = serializer.loads(token, max_age=max_age, salt=salt) 24 | except: 25 | return False 26 | 27 | return email 28 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson07/Exercise46/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise46/models/__init__.py -------------------------------------------------------------------------------- /Lesson07/Exercise46/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | -------------------------------------------------------------------------------- /Lesson07/Exercise46/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise46/resources/__init__.py -------------------------------------------------------------------------------- /Lesson07/Exercise46/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise46/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson07/Exercise48/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson07/Exercise48/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson07/Exercise48/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson07/Exercise48/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson07/Exercise48/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson07/Exercise48/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson07/Exercise48/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise48/models/__init__.py -------------------------------------------------------------------------------- /Lesson07/Exercise48/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson07/Exercise48/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise48/resources/__init__.py -------------------------------------------------------------------------------- /Lesson07/Exercise48/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson07/Exercise48/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson08/Activity13/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson08/Activity13/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson08/Activity13/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson08/Activity13/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson08/Activity13/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson08/Activity13/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Activity13/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Activity13/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity13/models/__init__.py -------------------------------------------------------------------------------- /Lesson08/Activity13/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson08/Activity13/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity13/resources/__init__.py -------------------------------------------------------------------------------- /Lesson08/Activity13/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity13/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson08/Activity13/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity13/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson08/Activity15/cmd.txt: -------------------------------------------------------------------------------- 1 | flask db migrate 2 | flask db upgrade -------------------------------------------------------------------------------- /Lesson08/Activity15/script.txt: -------------------------------------------------------------------------------- 1 | http POST localhost:5000/recipes "Authorization: Bearer {token}" name="Sweet Potato Casserole" description="This is a lovely Sweet Potato Casserole" num_of_servings=12 cook_time=60 ingredients="4 cups sweet potato, 1/2 cup white sugar, 2 eggs, 1/2 cup milk" directions="This is how you make it" 2 | http POST localhost:5000/recipes "Authorization: Bearer {token}" name="Pesto Pizza" description="This is a lovely Pesto Pizza" num_of_servings=6 cook_time=20 ingredients="1 pre-baked pizza crust, 1/2 cup pesto, 1 ripe tomato" directions="This is how you make it" 3 | 4 | http PUT localhost:5000/recipes/14/publish "Authorization: Bearer {token}" 5 | http PUT localhost:5000/recipes/15/publish "Authorization: Bearer {token}" -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity15/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity15/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity15/smilecook/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson08/Activity15/smilecook/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Activity15/smilecook/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson08/Exercise50/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson08/Exercise50/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson08/Exercise50/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson08/Exercise50/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson08/Exercise50/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson08/Exercise50/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Exercise50/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Exercise50/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise50/models/__init__.py -------------------------------------------------------------------------------- /Lesson08/Exercise50/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson08/Exercise50/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise50/resources/__init__.py -------------------------------------------------------------------------------- /Lesson08/Exercise50/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise50/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson08/Exercise50/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise50/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson08/Exercise52/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson08/Exercise52/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson08/Exercise52/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson08/Exercise52/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson08/Exercise52/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson08/Exercise52/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Exercise52/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Exercise52/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise52/models/__init__.py -------------------------------------------------------------------------------- /Lesson08/Exercise52/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson08/Exercise52/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise52/resources/__init__.py -------------------------------------------------------------------------------- /Lesson08/Exercise52/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise52/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson08/Exercise52/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise52/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson08/Exercise54/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | -------------------------------------------------------------------------------- /Lesson08/Exercise54/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | 5 | db = SQLAlchemy() 6 | jwt = JWTManager() 7 | image_set = UploadSet('images', IMAGES) 8 | -------------------------------------------------------------------------------- /Lesson08/Exercise54/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson08/Exercise54/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson08/Exercise54/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson08/Exercise54/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Exercise54/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson08/Exercise54/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise54/models/__init__.py -------------------------------------------------------------------------------- /Lesson08/Exercise54/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 -------------------------------------------------------------------------------- /Lesson08/Exercise54/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise54/resources/__init__.py -------------------------------------------------------------------------------- /Lesson08/Exercise54/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise54/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson08/Exercise54/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson08/Exercise54/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | 15 | CACHE_TYPE = 'simple' 16 | CACHE_DEFAULT_TIMEOUT = 10 * 60 17 | 18 | RATELIMIT_HEADERS_ENABLED = True 19 | -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | from flask_limiter import Limiter 6 | from flask_limiter.util import get_remote_address 7 | 8 | db = SQLAlchemy() 9 | jwt = JWTManager() 10 | image_set = UploadSet('images', IMAGES) 11 | cache = Cache() 12 | limiter = Limiter(key_func=get_remote_address) 13 | -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Activity17/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 15 | Flask-Limiter==1.0.1 -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Activity17/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Activity17/smilecook/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson09/Activity17/smilecook/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Activity17/smilecook/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise56/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | 15 | CACHE_TYPE = 'simple' 16 | CACHE_DEFAULT_TIMEOUT = 10 * 60 17 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | 6 | db = SQLAlchemy() 7 | jwt = JWTManager() 8 | image_set = UploadSet('images', IMAGES) 9 | cache = Cache() 10 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson09/Exercise56/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise56/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise56/models/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise56/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 -------------------------------------------------------------------------------- /Lesson09/Exercise56/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise56/resources/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise56/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise56/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise56/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise56/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise58/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | 15 | CACHE_TYPE = 'simple' 16 | CACHE_DEFAULT_TIMEOUT = 10 * 60 17 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | 6 | db = SQLAlchemy() 7 | jwt = JWTManager() 8 | image_set = UploadSet('images', IMAGES) 9 | cache = Cache() 10 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson09/Exercise58/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise58/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise58/models/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise58/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 -------------------------------------------------------------------------------- /Lesson09/Exercise58/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise58/resources/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise58/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise58/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise58/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise58/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise60/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | 15 | CACHE_TYPE = 'simple' 16 | CACHE_DEFAULT_TIMEOUT = 10 * 60 17 | 18 | RATELIMIT_HEADERS_ENABLED = True 19 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | from flask_limiter import Limiter 6 | from flask_limiter.util import get_remote_address 7 | 8 | db = SQLAlchemy() 9 | jwt = JWTManager() 10 | image_set = UploadSet('images', IMAGES) 11 | cache = Cache() 12 | limiter = Limiter(key_func=get_remote_address) 13 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson09/Exercise60/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise60/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise60/models/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise60/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 15 | Flask-Limiter==1.0.1 -------------------------------------------------------------------------------- /Lesson09/Exercise60/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise60/resources/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise60/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise60/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise60/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise60/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | DEBUG = True 3 | 4 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 5 | SQLALCHEMY_TRACK_MODIFICATIONS = False 6 | 7 | SECRET_KEY = 'super-secret-key' 8 | JWT_ERROR_MESSAGE_KEY = 'message' 9 | 10 | JWT_BLACKLIST_ENABLED = True 11 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 12 | 13 | UPLOADED_IMAGES_DEST = 'static/images' 14 | 15 | CACHE_TYPE = 'simple' 16 | CACHE_DEFAULT_TIMEOUT = 10 * 60 17 | 18 | RATELIMIT_HEADERS_ENABLED = True 19 | -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | from flask_limiter import Limiter 6 | from flask_limiter.util import get_remote_address 7 | 8 | db = SQLAlchemy() 9 | jwt = JWTManager() 10 | image_set = UploadSet('images', IMAGES) 11 | cache = Cache() 12 | limiter = Limiter(key_func=get_remote_address) 13 | -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise62/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 15 | Flask-Limiter==1.0.1 -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise62/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise62/smilecook/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson09/Exercise62/smilecook/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson09/Exercise62/smilecook/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson10/Exercise63/config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class Config: 5 | DEBUG = False 6 | 7 | SQLALCHEMY_TRACK_MODIFICATIONS = False 8 | 9 | JWT_ERROR_MESSAGE_KEY = 'message' 10 | 11 | JWT_BLACKLIST_ENABLED = True 12 | JWT_BLACKLIST_TOKEN_CHECKS = ['access', 'refresh'] 13 | 14 | UPLOADED_IMAGES_DEST = 'static/images' 15 | 16 | CACHE_TYPE = 'simple' 17 | CACHE_DEFAULT_TIMEOUT = 10 * 60 18 | 19 | RATELIMIT_HEADERS_ENABLED = True 20 | 21 | 22 | class DevelopmentConfig(Config): 23 | DEBUG = True 24 | 25 | SECRET_KEY = 'super-secret-key' 26 | 27 | SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://your_name:your_password@localhost:5432/smilecook' 28 | 29 | 30 | class ProductionConfig(Config): 31 | 32 | SECRET_KEY = os.environ.get('SECRET_KEY') 33 | 34 | SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') 35 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | from flask_limiter import Limiter 6 | from flask_limiter.util import get_remote_address 7 | 8 | db = SQLAlchemy() 9 | jwt = JWTManager() 10 | image_set = UploadSet('images', IMAGES) 11 | cache = Cache() 12 | limiter = Limiter(key_func=get_remote_address) 13 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson10/Exercise63/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise63/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise63/models/__init__.py -------------------------------------------------------------------------------- /Lesson10/Exercise63/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 15 | Flask-Limiter==1.0.1 -------------------------------------------------------------------------------- /Lesson10/Exercise63/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise63/resources/__init__.py -------------------------------------------------------------------------------- /Lesson10/Exercise63/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise63/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson10/Exercise63/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise63/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson10/Exercise64/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | from flask_limiter import Limiter 6 | from flask_limiter.util import get_remote_address 7 | 8 | db = SQLAlchemy() 9 | jwt = JWTManager() 10 | image_set = UploadSet('images', IMAGES) 11 | cache = Cache() 12 | limiter = Limiter(key_func=get_remote_address) 13 | -------------------------------------------------------------------------------- /Lesson10/Exercise64/mailgun.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | class MailgunApi: 5 | 6 | API_URL = 'https://api.mailgun.net/v3/{}/messages' 7 | 8 | def __init__(self, domain, api_key): 9 | self.domain = domain 10 | self.key = api_key 11 | self.base_url = self.API_URL.format(self.domain) 12 | 13 | def send_email(self, to, subject, text, html=None): 14 | 15 | if not isinstance(to, (list, tuple)): 16 | to = [to, ] 17 | 18 | data = { 19 | 'from': 'SmileCook '.format(self.domain), 20 | 'to': to, 21 | 'subject': subject, 22 | 'text': text, 23 | 'html': html 24 | } 25 | 26 | response = requests.post(url=self.base_url, 27 | auth=('api', self.key), 28 | data=data) 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /Lesson10/Exercise64/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson10/Exercise64/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson10/Exercise64/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise64/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise64/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise64/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise64/models/__init__.py -------------------------------------------------------------------------------- /Lesson10/Exercise64/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 15 | Flask-Limiter==1.0.1 -------------------------------------------------------------------------------- /Lesson10/Exercise64/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise64/resources/__init__.py -------------------------------------------------------------------------------- /Lesson10/Exercise64/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise64/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson10/Exercise64/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise64/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson10/Exercise67/script.txt: -------------------------------------------------------------------------------- 1 | import os 2 | os.urandom(24) -------------------------------------------------------------------------------- /Lesson10/Exercise68/cmd.txt: -------------------------------------------------------------------------------- 1 | git --version 2 | 3 | heroku login 4 | 5 | git init 6 | heroku git:remote -a your-heroku-app 7 | 8 | git add . 9 | git commit -am "first commit" 10 | git push heroku master 11 | 12 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/Procfile: -------------------------------------------------------------------------------- 1 | release: flask db upgrade 2 | web: gunicorn main:app 3 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/extensions.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | from flask_jwt_extended import JWTManager 3 | from flask_uploads import UploadSet, IMAGES 4 | from flask_caching import Cache 5 | from flask_limiter import Limiter 6 | from flask_limiter.util import get_remote_address 7 | 8 | db = SQLAlchemy() 9 | jwt = JWTManager() 10 | image_set = UploadSet('images', IMAGES) 11 | cache = Cache() 12 | limiter = Limiter(key_func=get_remote_address) 13 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/main.py: -------------------------------------------------------------------------------- 1 | from app import create_app 2 | 3 | app = create_app() 4 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/migrations/versions/7d7d6df19aa8_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 7d7d6df19aa8 4 | Revises: e047704cf548 5 | Create Date: 2019-11-17 12:26:23.842550 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '7d7d6df19aa8' 14 | down_revision = 'e047704cf548' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('ingredients', sa.String(length=1000), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'ingredients') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/migrations/versions/824c92be2806_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 824c92be2806 4 | Revises: 6089a861042f 5 | Create Date: 2019-11-14 16:36:45.228509 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = '824c92be2806' 14 | down_revision = '6089a861042f' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('user', sa.Column('avatar_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('user', 'avatar_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/migrations/versions/e047704cf548_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e047704cf548 4 | Revises: 824c92be2806 5 | Create Date: 2019-11-14 17:20:40.778113 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e047704cf548' 14 | down_revision = '824c92be2806' 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column('recipe', sa.Column('cover_image', sa.String(length=100), nullable=True)) 22 | # ### end Alembic commands ### 23 | 24 | 25 | def downgrade(): 26 | # ### commands auto generated by Alembic - please adjust! ### 27 | op.drop_column('recipe', 'cover_image') 28 | # ### end Alembic commands ### 29 | -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise68/smilecook/models/__init__.py -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==1.0.3 2 | Flask-RESTful==0.3.7 3 | httpie==1.0.3 4 | Flask-SQLAlchemy==2.4.0 5 | Flask-Migrate==2.5.2 6 | psycopg2-binary==2.8.3 7 | passlib==1.7.1 8 | Flask-JWT-Extended==3.20.0 9 | marshmallow==2.19.5 10 | webargs==5.4.0 11 | itsdangerous==1.1.0 12 | Flask-Uploads==0.2.1 13 | Pillow==6.2.1 14 | Flask-Caching==1.7.2 15 | Flask-Limiter==1.0.1 16 | gunicorn==19.9.0 -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/resources/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise68/smilecook/resources/__init__.py -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/static/images/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise68/smilecook/static/images/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson10/Exercise68/smilecook/static/images/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Exercise68/smilecook/static/images/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson10/Frontend/cmd.txt: -------------------------------------------------------------------------------- 1 | heroku login 2 | 3 | git init 4 | heroku git:remote -a your_heroku_app_name 5 | 6 | git add . 7 | git commit -am "make it better" 8 | git push heroku master 9 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: false, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard', 9 | '@vue/typescript' 10 | ], 11 | rules: { 12 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 14 | }, 15 | parserOptions: { 16 | parser: '@typescript-eslint/parser' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/Procfile: -------------------------------------------------------------------------------- 1 | web: npm start -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/README.md: -------------------------------------------------------------------------------- 1 | # smile-cook-official 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | yarn run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Frontend/smilecook-vuejs/public/favicon.ico -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | smile-cook-official 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const history = require('connect-history-api-fallback'); 4 | 5 | const app = express(); 6 | 7 | const staticFileMiddleware = express.static(path.join(__dirname + '/dist')); 8 | 9 | app.use(staticFileMiddleware); 10 | app.use(history({ 11 | disableDotRule: true, 12 | verbose: true 13 | })); 14 | app.use(staticFileMiddleware); 15 | 16 | app.get('/', function (req, res) { 17 | res.render(path.join(__dirname + '/dist/index.html')); 18 | }); 19 | 20 | var port = process.env.PORT || 5000; 21 | app.listen(port); 22 | console.log('server started '+ port); -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/assets/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Frontend/smilecook-vuejs/src/assets/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/assets/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Frontend/smilecook-vuejs/src/assets/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Frontend/smilecook-vuejs/src/assets/logo.png -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from 'vue' 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/static/images/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Frontend/smilecook-vuejs/src/static/images/default-avatar.jpg -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/static/images/default-recipe-cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TrainingByPackt/Python-API-Development-Fundamentals/4882ea65638ea86a233e0934d6897ee2d6ddbc35/Lesson10/Frontend/smilecook-vuejs/src/static/images/default-recipe-cover.jpg -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/utils.ts: -------------------------------------------------------------------------------- 1 | // require('dotenv').config(); 2 | 3 | // const apiUrl = require('../package.json').apiUrl 4 | 5 | export function APIHost(): string { 6 | if (process.env.NODE_ENV === 'production') { 7 | return 'https://cors-anywhere.herokuapp.com/' + process.env.VUE_APP_API_URL 8 | } else { 9 | return '/api/' 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/validation.ts: -------------------------------------------------------------------------------- 1 | function validEmail(email: string) { 2 | var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 3 | return re.test(email) 4 | } 5 | 6 | function validNumber(value: number) { 7 | var re = /^\d+$/ 8 | return re.test(String(value)) 9 | } 10 | 11 | export { validEmail, validNumber } 12 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/author/my-recipe-list-view.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/general/internal-server-error-view.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator' 2 | 3 | @Component 4 | export default class InternalServerErrorView extends Vue {} 5 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/general/internal-server-error-view.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/general/not-found-view.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator' 2 | 3 | @Component 4 | export default class NotFoundView extends Vue {} -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/general/not-found-view.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/home.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue } from 'vue-property-decorator' 2 | 3 | @Component 4 | export default class Home extends Vue {} -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/recipe/author-recipe-list-view.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/src/views/recipe/recipe-list-view.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "types": [ 15 | "webpack-env" 16 | ], 17 | "paths": { 18 | "@/*": [ 19 | "src/*" 20 | ] 21 | }, 22 | "lib": [ 23 | "esnext", 24 | "dom", 25 | "dom.iterable", 26 | "scripthost" 27 | ] 28 | }, 29 | "include": [ 30 | "src/**/*.ts", 31 | "src/**/*.tsx", 32 | "src/**/*.vue", 33 | "tests/**/*.ts", 34 | "tests/**/*.tsx" 35 | ], 36 | "exclude": [ 37 | "node_modules" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /Lesson10/Frontend/smilecook-vuejs/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | lintOnSave: false, 3 | devServer: { 4 | proxy: { 5 | '/api/': { 6 | target: 'http://127.0.0.1:5000/', 7 | // target: 'https://smile-cook-api.herokuapp.com/', 8 | pathRewrite: { '^/api/': '' } 9 | } 10 | } 11 | } 12 | } 13 | --------------------------------------------------------------------------------