diff --git a/Pipfile b/Pipfile new file mode 100644 index 00000000..9534830b --- /dev/null +++ b/Pipfile @@ -0,0 +1,11 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] + +[requires] +python_version = "3.6" diff --git a/module1-web-application-development-with-flask/.gitignore b/module1-web-application-development-with-flask/.gitignore new file mode 100644 index 00000000..09ae28fa --- /dev/null +++ b/module1-web-application-development-with-flask/.gitignore @@ -0,0 +1,3 @@ +migrations/ + +test_db.db \ No newline at end of file diff --git a/module1-web-application-development-with-flask/Pipfile b/module1-web-application-development-with-flask/Pipfile new file mode 100644 index 00000000..5aef96e4 --- /dev/null +++ b/module1-web-application-development-with-flask/Pipfile @@ -0,0 +1,14 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +flask = "*" +flask-sqlalchemy = "*" +flask-migrate = "*" + +[requires] +python_version = "3.6" diff --git a/module1-web-application-development-with-flask/Pipfile.lock b/module1-web-application-development-with-flask/Pipfile.lock new file mode 100644 index 00000000..cb425b6b --- /dev/null +++ b/module1-web-application-development-with-flask/Pipfile.lock @@ -0,0 +1,179 @@ +{ + "_meta": { + "hash": { + "sha256": "0d0656ea395efb10f6c8c22e3976488f199c8929a15bcaa17f644ef4ec76bdd6" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "alembic": { + "hashes": [ + "sha256:035ab00497217628bf5d0be82d664d8713ab13d37b630084da8e1f98facf4dbf" + ], + "version": "==1.4.2" + }, + "click": { + "hashes": [ + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + ], + "version": "==7.1.2" + }, + "flask": { + "hashes": [ + "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", + "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" + ], + "index": "pypi", + "version": "==1.1.2" + }, + "flask-migrate": { + "hashes": [ + "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732", + "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee" + ], + "index": "pypi", + "version": "==2.5.3" + }, + "flask-sqlalchemy": { + "hashes": [ + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" + ], + "index": "pypi", + "version": "==2.4.1" + }, + "itsdangerous": { + "hashes": [ + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + ], + "version": "==1.1.0" + }, + "jinja2": { + "hashes": [ + "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", + "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + ], + "version": "==2.11.2" + }, + "mako": { + "hashes": [ + "sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d", + "sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9" + ], + "version": "==1.1.2" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + ], + "version": "==1.1.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", + "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + ], + "version": "==2.8.1" + }, + "python-editor": { + "hashes": [ + "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", + "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", + "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8" + ], + "version": "==1.0.4" + }, + "six": { + "hashes": [ + "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", + "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" + ], + "version": "==1.14.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:128bc917ed20d78143a45024455ff0aed7d3b96772eba13d5dbaf9cc57e5c41b", + "sha256:156a27548ba4e1fed944ff9fcdc150633e61d350d673ae7baaf6c25c04ac1f71", + "sha256:27e2efc8f77661c9af2681755974205e7462f1ae126f498f4fe12a8b24761d15", + "sha256:2a12f8be25b9ea3d1d5b165202181f2b7da4b3395289000284e5bb86154ce87c", + "sha256:31c043d5211aa0e0773821fcc318eb5cbe2ec916dfbc4c6eea0c5188971988eb", + "sha256:65eb3b03229f684af0cf0ad3bcc771970c1260a82a791a8d07bffb63d8c95bcc", + "sha256:6cd157ce74a911325e164441ff2d9b4e244659a25b3146310518d83202f15f7a", + "sha256:703c002277f0fbc3c04d0ae4989a174753a7554b2963c584ce2ec0cddcf2bc53", + "sha256:869bbb637de58ab0a912b7f20e9192132f9fbc47fc6b5111cd1e0f6cdf5cf9b0", + "sha256:8a0e0cd21da047ea10267c37caf12add400a92f0620c8bc09e4a6531a765d6d7", + "sha256:8d01e949a5d22e5c4800d59b50617c56125fc187fbeb8fa423e99858546de616", + "sha256:925b4fe5e7c03ed76912b75a9a41dfd682d59c0be43bce88d3b27f7f5ba028fb", + "sha256:9cb1819008f0225a7c066cac8bb0cf90847b2c4a6eb9ebb7431dbd00c56c06c5", + "sha256:a87d496884f40c94c85a647c385f4fd5887941d2609f71043e2b73f2436d9c65", + "sha256:a9030cd30caf848a13a192c5e45367e3c6f363726569a56e75dc1151ee26d859", + "sha256:a9e75e49a0f1583eee0ce93270232b8e7bb4b1edc89cc70b07600d525aef4f43", + "sha256:b50f45d0e82b4562f59f0e0ca511f65e412f2a97d790eea5f60e34e5f1aabc9a", + "sha256:b7878e59ec31f12d54b3797689402ee3b5cfcb5598f2ebf26491732758751908", + "sha256:ce1ddaadee913543ff0154021d31b134551f63428065168e756d90bdc4c686f5", + "sha256:ce2646e4c0807f3461be0653502bb48c6e91a5171d6e450367082c79e12868bf", + "sha256:ce6c3d18b2a8ce364013d47b9cad71db815df31d55918403f8db7d890c9d07ae", + "sha256:e4e2664232005bd306f878b0f167a31f944a07c4de0152c444f8c61bbe3cfb38", + "sha256:e8aa395482728de8bdcca9cc0faf3765ab483e81e01923aaa736b42f0294f570", + "sha256:eb4fcf7105bf071c71068c6eee47499ab8d4b8f5a11fc35147c934f0faa60f23", + "sha256:ed375a79f06cad285166e5be74745df1ed6845c5624aafadec4b7a29c25866ef", + "sha256:f35248f7e0d63b234a109dd72fbfb4b5cb6cb6840b221d0df0ecbf54ab087654", + "sha256:f502ef245c492b391e0e23e94cba030ab91722dcc56963c85bfd7f3441ea2bbe", + "sha256:fe01bac7226499aedf472c62fa3b85b2c619365f3f14dd222ffe4f3aa91e5f98" + ], + "version": "==1.3.17" + }, + "werkzeug": { + "hashes": [ + "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", + "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" + ], + "version": "==1.0.1" + } + }, + "develop": {} +} diff --git a/module1-web-application-development-with-flask/web_app/__init__.py b/module1-web-application-development-with-flask/web_app/__init__.py new file mode 100644 index 00000000..3f38b651 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/__init__.py @@ -0,0 +1,27 @@ +from flask import Flask + +from web_app.routes.home_routes import home_routes +from web_app.routes.book_routes import book_routes +from web_app.routes.user_routes import user_routes +from web_app.models import db, migrate + + +DATABASE_URI = "sqlite:///test_db.db" + + +def create_app(): + app = Flask(__name__) + + app.config["SQLALCHEMY_DATABASE_URI"] = DATABASE_URI + db.init_app(app) + migrate.init_app(app, db) + + app.register_blueprint(home_routes) + app.register_blueprint(book_routes) + app.register_blueprint(user_routes) + return app + + +if __name__ == "__main__": + my_app = create_app() + my_app.run(debug=True) \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/models.py b/module1-web-application-development-with-flask/web_app/models.py new file mode 100644 index 00000000..c716e68c --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/models.py @@ -0,0 +1,27 @@ + + +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + + +db = SQLAlchemy() + +migrate = Migrate() + + +class Book(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(128)) + author_id = db.Column(db.String(128)) + + +class User(db.Model): + # __table_name__ = "user" + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String(128)) + + +class Tweet(db.Model): + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("user.id")) + text = db.Column(db.String(500)) diff --git a/module1-web-application-development-with-flask/web_app/routes/book_routes.py b/module1-web-application-development-with-flask/web_app/routes/book_routes.py new file mode 100644 index 00000000..5f645447 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/routes/book_routes.py @@ -0,0 +1,33 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Book, db + +book_routes = Blueprint("book_routes", __name__) + + +@book_routes.route("/books.json") +def list_books_json(): + book_records = Book.query.all() + return jsonify(book_records) + + +@book_routes.route("/books") +def list_books(): + book_records = Book.query.all() + return render_template("books.html", message="Here's some books", books=book_records) + + +@book_routes.route("/books/new") +def new_book(): + return render_template("new_book.html") + + +@book_routes.route("/books/create", methods=["POST"]) +def create_book(): + print("FORM DATA:", dict(request.form)) + new_book = Book(title=request.form["book_title"], author_id=request.form["author_name"]) + + db.session.add(new_book) + db.session.commit() + return redirect(f"/books") \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/routes/home_routes.py b/module1-web-application-development-with-flask/web_app/routes/home_routes.py new file mode 100644 index 00000000..e8cee3bd --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/routes/home_routes.py @@ -0,0 +1,14 @@ +from flask import Blueprint + +home_routes = Blueprint("home_routes", __name__) + + +@home_routes.route("/") +def index(): + x = 2 + 2 + return f"Hello World {x}" + + +@home_routes.route("/about") +def about(): + return "about me" \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/routes/user_routes.py b/module1-web-application-development-with-flask/web_app/routes/user_routes.py new file mode 100644 index 00000000..132e9c3e --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/routes/user_routes.py @@ -0,0 +1,49 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Tweet, User, db +from sqlalchemy.orm import joinedload + + +user_routes = Blueprint("user_routes", __name__) + + +@user_routes.route("/users/") +def list_users(): + user_records = User.query.all() + return render_template("users.html", users=user_records) + + +@user_routes.route("/users//") +def list_user_tweets(username=None): + user = User.query.filter_by(username=username).first() + user_id = user.id + user_tweets = Tweet.query.filter_by(user_id=user_id).all() + # st() + return render_template("user_tweets.html", username=username, user_tweets=user_tweets) + + +@user_routes.route("/users//new_tweet", methods=["POST"]) +def user_create_new_tweet(username=None): + user = User.query.filter_by(username=username).first() + # st() + user_id = user.id + new_tweet = Tweet(user_id=user_id, text=request.form["tweet_text"]) + db.session.add(new_tweet) + db.session.commit() + return redirect(f"/users/{username}/") + + +@user_routes.route("/users/new/") +def new_user(): + return render_template("new_user.html") + + +@user_routes.route("/users/create", methods=["POST"]) +def create_user(): + new_user = User(username=request.form["username"]) + print("FORM DATA:", dict(request.form)) + db.session.add(new_user) + db.session.commit() + # flash("asdf") + return redirect("/users") diff --git a/module1-web-application-development-with-flask/web_app/templates/books.html b/module1-web-application-development-with-flask/web_app/templates/books.html new file mode 100644 index 00000000..d6737885 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/templates/books.html @@ -0,0 +1,24 @@ + + + + + + Document + + + + {% if books %} + + {% else %} +

no books!

+ {% endif %} + + + + \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/templates/new_book.html b/module1-web-application-development-with-flask/web_app/templates/new_book.html new file mode 100644 index 00000000..04f7cd98 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/templates/new_book.html @@ -0,0 +1,24 @@ + + + + + + Document + + +

New Book message

+

fill this form

+
+ + + + + + +
+ + \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/templates/new_user.html b/module1-web-application-development-with-flask/web_app/templates/new_user.html new file mode 100644 index 00000000..8df26f44 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/templates/new_user.html @@ -0,0 +1,18 @@ + + + + + + Document + + +

New user

+

fill this form

+
+ + + + +
+ + \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/templates/user_tweets.html b/module1-web-application-development-with-flask/web_app/templates/user_tweets.html new file mode 100644 index 00000000..7d566366 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/templates/user_tweets.html @@ -0,0 +1,31 @@ + + + + + + Document + + +

{{ username }} tweets:

+ {% if user_tweets %} + + {% else %} +

no tweets!

+ {% endif %} +

Create tweet

+
+ + + + +
+ users list + + + \ No newline at end of file diff --git a/module1-web-application-development-with-flask/web_app/templates/users.html b/module1-web-application-development-with-flask/web_app/templates/users.html new file mode 100644 index 00000000..78b89975 --- /dev/null +++ b/module1-web-application-development-with-flask/web_app/templates/users.html @@ -0,0 +1,25 @@ + + + + + + Document + + + + {% if users %} + + {% else %} +

no users!

+ {% endif %} + create new users + + + + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/.gitignore b/module2-consuming-data-from-an-api/.gitignore new file mode 100644 index 00000000..2eea525d --- /dev/null +++ b/module2-consuming-data-from-an-api/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/Pipfile b/module2-consuming-data-from-an-api/Pipfile new file mode 100644 index 00000000..db207b85 --- /dev/null +++ b/module2-consuming-data-from-an-api/Pipfile @@ -0,0 +1,17 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +python-dotenv = "*" +requests = "*" +basilica = "*" +tweepy = "*" +flask = "*" +flask-migrate = "*" + +[requires] +python_version = "3.6" diff --git a/module2-consuming-data-from-an-api/Pipfile.lock b/module2-consuming-data-from-an-api/Pipfile.lock new file mode 100644 index 00000000..049829f3 --- /dev/null +++ b/module2-consuming-data-from-an-api/Pipfile.lock @@ -0,0 +1,286 @@ +{ + "_meta": { + "hash": { + "sha256": "8a95d2cb6f3681bad4e791c08fae5a42a54bf81565d3f42ae10847341ffc101d" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "alembic": { + "hashes": [ + "sha256:035ab00497217628bf5d0be82d664d8713ab13d37b630084da8e1f98facf4dbf" + ], + "version": "==1.4.2" + }, + "basilica": { + "hashes": [ + "sha256:10d111538e7fee4ece43b9a2f0f33c8b6a34dc1a061e44c035b53ac4297e3a4d" + ], + "index": "pypi", + "version": "==0.2.8" + }, + "certifi": { + "hashes": [ + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" + ], + "version": "==2020.4.5.1" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + ], + "version": "==7.1.2" + }, + "flask": { + "hashes": [ + "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", + "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" + ], + "index": "pypi", + "version": "==1.1.2" + }, + "flask-migrate": { + "hashes": [ + "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732", + "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee" + ], + "index": "pypi", + "version": "==2.5.3" + }, + "flask-sqlalchemy": { + "hashes": [ + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" + ], + "version": "==2.4.1" + }, + "idna": { + "hashes": [ + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" + ], + "version": "==2.9" + }, + "itsdangerous": { + "hashes": [ + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + ], + "version": "==1.1.0" + }, + "jinja2": { + "hashes": [ + "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", + "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + ], + "version": "==2.11.2" + }, + "mako": { + "hashes": [ + "sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d", + "sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9" + ], + "version": "==1.1.2" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + ], + "version": "==1.1.1" + }, + "oauthlib": { + "hashes": [ + "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", + "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" + ], + "version": "==3.1.0" + }, + "pillow": { + "hashes": [ + "sha256:04766c4930c174b46fd72d450674612ab44cca977ebbcc2dde722c6933290107", + "sha256:0e2a3bceb0fd4e0cb17192ae506d5f082b309ffe5fc370a5667959c9b2f85fa3", + "sha256:0f01e63c34f0e1e2580cc0b24e86a5ccbbfa8830909a52ee17624c4193224cd9", + "sha256:12e4bad6bddd8546a2f9771485c7e3d2b546b458ae8ff79621214119ac244523", + "sha256:1f694e28c169655c50bb89a3fa07f3b854d71eb47f50783621de813979ba87f3", + "sha256:3d25dd8d688f7318dca6d8cd4f962a360ee40346c15893ae3b95c061cdbc4079", + "sha256:4b02b9c27fad2054932e89f39703646d0c543f21d3cc5b8e05434215121c28cd", + "sha256:9744350687459234867cbebfe9df8f35ef9e1538f3e729adbd8fde0761adb705", + "sha256:a0b49960110bc6ff5fead46013bcb8825d101026d466f3a4de3476defe0fb0dd", + "sha256:ae2b270f9a0b8822b98655cb3a59cdb1bd54a34807c6c56b76dd2e786c3b7db3", + "sha256:b37bb3bd35edf53125b0ff257822afa6962649995cbdfde2791ddb62b239f891", + "sha256:b532bcc2f008e96fd9241177ec580829dee817b090532f43e54074ecffdcd97f", + "sha256:b67a6c47ed963c709ed24566daa3f95a18f07d3831334da570c71da53d97d088", + "sha256:b943e71c2065ade6fef223358e56c167fc6ce31c50bc7a02dd5c17ee4338e8ac", + "sha256:ccc9ad2460eb5bee5642eaf75a0438d7f8887d484490d5117b98edd7f33118b7", + "sha256:d23e2aa9b969cf9c26edfb4b56307792b8b374202810bd949effd1c6e11ebd6d", + "sha256:eaa83729eab9c60884f362ada982d3a06beaa6cc8b084cf9f76cae7739481dfa", + "sha256:ee94fce8d003ac9fd206496f2707efe9eadcb278d94c271f129ab36aa7181344", + "sha256:f455efb7a98557412dc6f8e463c1faf1f1911ec2432059fa3e582b6000fc90e2", + "sha256:f46e0e024346e1474083c729d50de909974237c72daca05393ee32389dabe457", + "sha256:f54be399340aa602066adb63a86a6a5d4f395adfdd9da2b9a0162ea808c7b276", + "sha256:f784aad988f12c80aacfa5b381ec21fd3f38f851720f652b9f33facc5101cf4d" + ], + "version": "==7.1.2" + }, + "pysocks": { + "hashes": [ + "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299", + "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", + "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0" + ], + "version": "==1.7.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", + "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + ], + "version": "==2.8.1" + }, + "python-dotenv": { + "hashes": [ + "sha256:25c0ff1a3e12f4bde8d592cc254ab075cfe734fc5dd989036716fd17ee7e5ec7", + "sha256:3b9909bc96b0edc6b01586e1eed05e71174ef4e04c71da5786370cebea53ad74" + ], + "index": "pypi", + "version": "==0.13.0" + }, + "python-editor": { + "hashes": [ + "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", + "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", + "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8" + ], + "version": "==1.0.4" + }, + "requests": { + "hashes": [ + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" + ], + "index": "pypi", + "version": "==2.23.0" + }, + "requests-oauthlib": { + "hashes": [ + "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", + "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" + ], + "version": "==1.3.0" + }, + "six": { + "hashes": [ + "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", + "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" + ], + "version": "==1.14.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:128bc917ed20d78143a45024455ff0aed7d3b96772eba13d5dbaf9cc57e5c41b", + "sha256:156a27548ba4e1fed944ff9fcdc150633e61d350d673ae7baaf6c25c04ac1f71", + "sha256:27e2efc8f77661c9af2681755974205e7462f1ae126f498f4fe12a8b24761d15", + "sha256:2a12f8be25b9ea3d1d5b165202181f2b7da4b3395289000284e5bb86154ce87c", + "sha256:31c043d5211aa0e0773821fcc318eb5cbe2ec916dfbc4c6eea0c5188971988eb", + "sha256:65eb3b03229f684af0cf0ad3bcc771970c1260a82a791a8d07bffb63d8c95bcc", + "sha256:6cd157ce74a911325e164441ff2d9b4e244659a25b3146310518d83202f15f7a", + "sha256:703c002277f0fbc3c04d0ae4989a174753a7554b2963c584ce2ec0cddcf2bc53", + "sha256:869bbb637de58ab0a912b7f20e9192132f9fbc47fc6b5111cd1e0f6cdf5cf9b0", + "sha256:8a0e0cd21da047ea10267c37caf12add400a92f0620c8bc09e4a6531a765d6d7", + "sha256:8d01e949a5d22e5c4800d59b50617c56125fc187fbeb8fa423e99858546de616", + "sha256:925b4fe5e7c03ed76912b75a9a41dfd682d59c0be43bce88d3b27f7f5ba028fb", + "sha256:9cb1819008f0225a7c066cac8bb0cf90847b2c4a6eb9ebb7431dbd00c56c06c5", + "sha256:a87d496884f40c94c85a647c385f4fd5887941d2609f71043e2b73f2436d9c65", + "sha256:a9030cd30caf848a13a192c5e45367e3c6f363726569a56e75dc1151ee26d859", + "sha256:a9e75e49a0f1583eee0ce93270232b8e7bb4b1edc89cc70b07600d525aef4f43", + "sha256:b50f45d0e82b4562f59f0e0ca511f65e412f2a97d790eea5f60e34e5f1aabc9a", + "sha256:b7878e59ec31f12d54b3797689402ee3b5cfcb5598f2ebf26491732758751908", + "sha256:ce1ddaadee913543ff0154021d31b134551f63428065168e756d90bdc4c686f5", + "sha256:ce2646e4c0807f3461be0653502bb48c6e91a5171d6e450367082c79e12868bf", + "sha256:ce6c3d18b2a8ce364013d47b9cad71db815df31d55918403f8db7d890c9d07ae", + "sha256:e4e2664232005bd306f878b0f167a31f944a07c4de0152c444f8c61bbe3cfb38", + "sha256:e8aa395482728de8bdcca9cc0faf3765ab483e81e01923aaa736b42f0294f570", + "sha256:eb4fcf7105bf071c71068c6eee47499ab8d4b8f5a11fc35147c934f0faa60f23", + "sha256:ed375a79f06cad285166e5be74745df1ed6845c5624aafadec4b7a29c25866ef", + "sha256:f35248f7e0d63b234a109dd72fbfb4b5cb6cb6840b221d0df0ecbf54ab087654", + "sha256:f502ef245c492b391e0e23e94cba030ab91722dcc56963c85bfd7f3441ea2bbe", + "sha256:fe01bac7226499aedf472c62fa3b85b2c619365f3f14dd222ffe4f3aa91e5f98" + ], + "version": "==1.3.17" + }, + "tweepy": { + "hashes": [ + "sha256:8abd828ba51a85a2b5bb7373715d6d3bb32d18ac624e3a4db02e4ef8ab48316b", + "sha256:ecc7f200c86127903017e48824efd008734814e95f3e8e9b45ce0f4120dd08db" + ], + "index": "pypi", + "version": "==3.8.0" + }, + "urllib3": { + "hashes": [ + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + ], + "version": "==1.25.9" + }, + "werkzeug": { + "hashes": [ + "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", + "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" + ], + "version": "==1.0.1" + } + }, + "develop": {} +} diff --git a/module2-consuming-data-from-an-api/migrations/README b/module2-consuming-data-from-an-api/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/module2-consuming-data-from-an-api/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/migrations/alembic.ini b/module2-consuming-data-from-an-api/migrations/alembic.ini new file mode 100644 index 00000000..f8ed4801 --- /dev/null +++ b/module2-consuming-data-from-an-api/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/module2-consuming-data-from-an-api/migrations/env.py b/module2-consuming-data-from-an-api/migrations/env.py new file mode 100644 index 00000000..94521792 --- /dev/null +++ b/module2-consuming-data-from-an-api/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from flask import current_app +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/module2-consuming-data-from-an-api/migrations/script.py.mako b/module2-consuming-data-from-an-api/migrations/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/module2-consuming-data-from-an-api/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/module2-consuming-data-from-an-api/migrations/versions/153f1fd5ddf9_.py b/module2-consuming-data-from-an-api/migrations/versions/153f1fd5ddf9_.py new file mode 100644 index 00000000..c712da49 --- /dev/null +++ b/module2-consuming-data-from-an-api/migrations/versions/153f1fd5ddf9_.py @@ -0,0 +1,51 @@ +"""empty message + +Revision ID: 153f1fd5ddf9 +Revises: +Create Date: 2020-05-20 15:25:43.879556 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '153f1fd5ddf9' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('book', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(length=128), nullable=True), + sa.Column('author_id', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('user', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('screen_name', sa.String(length=128), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('location', sa.String(), nullable=True), + sa.Column('followers_count', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('tweet', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('user_id', sa.BigInteger(), nullable=True), + sa.Column('full_text', sa.String(length=500), nullable=True), + sa.Column('embedding', sa.PickleType(), nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('tweet') + op.drop_table('user') + op.drop_table('book') + # ### end Alembic commands ### diff --git a/module2-consuming-data-from-an-api/web_app/__init__.py b/module2-consuming-data-from-an-api/web_app/__init__.py new file mode 100644 index 00000000..6d233d93 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/__init__.py @@ -0,0 +1,29 @@ +from flask import Flask + +from web_app.routes.home_routes import home_routes +from web_app.routes.book_routes import book_routes +from web_app.routes.user_routes import user_routes +from web_app.routes.twitter_routes import twitter_routes +from web_app.models import db, migrate + + +DATABASE_URI = "sqlite:///test_db.db" + + +def create_app(): + app = Flask(__name__) + + app.config["SQLALCHEMY_DATABASE_URI"] = DATABASE_URI + db.init_app(app) + migrate.init_app(app, db) + + # app.register_blueprint(home_routes) + # app.register_blueprint(book_routes) + app.register_blueprint(user_routes) + app.register_blueprint(twitter_routes) + return app + + +if __name__ == "__main__": + my_app = create_app() + my_app.run(debug=True) \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/models.py b/module2-consuming-data-from-an-api/web_app/models.py new file mode 100644 index 00000000..93c93ee4 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/models.py @@ -0,0 +1,44 @@ + + +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + + +db = SQLAlchemy() + +migrate = Migrate() + + +class Book(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(128)) + author_id = db.Column(db.String(128)) + + +# class User(db.Model): +# # __table_name__ = "user" +# id = db.Column(db.Integer, primary_key=True) +# username = db.Column(db.String(128)) + + +# class Tweet(db.Model): +# id = db.Column(db.Integer, primary_key=True) +# user_id = db.Column(db.Integer, db.ForeignKey("user.id")) +# text = db.Column(db.String(500)) + + +class User(db.Model): + id = db.Column(db.BigInteger, primary_key=True) + screen_name = db.Column(db.String(128), nullable=False) + name = db.Column(db.String) + location = db.Column(db.String) + followers_count = db.Column(db.Integer) + #latest_tweet_id = db.Column(db.BigInteger) + + +class Tweet(db.Model): + id = db.Column(db.BigInteger, primary_key=True) + user_id = db.Column(db.BigInteger, db.ForeignKey("user.id")) + full_text = db.Column(db.String(500)) + embedding = db.Column(db.PickleType) + user = db.relationship("User", backref=db.backref("tweets", lazy=True)) diff --git a/module2-consuming-data-from-an-api/web_app/routes/book_routes.py b/module2-consuming-data-from-an-api/web_app/routes/book_routes.py new file mode 100644 index 00000000..5f645447 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/routes/book_routes.py @@ -0,0 +1,33 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Book, db + +book_routes = Blueprint("book_routes", __name__) + + +@book_routes.route("/books.json") +def list_books_json(): + book_records = Book.query.all() + return jsonify(book_records) + + +@book_routes.route("/books") +def list_books(): + book_records = Book.query.all() + return render_template("books.html", message="Here's some books", books=book_records) + + +@book_routes.route("/books/new") +def new_book(): + return render_template("new_book.html") + + +@book_routes.route("/books/create", methods=["POST"]) +def create_book(): + print("FORM DATA:", dict(request.form)) + new_book = Book(title=request.form["book_title"], author_id=request.form["author_name"]) + + db.session.add(new_book) + db.session.commit() + return redirect(f"/books") \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/routes/home_routes.py b/module2-consuming-data-from-an-api/web_app/routes/home_routes.py new file mode 100644 index 00000000..d7c18263 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/routes/home_routes.py @@ -0,0 +1,13 @@ +from flask import Blueprint, render_template + +home_routes = Blueprint("home_routes", __name__) + + +@home_routes.route("/") +def index(): + return render_template("prediction_form.html") + + +@home_routes.route("/about") +def about(): + return "about me" \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/routes/twitter_routes.py b/module2-consuming-data-from-an-api/web_app/routes/twitter_routes.py new file mode 100644 index 00000000..4089e2bd --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/routes/twitter_routes.py @@ -0,0 +1,51 @@ +from flask import Blueprint, render_template, jsonify +from web_app.services.twitter_service import twitter_api +from web_app.services.basilica_service import basilica_api_client +from web_app.models import User, Tweet, db +from pdb import set_trace as st + +twitter_routes = Blueprint("twitter_routes", __name__) + + +@twitter_routes.route("/users//") +def get_user(screen_name=None): + print(screen_name) + st() + api = twitter_api() + user = api.get_user(screen_name) + statuses = api.user_timeline(screen_name, tweet_mode="extended", count=150, exclude_replies=True, include_rts=False) + # return jsonify({"user": user._json, "tweets": [s._json for s in statuses]}) + + db_user = User.query.get(user.id) or User(id=user.id) + db_user.screen_name = user.screen_name + db_user.name = user.name + db_user.location = user.location + db_user.followers_count = user.followers_count + db.session.add(db_user) + db.session.commit() + #breakpoint() + + all_tweet_texts = [status.full_text for status in statuses] + st() + basilica_connection = basilica_api_client() + embeddings = list(basilica_connection.embed_sentences(sentences=all_tweet_texts, model="twitter")) + print("NUMBER OF EMBEDDINGS", len(embeddings)) + # TODO: explore using the zip() function maybe... + counter = 0 + for status in statuses: + print(status.full_text) + print("----") + #print(dir(status)) + # get existing tweet from the db or initialize a new one: + db_tweet = Tweet.query.get(status.id) or Tweet(id=status.id) + db_tweet.user_id = status.author.id # or db_user.id + db_tweet.full_text = status.full_text + #embedding = basilica_client.embed_sentence(status.full_text, model="twitter") # todo: prefer to make a single request to basilica with all the tweet texts, instead of a request per tweet + embedding = embeddings[counter] + print(len(embedding)) + db_tweet.embedding = embedding + db.session.add(db_tweet) + counter+=1 + db.session.commit() + #breakpoint() + return "OK" \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/routes/user_routes.py b/module2-consuming-data-from-an-api/web_app/routes/user_routes.py new file mode 100644 index 00000000..014f0435 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/routes/user_routes.py @@ -0,0 +1,54 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Tweet, User, db +from sqlalchemy.orm import joinedload + + +user_routes = Blueprint("user_routes", __name__) + + +@user_routes.route("/") +def index(): + return redirect("/users/") + + +@user_routes.route("/users/") +def list_users(): + user_records = User.query.all() + return render_template("users.html", users=user_records) + + +# @user_routes.route("/users//") +# def list_user_tweets(username=None): +# user = User.query.filter_by(username=username).first() +# user_id = user.id +# user_tweets = Tweet.query.filter_by(user_id=user_id).all() +# # st() +# return render_template("user_tweets.html", username=username, user_tweets=user_tweets) + + +# @user_routes.route("/users//new_tweet", methods=["POST"]) +# def user_create_new_tweet(username=None): +# user = User.query.filter_by(username=username).first() +# # st() +# user_id = user.id +# new_tweet = Tweet(user_id=user_id, text=request.form["tweet_text"]) +# db.session.add(new_tweet) +# db.session.commit() +# return redirect(f"/users/{username}/") + + +@user_routes.route("/users/new/") +def new_user(): + return render_template("new_user.html") + + +@user_routes.route("/users/create", methods=["POST"]) +def create_user(): + new_user = User(screen_name=request.form["username"]) + print("FORM DATA:", dict(request.form)) + db.session.add(new_user) + db.session.commit() + # flash("asdf") + return redirect("/users") diff --git a/module2-consuming-data-from-an-api/web_app/services/basilica_service.py b/module2-consuming-data-from-an-api/web_app/services/basilica_service.py new file mode 100644 index 00000000..ad22e155 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/services/basilica_service.py @@ -0,0 +1,26 @@ + +from basilica import Connection +from pdb import set_trace as st +import os +from dotenv import load_dotenv + +load_dotenv() +BASILICA_API_KEY = os.getenv("BASILICA_API_KEY", default="oops") + +# sentences = [ +# "This is a sentence!", +# "This is a similar sentence!", +# "I don't think this sentence is very similar at all...", +# ] +# x = 12 +# connection = Connection(BASILICA_API_KEY) + + +def basilica_api_client(): + connection = Connection(BASILICA_API_KEY) + print(type(connection)) #> + return connection +# embeddings = list(connection.embed_sentences(sentences)) +# # embeddings = list(c.embed_sentence(sentence)) for one sentence +# print(embeddings) +# # st() \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/services/twitter_service.py b/module2-consuming-data-from-an-api/web_app/services/twitter_service.py new file mode 100644 index 00000000..0a7df01d --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/services/twitter_service.py @@ -0,0 +1,29 @@ +import tweepy +import os +from dotenv import load_dotenv + +load_dotenv() + +TWITTER_API_KEY = os.getenv("TWITTER_API_KEY") +TWITTER_API_SECRET = os.getenv("TWITTER_API_SECRET") +TWITTER_ACCESS_TOKEN = os.getenv("TWITTER_ACCESS_TOKEN") +TWITTER_ACCESS_TOKEN_SECRET = os.getenv("TWITTER_ACCESS_TOKEN_SECRET") + + +def twitter_api(): + auth = tweepy.OAuthHandler(TWITTER_API_KEY, TWITTER_API_SECRET) + auth.set_access_token(TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET) + print("AUTH", auth) + api = tweepy.API(auth) + print("API", api) + #print(dir(api)) + return api + + +if __name__ == "__main__": + api = twitter_api() + user = api.get_user("elonmusk") + print("USER", user) + print(user.screen_name) + print(user.name) + print(user.followers_count) \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/books.html b/module2-consuming-data-from-an-api/web_app/templates/books.html new file mode 100644 index 00000000..d6737885 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/books.html @@ -0,0 +1,24 @@ + + + + + + Document + + + + {% if books %} +
    + {% for book in books %} +

    +

  • {{ book.title }}
  • +

    + {% endfor %} +
+ {% else %} +

no books!

+ {% endif %} + + + + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/new_book.html b/module2-consuming-data-from-an-api/web_app/templates/new_book.html new file mode 100644 index 00000000..04f7cd98 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/new_book.html @@ -0,0 +1,24 @@ + + + + + + Document + + +

New Book message

+

fill this form

+
+ + + + + + +
+ + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/new_user.html b/module2-consuming-data-from-an-api/web_app/templates/new_user.html new file mode 100644 index 00000000..8df26f44 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/new_user.html @@ -0,0 +1,18 @@ + + + + + + Document + + +

New user

+

fill this form

+
+ + + + +
+ + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/prediction_form.html b/module2-consuming-data-from-an-api/web_app/templates/prediction_form.html new file mode 100644 index 00000000..dbed6e7d --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/prediction_form.html @@ -0,0 +1,39 @@ + + + + + + Document + + +

Prediction Time

+ +

Use the form below to predict which user is more likely to say a given tweet...

+ +
+ + + +
+ + + +
+ + + +
+ + +
+ + + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/result.html b/module2-consuming-data-from-an-api/web_app/templates/result.html new file mode 100644 index 00000000..2e7cc03b --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/result.html @@ -0,0 +1,20 @@ + + + + + + Document + + + + + +

Results!

+ +

Between '@{{ screen_name_a }}' and '@{{ screen_name_b }}', + the user who is most likely to say '{{ tweet_text }}' + is '@{{ screen_name_most_likely }}' +

+ + + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/user_tweets.html b/module2-consuming-data-from-an-api/web_app/templates/user_tweets.html new file mode 100644 index 00000000..7d566366 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/user_tweets.html @@ -0,0 +1,31 @@ + + + + + + Document + + +

{{ username }} tweets:

+ {% if user_tweets %} +
    + {% for tweets in user_tweets %} +

    +

  • {{ tweets.text }}
  • +

    + {% endfor %} +
+ {% else %} +

no tweets!

+ {% endif %} +

Create tweet

+
+ + + + +
+ users list + + + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/templates/users.html b/module2-consuming-data-from-an-api/web_app/templates/users.html new file mode 100644 index 00000000..78b89975 --- /dev/null +++ b/module2-consuming-data-from-an-api/web_app/templates/users.html @@ -0,0 +1,25 @@ + + + + + + Document + + + + {% if users %} + + {% else %} +

no users!

+ {% endif %} + create new users + + + + \ No newline at end of file diff --git a/module2-consuming-data-from-an-api/web_app/test_db.db b/module2-consuming-data-from-an-api/web_app/test_db.db new file mode 100644 index 00000000..447450b3 Binary files /dev/null and b/module2-consuming-data-from-an-api/web_app/test_db.db differ diff --git a/module3-adding-data-science-to-a-web-application/Pipfile b/module3-adding-data-science-to-a-web-application/Pipfile new file mode 100644 index 00000000..cbfe0f59 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/Pipfile @@ -0,0 +1,19 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +python-dotenv = "*" +requests = "*" +basilica = "*" +tweepy = "*" +flask = "*" +flask-migrate = "*" +sqlalchemy = "*" +scikit-learn = "*" + +[requires] +python_version = "3.6" diff --git a/module3-adding-data-science-to-a-web-application/Pipfile.lock b/module3-adding-data-science-to-a-web-application/Pipfile.lock new file mode 100644 index 00000000..1752ceb3 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/Pipfile.lock @@ -0,0 +1,375 @@ +{ + "_meta": { + "hash": { + "sha256": "3eebd6216f3a21ea97edbd2205d3b0eb2d92a9134823254bd1c857947df7a30d" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "alembic": { + "hashes": [ + "sha256:035ab00497217628bf5d0be82d664d8713ab13d37b630084da8e1f98facf4dbf" + ], + "version": "==1.4.2" + }, + "basilica": { + "hashes": [ + "sha256:10d111538e7fee4ece43b9a2f0f33c8b6a34dc1a061e44c035b53ac4297e3a4d" + ], + "index": "pypi", + "version": "==0.2.8" + }, + "certifi": { + "hashes": [ + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" + ], + "version": "==2020.4.5.1" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", + "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" + ], + "version": "==7.1.2" + }, + "flask": { + "hashes": [ + "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", + "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" + ], + "index": "pypi", + "version": "==1.1.2" + }, + "flask-migrate": { + "hashes": [ + "sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732", + "sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee" + ], + "index": "pypi", + "version": "==2.5.3" + }, + "flask-sqlalchemy": { + "hashes": [ + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" + ], + "version": "==2.4.1" + }, + "idna": { + "hashes": [ + "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", + "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" + ], + "version": "==2.9" + }, + "itsdangerous": { + "hashes": [ + "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", + "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" + ], + "version": "==1.1.0" + }, + "jinja2": { + "hashes": [ + "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", + "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" + ], + "version": "==2.11.2" + }, + "joblib": { + "hashes": [ + "sha256:61e49189c84b3c5d99a969d314853f4d1d263316cc694bec17548ebaa9c47b6e", + "sha256:6825784ffda353cc8a1be573118085789e5b5d29401856b35b756645ab5aecb5" + ], + "version": "==0.15.1" + }, + "mako": { + "hashes": [ + "sha256:3139c5d64aa5d175dbafb95027057128b5fbd05a40c53999f3905ceb53366d9d", + "sha256:8e8b53c71c7e59f3de716b6832c4e401d903af574f6962edbbbf6ecc2a5fe6c9" + ], + "version": "==1.1.2" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + ], + "version": "==1.1.1" + }, + "numpy": { + "hashes": [ + "sha256:00d7b54c025601e28f468953d065b9b121ddca7fff30bed7be082d3656dd798d", + "sha256:02ec9582808c4e48be4e93cd629c855e644882faf704bc2bd6bbf58c08a2a897", + "sha256:0e6f72f7bb08f2f350ed4408bb7acdc0daba637e73bce9f5ea2b207039f3af88", + "sha256:1be2e96314a66f5f1ce7764274327fd4fb9da58584eaff00b5a5221edefee7d6", + "sha256:2466fbcf23711ebc5daa61d28ced319a6159b260a18839993d871096d66b93f7", + "sha256:2b573fcf6f9863ce746e4ad00ac18a948978bb3781cffa4305134d31801f3e26", + "sha256:3f0dae97e1126f529ebb66f3c63514a0f72a177b90d56e4bce8a0b5def34627a", + "sha256:50fb72bcbc2cf11e066579cb53c4ca8ac0227abb512b6cbc1faa02d1595a2a5d", + "sha256:57aea170fb23b1fd54fa537359d90d383d9bf5937ee54ae8045a723caa5e0961", + "sha256:709c2999b6bd36cdaf85cf888d8512da7433529f14a3689d6e37ab5242e7add5", + "sha256:7d59f21e43bbfd9a10953a7e26b35b6849d888fc5a331fa84a2d9c37bd9fe2a2", + "sha256:904b513ab8fbcbdb062bed1ce2f794ab20208a1b01ce9bd90776c6c7e7257032", + "sha256:96dd36f5cdde152fd6977d1bbc0f0561bccffecfde63cd397c8e6033eb66baba", + "sha256:9933b81fecbe935e6a7dc89cbd2b99fea1bf362f2790daf9422a7bb1dc3c3085", + "sha256:bbcc85aaf4cd84ba057decaead058f43191cc0e30d6bc5d44fe336dc3d3f4509", + "sha256:dccd380d8e025c867ddcb2f84b439722cf1f23f3a319381eac45fd077dee7170", + "sha256:e22cd0f72fc931d6abc69dc7764484ee20c6a60b0d0fee9ce0426029b1c1bdae", + "sha256:ed722aefb0ebffd10b32e67f48e8ac4c5c4cf5d3a785024fdf0e9eb17529cd9d", + "sha256:efb7ac5572c9a57159cf92c508aad9f856f1cb8e8302d7fdb99061dbe52d712c", + "sha256:efdba339fffb0e80fcc19524e4fdbda2e2b5772ea46720c44eaac28096d60720", + "sha256:f22273dd6a403ed870207b853a856ff6327d5cbce7a835dfa0645b3fc00273ec" + ], + "version": "==1.18.4" + }, + "oauthlib": { + "hashes": [ + "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", + "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" + ], + "version": "==3.1.0" + }, + "pillow": { + "hashes": [ + "sha256:04766c4930c174b46fd72d450674612ab44cca977ebbcc2dde722c6933290107", + "sha256:0e2a3bceb0fd4e0cb17192ae506d5f082b309ffe5fc370a5667959c9b2f85fa3", + "sha256:0f01e63c34f0e1e2580cc0b24e86a5ccbbfa8830909a52ee17624c4193224cd9", + "sha256:12e4bad6bddd8546a2f9771485c7e3d2b546b458ae8ff79621214119ac244523", + "sha256:1f694e28c169655c50bb89a3fa07f3b854d71eb47f50783621de813979ba87f3", + "sha256:3d25dd8d688f7318dca6d8cd4f962a360ee40346c15893ae3b95c061cdbc4079", + "sha256:4b02b9c27fad2054932e89f39703646d0c543f21d3cc5b8e05434215121c28cd", + "sha256:9744350687459234867cbebfe9df8f35ef9e1538f3e729adbd8fde0761adb705", + "sha256:a0b49960110bc6ff5fead46013bcb8825d101026d466f3a4de3476defe0fb0dd", + "sha256:ae2b270f9a0b8822b98655cb3a59cdb1bd54a34807c6c56b76dd2e786c3b7db3", + "sha256:b37bb3bd35edf53125b0ff257822afa6962649995cbdfde2791ddb62b239f891", + "sha256:b532bcc2f008e96fd9241177ec580829dee817b090532f43e54074ecffdcd97f", + "sha256:b67a6c47ed963c709ed24566daa3f95a18f07d3831334da570c71da53d97d088", + "sha256:b943e71c2065ade6fef223358e56c167fc6ce31c50bc7a02dd5c17ee4338e8ac", + "sha256:ccc9ad2460eb5bee5642eaf75a0438d7f8887d484490d5117b98edd7f33118b7", + "sha256:d23e2aa9b969cf9c26edfb4b56307792b8b374202810bd949effd1c6e11ebd6d", + "sha256:eaa83729eab9c60884f362ada982d3a06beaa6cc8b084cf9f76cae7739481dfa", + "sha256:ee94fce8d003ac9fd206496f2707efe9eadcb278d94c271f129ab36aa7181344", + "sha256:f455efb7a98557412dc6f8e463c1faf1f1911ec2432059fa3e582b6000fc90e2", + "sha256:f46e0e024346e1474083c729d50de909974237c72daca05393ee32389dabe457", + "sha256:f54be399340aa602066adb63a86a6a5d4f395adfdd9da2b9a0162ea808c7b276", + "sha256:f784aad988f12c80aacfa5b381ec21fd3f38f851720f652b9f33facc5101cf4d" + ], + "version": "==7.1.2" + }, + "pysocks": { + "hashes": [ + "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299", + "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5", + "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0" + ], + "version": "==1.7.1" + }, + "python-dateutil": { + "hashes": [ + "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", + "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" + ], + "version": "==2.8.1" + }, + "python-dotenv": { + "hashes": [ + "sha256:25c0ff1a3e12f4bde8d592cc254ab075cfe734fc5dd989036716fd17ee7e5ec7", + "sha256:3b9909bc96b0edc6b01586e1eed05e71174ef4e04c71da5786370cebea53ad74" + ], + "index": "pypi", + "version": "==0.13.0" + }, + "python-editor": { + "hashes": [ + "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d", + "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b", + "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8" + ], + "version": "==1.0.4" + }, + "requests": { + "hashes": [ + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" + ], + "index": "pypi", + "version": "==2.23.0" + }, + "requests-oauthlib": { + "hashes": [ + "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", + "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" + ], + "version": "==1.3.0" + }, + "scikit-learn": { + "hashes": [ + "sha256:04799686060ecbf8992f26a35be1d99e981894c8c7860c1365cda4200f954a16", + "sha256:058d213092de4384710137af1300ed0ff030b8c40459a6c6f73c31ccd274cc39", + "sha256:0c3464e46ef8bd4f1bfa5c009648c6449412c8f7e9b3fc0c9e3d800139c48827", + "sha256:0e7b55f73b35537ecd0d19df29dd39aa9e076dba78f3507b8136c819d84611fd", + "sha256:16feae4361be6b299d4d08df5a30956b4bfc8eadf173fe9258f6d59630f851d4", + "sha256:244ca85d6eba17a1e6e8a66ab2f584be6a7784b5f59297e3d7ff8c7983af627c", + "sha256:3e6e92b495eee193a8fa12a230c9b7976ea0fc1263719338e35c986ea1e42cff", + "sha256:5bcea4d6ee431c814261117281363208408aa4e665633655895feb059021aca6", + "sha256:93f56abd316d131645559ec0ab4f45e3391c2ccdd4eadaa4912f4c1e0a6f2c96", + "sha256:9e04c0811ea92931ee8490d638171b8cb2f21387efcfff526bbc8c2a3da60f1c", + "sha256:bded94236e16774385202cafd26190ce96db18e4dc21e99473848c61e4fdc400", + "sha256:c2fa33d20408b513cf432505c80e6eb4bf4d71434f1ae36680765d4a2c2a16ec", + "sha256:e3fec1c8831f8f93ad85581ca29ca1bb88e2da377fb097cf8322aa89c21bc9b8", + "sha256:e585682e37f2faa81ad6cd4472fff646bf2fd0542147bec93697a905db8e6bd2", + "sha256:e9879ba9e64ec3add41bf201e06034162f853652ef4849b361d73b0deb3153ad", + "sha256:ebe853e6f318f9d8b3b74dd17e553720d35646eff675a69eeaed12fbbbb07daa" + ], + "index": "pypi", + "version": "==0.23.1" + }, + "scipy": { + "hashes": [ + "sha256:00af72998a46c25bdb5824d2b729e7dabec0c765f9deb0b504f928591f5ff9d4", + "sha256:0902a620a381f101e184a958459b36d3ee50f5effd186db76e131cbefcbb96f7", + "sha256:1e3190466d669d658233e8a583b854f6386dd62d655539b77b3fa25bfb2abb70", + "sha256:2cce3f9847a1a51019e8c5b47620da93950e58ebc611f13e0d11f4980ca5fecb", + "sha256:3092857f36b690a321a662fe5496cb816a7f4eecd875e1d36793d92d3f884073", + "sha256:386086e2972ed2db17cebf88610aab7d7f6e2c0ca30042dc9a89cf18dcc363fa", + "sha256:71eb180f22c49066f25d6df16f8709f215723317cc951d99e54dc88020ea57be", + "sha256:770254a280d741dd3436919d47e35712fb081a6ff8bafc0f319382b954b77802", + "sha256:787cc50cab3020a865640aba3485e9fbd161d4d3b0d03a967df1a2881320512d", + "sha256:8a07760d5c7f3a92e440ad3aedcc98891e915ce857664282ae3c0220f3301eb6", + "sha256:8d3bc3993b8e4be7eade6dcc6fd59a412d96d3a33fa42b0fa45dc9e24495ede9", + "sha256:9508a7c628a165c2c835f2497837bf6ac80eb25291055f56c129df3c943cbaf8", + "sha256:a144811318853a23d32a07bc7fd5561ff0cac5da643d96ed94a4ffe967d89672", + "sha256:a1aae70d52d0b074d8121333bc807a485f9f1e6a69742010b33780df2e60cfe0", + "sha256:a2d6df9eb074af7f08866598e4ef068a2b310d98f87dc23bd1b90ec7bdcec802", + "sha256:bb517872058a1f087c4528e7429b4a44533a902644987e7b2fe35ecc223bc408", + "sha256:c5cac0c0387272ee0e789e94a570ac51deb01c796b37fb2aad1fb13f85e2f97d", + "sha256:cc971a82ea1170e677443108703a2ec9ff0f70752258d0e9f5433d00dda01f59", + "sha256:dba8306f6da99e37ea08c08fef6e274b5bf8567bb094d1dbe86a20e532aca088", + "sha256:dc60bb302f48acf6da8ca4444cfa17d52c63c5415302a9ee77b3b21618090521", + "sha256:dee1bbf3a6c8f73b6b218cb28eed8dd13347ea2f87d572ce19b289d6fd3fbc59" + ], + "version": "==1.4.1" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "version": "==1.15.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:128bc917ed20d78143a45024455ff0aed7d3b96772eba13d5dbaf9cc57e5c41b", + "sha256:156a27548ba4e1fed944ff9fcdc150633e61d350d673ae7baaf6c25c04ac1f71", + "sha256:27e2efc8f77661c9af2681755974205e7462f1ae126f498f4fe12a8b24761d15", + "sha256:2a12f8be25b9ea3d1d5b165202181f2b7da4b3395289000284e5bb86154ce87c", + "sha256:31c043d5211aa0e0773821fcc318eb5cbe2ec916dfbc4c6eea0c5188971988eb", + "sha256:65eb3b03229f684af0cf0ad3bcc771970c1260a82a791a8d07bffb63d8c95bcc", + "sha256:6cd157ce74a911325e164441ff2d9b4e244659a25b3146310518d83202f15f7a", + "sha256:703c002277f0fbc3c04d0ae4989a174753a7554b2963c584ce2ec0cddcf2bc53", + "sha256:869bbb637de58ab0a912b7f20e9192132f9fbc47fc6b5111cd1e0f6cdf5cf9b0", + "sha256:8a0e0cd21da047ea10267c37caf12add400a92f0620c8bc09e4a6531a765d6d7", + "sha256:8d01e949a5d22e5c4800d59b50617c56125fc187fbeb8fa423e99858546de616", + "sha256:925b4fe5e7c03ed76912b75a9a41dfd682d59c0be43bce88d3b27f7f5ba028fb", + "sha256:9cb1819008f0225a7c066cac8bb0cf90847b2c4a6eb9ebb7431dbd00c56c06c5", + "sha256:a87d496884f40c94c85a647c385f4fd5887941d2609f71043e2b73f2436d9c65", + "sha256:a9030cd30caf848a13a192c5e45367e3c6f363726569a56e75dc1151ee26d859", + "sha256:a9e75e49a0f1583eee0ce93270232b8e7bb4b1edc89cc70b07600d525aef4f43", + "sha256:b50f45d0e82b4562f59f0e0ca511f65e412f2a97d790eea5f60e34e5f1aabc9a", + "sha256:b7878e59ec31f12d54b3797689402ee3b5cfcb5598f2ebf26491732758751908", + "sha256:ce1ddaadee913543ff0154021d31b134551f63428065168e756d90bdc4c686f5", + "sha256:ce2646e4c0807f3461be0653502bb48c6e91a5171d6e450367082c79e12868bf", + "sha256:ce6c3d18b2a8ce364013d47b9cad71db815df31d55918403f8db7d890c9d07ae", + "sha256:e4e2664232005bd306f878b0f167a31f944a07c4de0152c444f8c61bbe3cfb38", + "sha256:e8aa395482728de8bdcca9cc0faf3765ab483e81e01923aaa736b42f0294f570", + "sha256:eb4fcf7105bf071c71068c6eee47499ab8d4b8f5a11fc35147c934f0faa60f23", + "sha256:ed375a79f06cad285166e5be74745df1ed6845c5624aafadec4b7a29c25866ef", + "sha256:f35248f7e0d63b234a109dd72fbfb4b5cb6cb6840b221d0df0ecbf54ab087654", + "sha256:f502ef245c492b391e0e23e94cba030ab91722dcc56963c85bfd7f3441ea2bbe", + "sha256:fe01bac7226499aedf472c62fa3b85b2c619365f3f14dd222ffe4f3aa91e5f98" + ], + "index": "pypi", + "version": "==1.3.17" + }, + "threadpoolctl": { + "hashes": [ + "sha256:48b3e3e9ee079d6b5295c65cbe255b36a3026afc6dde3fb49c085cd0c004bbcf", + "sha256:72eed211bb25feecc3244c5c26b015579777a466589e9b854c66f18d6deaeee1" + ], + "version": "==2.0.0" + }, + "tweepy": { + "hashes": [ + "sha256:8abd828ba51a85a2b5bb7373715d6d3bb32d18ac624e3a4db02e4ef8ab48316b", + "sha256:ecc7f200c86127903017e48824efd008734814e95f3e8e9b45ce0f4120dd08db" + ], + "index": "pypi", + "version": "==3.8.0" + }, + "urllib3": { + "hashes": [ + "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527", + "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115" + ], + "version": "==1.25.9" + }, + "werkzeug": { + "hashes": [ + "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", + "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" + ], + "version": "==1.0.1" + } + }, + "develop": {} +} diff --git a/module3-adding-data-science-to-a-web-application/migrations/README b/module3-adding-data-science-to-a-web-application/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/migrations/alembic.ini b/module3-adding-data-science-to-a-web-application/migrations/alembic.ini new file mode 100644 index 00000000..f8ed4801 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/migrations/alembic.ini @@ -0,0 +1,45 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/module3-adding-data-science-to-a-web-application/migrations/env.py b/module3-adding-data-science-to-a-web-application/migrations/env.py new file mode 100644 index 00000000..94521792 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/migrations/env.py @@ -0,0 +1,96 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +from flask import current_app +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/module3-adding-data-science-to-a-web-application/migrations/script.py.mako b/module3-adding-data-science-to-a-web-application/migrations/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/module3-adding-data-science-to-a-web-application/migrations/versions/65d0f9cd7b34_.py b/module3-adding-data-science-to-a-web-application/migrations/versions/65d0f9cd7b34_.py new file mode 100644 index 00000000..7d070e5a --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/migrations/versions/65d0f9cd7b34_.py @@ -0,0 +1,51 @@ +"""empty message + +Revision ID: 65d0f9cd7b34 +Revises: +Create Date: 2020-05-21 12:27:34.652463 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '65d0f9cd7b34' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('book', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('title', sa.String(length=128), nullable=True), + sa.Column('author_id', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('user', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('screen_name', sa.String(length=128), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('location', sa.String(), nullable=True), + sa.Column('followers_count', sa.Integer(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('tweet', + sa.Column('id', sa.BigInteger(), nullable=False), + sa.Column('user_id', sa.BigInteger(), nullable=True), + sa.Column('full_text', sa.String(length=500), nullable=True), + sa.Column('embedding', sa.PickleType(), nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('tweet') + op.drop_table('user') + op.drop_table('book') + # ### end Alembic commands ### diff --git a/module3-adding-data-science-to-a-web-application/models/latest_model.pkl b/module3-adding-data-science-to-a-web-application/models/latest_model.pkl new file mode 100644 index 00000000..b9ea874e Binary files /dev/null and b/module3-adding-data-science-to-a-web-application/models/latest_model.pkl differ diff --git a/module3-adding-data-science-to-a-web-application/web_app/__init__.py b/module3-adding-data-science-to-a-web-application/web_app/__init__.py new file mode 100644 index 00000000..6d233d93 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/__init__.py @@ -0,0 +1,29 @@ +from flask import Flask + +from web_app.routes.home_routes import home_routes +from web_app.routes.book_routes import book_routes +from web_app.routes.user_routes import user_routes +from web_app.routes.twitter_routes import twitter_routes +from web_app.models import db, migrate + + +DATABASE_URI = "sqlite:///test_db.db" + + +def create_app(): + app = Flask(__name__) + + app.config["SQLALCHEMY_DATABASE_URI"] = DATABASE_URI + db.init_app(app) + migrate.init_app(app, db) + + # app.register_blueprint(home_routes) + # app.register_blueprint(book_routes) + app.register_blueprint(user_routes) + app.register_blueprint(twitter_routes) + return app + + +if __name__ == "__main__": + my_app = create_app() + my_app.run(debug=True) \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/iris_classifier.py b/module3-adding-data-science-to-a-web-application/web_app/iris_classifier.py new file mode 100644 index 00000000..e42ac9bb --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/iris_classifier.py @@ -0,0 +1,43 @@ +import os +import pickle + +from sklearn.datasets import load_iris +from sklearn.linear_model import LogisticRegression # for example + +MODEL_FILEPATH = os.path.join(os.path.dirname(__file__), "..", "models", "latest_model.pkl") + + +def train_and_save_model(): + print("TRAINING THE MODEL...") + X, y = load_iris(return_X_y=True) + #print(type(X), X.shape) #> (150, 4) + #print(type(y), y.shape) #> (150,) + classifier = LogisticRegression() # for example + classifier.fit(X, y) + + print("SAVING THE MODEL...") + with open(MODEL_FILEPATH, "wb") as model_file: + pickle.dump(classifier, model_file) + + return classifier + + +def load_model(): + print("LOADING THE MODEL...") + with open(MODEL_FILEPATH, "rb") as model_file: + saved_model = pickle.load(model_file) + return saved_model + +if __name__ == "__main__": + + train_and_save_model() + + clf = load_model() + print("CLASSIFIER:", clf) + + X, y = load_iris(return_X_y=True) # just to have some data to use when predicting + inputs = X[:2, :] + print(type(inputs), inputs) + + result = clf.predict(inputs) + print("RESULT:", result) \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/models.py b/module3-adding-data-science-to-a-web-application/web_app/models.py new file mode 100644 index 00000000..93c93ee4 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/models.py @@ -0,0 +1,44 @@ + + +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + + +db = SQLAlchemy() + +migrate = Migrate() + + +class Book(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(128)) + author_id = db.Column(db.String(128)) + + +# class User(db.Model): +# # __table_name__ = "user" +# id = db.Column(db.Integer, primary_key=True) +# username = db.Column(db.String(128)) + + +# class Tweet(db.Model): +# id = db.Column(db.Integer, primary_key=True) +# user_id = db.Column(db.Integer, db.ForeignKey("user.id")) +# text = db.Column(db.String(500)) + + +class User(db.Model): + id = db.Column(db.BigInteger, primary_key=True) + screen_name = db.Column(db.String(128), nullable=False) + name = db.Column(db.String) + location = db.Column(db.String) + followers_count = db.Column(db.Integer) + #latest_tweet_id = db.Column(db.BigInteger) + + +class Tweet(db.Model): + id = db.Column(db.BigInteger, primary_key=True) + user_id = db.Column(db.BigInteger, db.ForeignKey("user.id")) + full_text = db.Column(db.String(500)) + embedding = db.Column(db.PickleType) + user = db.relationship("User", backref=db.backref("tweets", lazy=True)) diff --git a/module3-adding-data-science-to-a-web-application/web_app/routes/book_routes.py b/module3-adding-data-science-to-a-web-application/web_app/routes/book_routes.py new file mode 100644 index 00000000..5f645447 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/routes/book_routes.py @@ -0,0 +1,33 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Book, db + +book_routes = Blueprint("book_routes", __name__) + + +@book_routes.route("/books.json") +def list_books_json(): + book_records = Book.query.all() + return jsonify(book_records) + + +@book_routes.route("/books") +def list_books(): + book_records = Book.query.all() + return render_template("books.html", message="Here's some books", books=book_records) + + +@book_routes.route("/books/new") +def new_book(): + return render_template("new_book.html") + + +@book_routes.route("/books/create", methods=["POST"]) +def create_book(): + print("FORM DATA:", dict(request.form)) + new_book = Book(title=request.form["book_title"], author_id=request.form["author_name"]) + + db.session.add(new_book) + db.session.commit() + return redirect(f"/books") \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/routes/home_routes.py b/module3-adding-data-science-to-a-web-application/web_app/routes/home_routes.py new file mode 100644 index 00000000..e8cee3bd --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/routes/home_routes.py @@ -0,0 +1,14 @@ +from flask import Blueprint + +home_routes = Blueprint("home_routes", __name__) + + +@home_routes.route("/") +def index(): + x = 2 + 2 + return f"Hello World {x}" + + +@home_routes.route("/about") +def about(): + return "about me" \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/routes/stats_routes.py b/module3-adding-data-science-to-a-web-application/web_app/routes/stats_routes.py new file mode 100644 index 00000000..bf3134d7 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/routes/stats_routes.py @@ -0,0 +1,64 @@ +# web_app/routes/stats_routes.py + +from flask import Blueprint, request, jsonify, render_template + +# from sklearn.linear_model import LogisticRegression # for example + +# from web_app.models import User, Tweet +# from web_app.services.basilica_service import basilica_api_client + +stats_routes = Blueprint("stats_routes", __name__) + +@stats_routes.route("/predict", methods=["POST"]) +def predict(): + print("PREDICT ROUTE...") + print("FORM DATA:", dict(request.form)) + #> {'screen_name_a': 'elonmusk', 'screen_name_b': 's2t2', 'tweet_text': 'Example tweet text here'} + screen_name_a = request.form["screen_name_a"] + screen_name_b = request.form["screen_name_b"] + tweet_text = request.form["tweet_text"] + + # print("-----------------") + # print("FETCHING TWEETS FROM THE DATABASE...") + # # todo: wrap in a try block in case the user's don't exist in the database + # user_a = User.query.filter(User.screen_name == screen_name_a).one() + # user_b = User.query.filter(User.screen_name == screen_name_b).one() + # user_a_tweets = user_a.tweets + # user_b_tweets = user_b.tweets + # #user_a_embeddings = [tweet.embedding for tweet in user_a_tweets] + # #user_b_embeddings = [tweet.embedding for tweet in user_b_tweets] + # print("USER A", user_a.screen_name, len(user_a.tweets)) + # print("USER B", user_b.screen_name, len(user_b.tweets)) + + # print("-----------------") + # print("TRAINING THE MODEL...") + # embeddings = [] + # labels = [] + # for tweet in user_a_tweets: + # labels.append(user_a.screen_name) + # embeddings.append(tweet.embedding) + + # for tweet in user_b_tweets: + # labels.append(user_b.screen_name) + # embeddings.append(tweet.embedding) + + # classifier = LogisticRegression() # for example + # classifier.fit(embeddings, labels) + + # print("-----------------") + # print("MAKING A PREDICTION...") + # #result_a = classifier.predict([user_a_tweets[0].embedding]) + # #result_b = classifier.predict([user_b_tweets[0].embedding]) + + # basilica_api = basilica_api_client() + # example_embedding = basilica_api.embed_sentence(tweet_text) + # result = classifier.predict([example_embedding]) + # #breakpoint() + + # #return jsonify({"message": "RESULTS", "most_likely": result[0]}) + # return render_template("results.html", + screen_name_a=screen_name_a, + screen_name_b=screen_name_b, + tweet_text=tweet_text, + # screen_name_most_likely= result[0] + ) \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/routes/twitter_routes.py b/module3-adding-data-science-to-a-web-application/web_app/routes/twitter_routes.py new file mode 100644 index 00000000..d5baa0b3 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/routes/twitter_routes.py @@ -0,0 +1,50 @@ +from flask import Blueprint, render_template, jsonify +from web_app.services.twitter_service import twitter_api +from web_app.services.basilica_service import basilica_api_client +from web_app.models import User, Tweet, db +from pdb import set_trace as st + +twitter_routes = Blueprint("twitter_routes", __name__) + + +@twitter_routes.route("/users//") +def get_user(screen_name=None): + print(screen_name) + api = twitter_api() + user = api.get_user(screen_name) + statuses = api.user_timeline(screen_name, tweet_mode="extended", count=150, exclude_replies=True, include_rts=False) + # return jsonify({"user": user._json, "tweets": [s._json for s in statuses]}) + + db_user = User.query.get(user.id) or User(id=user.id) + db_user.screen_name = user.screen_name + db_user.name = user.name + db_user.location = user.location + db_user.followers_count = user.followers_count + db.session.add(db_user) + db.session.commit() + #breakpoint() + + all_tweet_texts = [status.full_text for status in statuses] + # st() + basilica_connection = basilica_api_client() + embeddings = list(basilica_connection.embed_sentences(sentences=all_tweet_texts, model="twitter")) + print("NUMBER OF EMBEDDINGS", len(embeddings)) + # TODO: explore using the zip() function maybe... + counter = 0 + for status in statuses: + print(status.full_text) + print("----") + #print(dir(status)) + # get existing tweet from the db or initialize a new one: + db_tweet = Tweet.query.get(status.id) or Tweet(id=status.id) + db_tweet.user_id = status.author.id # or db_user.id + db_tweet.full_text = status.full_text + #embedding = basilica_client.embed_sentence(status.full_text, model="twitter") # todo: prefer to make a single request to basilica with all the tweet texts, instead of a request per tweet + embedding = embeddings[counter] + print(len(embedding)) + db_tweet.embedding = embedding + db.session.add(db_tweet) + counter+=1 + db.session.commit() + #breakpoint() + return "OK" diff --git a/module3-adding-data-science-to-a-web-application/web_app/routes/user_routes.py b/module3-adding-data-science-to-a-web-application/web_app/routes/user_routes.py new file mode 100644 index 00000000..014f0435 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/routes/user_routes.py @@ -0,0 +1,54 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Tweet, User, db +from sqlalchemy.orm import joinedload + + +user_routes = Blueprint("user_routes", __name__) + + +@user_routes.route("/") +def index(): + return redirect("/users/") + + +@user_routes.route("/users/") +def list_users(): + user_records = User.query.all() + return render_template("users.html", users=user_records) + + +# @user_routes.route("/users//") +# def list_user_tweets(username=None): +# user = User.query.filter_by(username=username).first() +# user_id = user.id +# user_tweets = Tweet.query.filter_by(user_id=user_id).all() +# # st() +# return render_template("user_tweets.html", username=username, user_tweets=user_tweets) + + +# @user_routes.route("/users//new_tweet", methods=["POST"]) +# def user_create_new_tweet(username=None): +# user = User.query.filter_by(username=username).first() +# # st() +# user_id = user.id +# new_tweet = Tweet(user_id=user_id, text=request.form["tweet_text"]) +# db.session.add(new_tweet) +# db.session.commit() +# return redirect(f"/users/{username}/") + + +@user_routes.route("/users/new/") +def new_user(): + return render_template("new_user.html") + + +@user_routes.route("/users/create", methods=["POST"]) +def create_user(): + new_user = User(screen_name=request.form["username"]) + print("FORM DATA:", dict(request.form)) + db.session.add(new_user) + db.session.commit() + # flash("asdf") + return redirect("/users") diff --git a/module3-adding-data-science-to-a-web-application/web_app/services/basilica_service.py b/module3-adding-data-science-to-a-web-application/web_app/services/basilica_service.py new file mode 100644 index 00000000..ad22e155 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/services/basilica_service.py @@ -0,0 +1,26 @@ + +from basilica import Connection +from pdb import set_trace as st +import os +from dotenv import load_dotenv + +load_dotenv() +BASILICA_API_KEY = os.getenv("BASILICA_API_KEY", default="oops") + +# sentences = [ +# "This is a sentence!", +# "This is a similar sentence!", +# "I don't think this sentence is very similar at all...", +# ] +# x = 12 +# connection = Connection(BASILICA_API_KEY) + + +def basilica_api_client(): + connection = Connection(BASILICA_API_KEY) + print(type(connection)) #> + return connection +# embeddings = list(connection.embed_sentences(sentences)) +# # embeddings = list(c.embed_sentence(sentence)) for one sentence +# print(embeddings) +# # st() \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/services/twitter_service.py b/module3-adding-data-science-to-a-web-application/web_app/services/twitter_service.py new file mode 100644 index 00000000..0a7df01d --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/services/twitter_service.py @@ -0,0 +1,29 @@ +import tweepy +import os +from dotenv import load_dotenv + +load_dotenv() + +TWITTER_API_KEY = os.getenv("TWITTER_API_KEY") +TWITTER_API_SECRET = os.getenv("TWITTER_API_SECRET") +TWITTER_ACCESS_TOKEN = os.getenv("TWITTER_ACCESS_TOKEN") +TWITTER_ACCESS_TOKEN_SECRET = os.getenv("TWITTER_ACCESS_TOKEN_SECRET") + + +def twitter_api(): + auth = tweepy.OAuthHandler(TWITTER_API_KEY, TWITTER_API_SECRET) + auth.set_access_token(TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET) + print("AUTH", auth) + api = tweepy.API(auth) + print("API", api) + #print(dir(api)) + return api + + +if __name__ == "__main__": + api = twitter_api() + user = api.get_user("elonmusk") + print("USER", user) + print(user.screen_name) + print(user.name) + print(user.followers_count) \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/templates/books.html b/module3-adding-data-science-to-a-web-application/web_app/templates/books.html new file mode 100644 index 00000000..d6737885 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/templates/books.html @@ -0,0 +1,24 @@ + + + + + + Document + + + + {% if books %} +
    + {% for book in books %} +

    +

  • {{ book.title }}
  • +

    + {% endfor %} +
+ {% else %} +

no books!

+ {% endif %} + + + + \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/templates/new_book.html b/module3-adding-data-science-to-a-web-application/web_app/templates/new_book.html new file mode 100644 index 00000000..04f7cd98 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/templates/new_book.html @@ -0,0 +1,24 @@ + + + + + + Document + + +

New Book message

+

fill this form

+
+ + + + + + +
+ + \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/templates/new_user.html b/module3-adding-data-science-to-a-web-application/web_app/templates/new_user.html new file mode 100644 index 00000000..8df26f44 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/templates/new_user.html @@ -0,0 +1,18 @@ + + + + + + Document + + +

New user

+

fill this form

+
+ + + + +
+ + \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/templates/user_tweets.html b/module3-adding-data-science-to-a-web-application/web_app/templates/user_tweets.html new file mode 100644 index 00000000..7d566366 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/templates/user_tweets.html @@ -0,0 +1,31 @@ + + + + + + Document + + +

{{ username }} tweets:

+ {% if user_tweets %} +
    + {% for tweets in user_tweets %} +

    +

  • {{ tweets.text }}
  • +

    + {% endfor %} +
+ {% else %} +

no tweets!

+ {% endif %} +

Create tweet

+
+ + + + +
+ users list + + + \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/templates/users.html b/module3-adding-data-science-to-a-web-application/web_app/templates/users.html new file mode 100644 index 00000000..78b89975 --- /dev/null +++ b/module3-adding-data-science-to-a-web-application/web_app/templates/users.html @@ -0,0 +1,25 @@ + + + + + + Document + + + + {% if users %} + + {% else %} +

no users!

+ {% endif %} + create new users + + + + \ No newline at end of file diff --git a/module3-adding-data-science-to-a-web-application/web_app/test_db.db b/module3-adding-data-science-to-a-web-application/web_app/test_db.db new file mode 100644 index 00000000..a23e4d7e Binary files /dev/null and b/module3-adding-data-science-to-a-web-application/web_app/test_db.db differ diff --git a/module4-web-application-deployment/web_app/__init__.py b/module4-web-application-deployment/web_app/__init__.py new file mode 100644 index 00000000..6d233d93 --- /dev/null +++ b/module4-web-application-deployment/web_app/__init__.py @@ -0,0 +1,29 @@ +from flask import Flask + +from web_app.routes.home_routes import home_routes +from web_app.routes.book_routes import book_routes +from web_app.routes.user_routes import user_routes +from web_app.routes.twitter_routes import twitter_routes +from web_app.models import db, migrate + + +DATABASE_URI = "sqlite:///test_db.db" + + +def create_app(): + app = Flask(__name__) + + app.config["SQLALCHEMY_DATABASE_URI"] = DATABASE_URI + db.init_app(app) + migrate.init_app(app, db) + + # app.register_blueprint(home_routes) + # app.register_blueprint(book_routes) + app.register_blueprint(user_routes) + app.register_blueprint(twitter_routes) + return app + + +if __name__ == "__main__": + my_app = create_app() + my_app.run(debug=True) \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/iris_classifier.py b/module4-web-application-deployment/web_app/iris_classifier.py new file mode 100644 index 00000000..e42ac9bb --- /dev/null +++ b/module4-web-application-deployment/web_app/iris_classifier.py @@ -0,0 +1,43 @@ +import os +import pickle + +from sklearn.datasets import load_iris +from sklearn.linear_model import LogisticRegression # for example + +MODEL_FILEPATH = os.path.join(os.path.dirname(__file__), "..", "models", "latest_model.pkl") + + +def train_and_save_model(): + print("TRAINING THE MODEL...") + X, y = load_iris(return_X_y=True) + #print(type(X), X.shape) #> (150, 4) + #print(type(y), y.shape) #> (150,) + classifier = LogisticRegression() # for example + classifier.fit(X, y) + + print("SAVING THE MODEL...") + with open(MODEL_FILEPATH, "wb") as model_file: + pickle.dump(classifier, model_file) + + return classifier + + +def load_model(): + print("LOADING THE MODEL...") + with open(MODEL_FILEPATH, "rb") as model_file: + saved_model = pickle.load(model_file) + return saved_model + +if __name__ == "__main__": + + train_and_save_model() + + clf = load_model() + print("CLASSIFIER:", clf) + + X, y = load_iris(return_X_y=True) # just to have some data to use when predicting + inputs = X[:2, :] + print(type(inputs), inputs) + + result = clf.predict(inputs) + print("RESULT:", result) \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/models.py b/module4-web-application-deployment/web_app/models.py new file mode 100644 index 00000000..93c93ee4 --- /dev/null +++ b/module4-web-application-deployment/web_app/models.py @@ -0,0 +1,44 @@ + + +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + + +db = SQLAlchemy() + +migrate = Migrate() + + +class Book(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(128)) + author_id = db.Column(db.String(128)) + + +# class User(db.Model): +# # __table_name__ = "user" +# id = db.Column(db.Integer, primary_key=True) +# username = db.Column(db.String(128)) + + +# class Tweet(db.Model): +# id = db.Column(db.Integer, primary_key=True) +# user_id = db.Column(db.Integer, db.ForeignKey("user.id")) +# text = db.Column(db.String(500)) + + +class User(db.Model): + id = db.Column(db.BigInteger, primary_key=True) + screen_name = db.Column(db.String(128), nullable=False) + name = db.Column(db.String) + location = db.Column(db.String) + followers_count = db.Column(db.Integer) + #latest_tweet_id = db.Column(db.BigInteger) + + +class Tweet(db.Model): + id = db.Column(db.BigInteger, primary_key=True) + user_id = db.Column(db.BigInteger, db.ForeignKey("user.id")) + full_text = db.Column(db.String(500)) + embedding = db.Column(db.PickleType) + user = db.relationship("User", backref=db.backref("tweets", lazy=True)) diff --git a/module4-web-application-deployment/web_app/routes/book_routes.py b/module4-web-application-deployment/web_app/routes/book_routes.py new file mode 100644 index 00000000..5f645447 --- /dev/null +++ b/module4-web-application-deployment/web_app/routes/book_routes.py @@ -0,0 +1,33 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Book, db + +book_routes = Blueprint("book_routes", __name__) + + +@book_routes.route("/books.json") +def list_books_json(): + book_records = Book.query.all() + return jsonify(book_records) + + +@book_routes.route("/books") +def list_books(): + book_records = Book.query.all() + return render_template("books.html", message="Here's some books", books=book_records) + + +@book_routes.route("/books/new") +def new_book(): + return render_template("new_book.html") + + +@book_routes.route("/books/create", methods=["POST"]) +def create_book(): + print("FORM DATA:", dict(request.form)) + new_book = Book(title=request.form["book_title"], author_id=request.form["author_name"]) + + db.session.add(new_book) + db.session.commit() + return redirect(f"/books") \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/routes/home_routes.py b/module4-web-application-deployment/web_app/routes/home_routes.py new file mode 100644 index 00000000..e8cee3bd --- /dev/null +++ b/module4-web-application-deployment/web_app/routes/home_routes.py @@ -0,0 +1,14 @@ +from flask import Blueprint + +home_routes = Blueprint("home_routes", __name__) + + +@home_routes.route("/") +def index(): + x = 2 + 2 + return f"Hello World {x}" + + +@home_routes.route("/about") +def about(): + return "about me" \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/routes/stats_routes.py b/module4-web-application-deployment/web_app/routes/stats_routes.py new file mode 100644 index 00000000..bf3134d7 --- /dev/null +++ b/module4-web-application-deployment/web_app/routes/stats_routes.py @@ -0,0 +1,64 @@ +# web_app/routes/stats_routes.py + +from flask import Blueprint, request, jsonify, render_template + +# from sklearn.linear_model import LogisticRegression # for example + +# from web_app.models import User, Tweet +# from web_app.services.basilica_service import basilica_api_client + +stats_routes = Blueprint("stats_routes", __name__) + +@stats_routes.route("/predict", methods=["POST"]) +def predict(): + print("PREDICT ROUTE...") + print("FORM DATA:", dict(request.form)) + #> {'screen_name_a': 'elonmusk', 'screen_name_b': 's2t2', 'tweet_text': 'Example tweet text here'} + screen_name_a = request.form["screen_name_a"] + screen_name_b = request.form["screen_name_b"] + tweet_text = request.form["tweet_text"] + + # print("-----------------") + # print("FETCHING TWEETS FROM THE DATABASE...") + # # todo: wrap in a try block in case the user's don't exist in the database + # user_a = User.query.filter(User.screen_name == screen_name_a).one() + # user_b = User.query.filter(User.screen_name == screen_name_b).one() + # user_a_tweets = user_a.tweets + # user_b_tweets = user_b.tweets + # #user_a_embeddings = [tweet.embedding for tweet in user_a_tweets] + # #user_b_embeddings = [tweet.embedding for tweet in user_b_tweets] + # print("USER A", user_a.screen_name, len(user_a.tweets)) + # print("USER B", user_b.screen_name, len(user_b.tweets)) + + # print("-----------------") + # print("TRAINING THE MODEL...") + # embeddings = [] + # labels = [] + # for tweet in user_a_tweets: + # labels.append(user_a.screen_name) + # embeddings.append(tweet.embedding) + + # for tweet in user_b_tweets: + # labels.append(user_b.screen_name) + # embeddings.append(tweet.embedding) + + # classifier = LogisticRegression() # for example + # classifier.fit(embeddings, labels) + + # print("-----------------") + # print("MAKING A PREDICTION...") + # #result_a = classifier.predict([user_a_tweets[0].embedding]) + # #result_b = classifier.predict([user_b_tweets[0].embedding]) + + # basilica_api = basilica_api_client() + # example_embedding = basilica_api.embed_sentence(tweet_text) + # result = classifier.predict([example_embedding]) + # #breakpoint() + + # #return jsonify({"message": "RESULTS", "most_likely": result[0]}) + # return render_template("results.html", + screen_name_a=screen_name_a, + screen_name_b=screen_name_b, + tweet_text=tweet_text, + # screen_name_most_likely= result[0] + ) \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/routes/twitter_routes.py b/module4-web-application-deployment/web_app/routes/twitter_routes.py new file mode 100644 index 00000000..d5baa0b3 --- /dev/null +++ b/module4-web-application-deployment/web_app/routes/twitter_routes.py @@ -0,0 +1,50 @@ +from flask import Blueprint, render_template, jsonify +from web_app.services.twitter_service import twitter_api +from web_app.services.basilica_service import basilica_api_client +from web_app.models import User, Tweet, db +from pdb import set_trace as st + +twitter_routes = Blueprint("twitter_routes", __name__) + + +@twitter_routes.route("/users//") +def get_user(screen_name=None): + print(screen_name) + api = twitter_api() + user = api.get_user(screen_name) + statuses = api.user_timeline(screen_name, tweet_mode="extended", count=150, exclude_replies=True, include_rts=False) + # return jsonify({"user": user._json, "tweets": [s._json for s in statuses]}) + + db_user = User.query.get(user.id) or User(id=user.id) + db_user.screen_name = user.screen_name + db_user.name = user.name + db_user.location = user.location + db_user.followers_count = user.followers_count + db.session.add(db_user) + db.session.commit() + #breakpoint() + + all_tweet_texts = [status.full_text for status in statuses] + # st() + basilica_connection = basilica_api_client() + embeddings = list(basilica_connection.embed_sentences(sentences=all_tweet_texts, model="twitter")) + print("NUMBER OF EMBEDDINGS", len(embeddings)) + # TODO: explore using the zip() function maybe... + counter = 0 + for status in statuses: + print(status.full_text) + print("----") + #print(dir(status)) + # get existing tweet from the db or initialize a new one: + db_tweet = Tweet.query.get(status.id) or Tweet(id=status.id) + db_tweet.user_id = status.author.id # or db_user.id + db_tweet.full_text = status.full_text + #embedding = basilica_client.embed_sentence(status.full_text, model="twitter") # todo: prefer to make a single request to basilica with all the tweet texts, instead of a request per tweet + embedding = embeddings[counter] + print(len(embedding)) + db_tweet.embedding = embedding + db.session.add(db_tweet) + counter+=1 + db.session.commit() + #breakpoint() + return "OK" diff --git a/module4-web-application-deployment/web_app/routes/user_routes.py b/module4-web-application-deployment/web_app/routes/user_routes.py new file mode 100644 index 00000000..014f0435 --- /dev/null +++ b/module4-web-application-deployment/web_app/routes/user_routes.py @@ -0,0 +1,54 @@ + +from flask import Blueprint, jsonify, request, render_template, redirect, flash +from pdb import set_trace as st +from web_app.models import Tweet, User, db +from sqlalchemy.orm import joinedload + + +user_routes = Blueprint("user_routes", __name__) + + +@user_routes.route("/") +def index(): + return redirect("/users/") + + +@user_routes.route("/users/") +def list_users(): + user_records = User.query.all() + return render_template("users.html", users=user_records) + + +# @user_routes.route("/users//") +# def list_user_tweets(username=None): +# user = User.query.filter_by(username=username).first() +# user_id = user.id +# user_tweets = Tweet.query.filter_by(user_id=user_id).all() +# # st() +# return render_template("user_tweets.html", username=username, user_tweets=user_tweets) + + +# @user_routes.route("/users//new_tweet", methods=["POST"]) +# def user_create_new_tweet(username=None): +# user = User.query.filter_by(username=username).first() +# # st() +# user_id = user.id +# new_tweet = Tweet(user_id=user_id, text=request.form["tweet_text"]) +# db.session.add(new_tweet) +# db.session.commit() +# return redirect(f"/users/{username}/") + + +@user_routes.route("/users/new/") +def new_user(): + return render_template("new_user.html") + + +@user_routes.route("/users/create", methods=["POST"]) +def create_user(): + new_user = User(screen_name=request.form["username"]) + print("FORM DATA:", dict(request.form)) + db.session.add(new_user) + db.session.commit() + # flash("asdf") + return redirect("/users") diff --git a/module4-web-application-deployment/web_app/services/basilica_service.py b/module4-web-application-deployment/web_app/services/basilica_service.py new file mode 100644 index 00000000..ad22e155 --- /dev/null +++ b/module4-web-application-deployment/web_app/services/basilica_service.py @@ -0,0 +1,26 @@ + +from basilica import Connection +from pdb import set_trace as st +import os +from dotenv import load_dotenv + +load_dotenv() +BASILICA_API_KEY = os.getenv("BASILICA_API_KEY", default="oops") + +# sentences = [ +# "This is a sentence!", +# "This is a similar sentence!", +# "I don't think this sentence is very similar at all...", +# ] +# x = 12 +# connection = Connection(BASILICA_API_KEY) + + +def basilica_api_client(): + connection = Connection(BASILICA_API_KEY) + print(type(connection)) #> + return connection +# embeddings = list(connection.embed_sentences(sentences)) +# # embeddings = list(c.embed_sentence(sentence)) for one sentence +# print(embeddings) +# # st() \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/services/twitter_service.py b/module4-web-application-deployment/web_app/services/twitter_service.py new file mode 100644 index 00000000..0a7df01d --- /dev/null +++ b/module4-web-application-deployment/web_app/services/twitter_service.py @@ -0,0 +1,29 @@ +import tweepy +import os +from dotenv import load_dotenv + +load_dotenv() + +TWITTER_API_KEY = os.getenv("TWITTER_API_KEY") +TWITTER_API_SECRET = os.getenv("TWITTER_API_SECRET") +TWITTER_ACCESS_TOKEN = os.getenv("TWITTER_ACCESS_TOKEN") +TWITTER_ACCESS_TOKEN_SECRET = os.getenv("TWITTER_ACCESS_TOKEN_SECRET") + + +def twitter_api(): + auth = tweepy.OAuthHandler(TWITTER_API_KEY, TWITTER_API_SECRET) + auth.set_access_token(TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET) + print("AUTH", auth) + api = tweepy.API(auth) + print("API", api) + #print(dir(api)) + return api + + +if __name__ == "__main__": + api = twitter_api() + user = api.get_user("elonmusk") + print("USER", user) + print(user.screen_name) + print(user.name) + print(user.followers_count) \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/templates/books.html b/module4-web-application-deployment/web_app/templates/books.html new file mode 100644 index 00000000..d6737885 --- /dev/null +++ b/module4-web-application-deployment/web_app/templates/books.html @@ -0,0 +1,24 @@ + + + + + + Document + + + + {% if books %} +
    + {% for book in books %} +

    +

  • {{ book.title }}
  • +

    + {% endfor %} +
+ {% else %} +

no books!

+ {% endif %} + + + + \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/templates/new_book.html b/module4-web-application-deployment/web_app/templates/new_book.html new file mode 100644 index 00000000..04f7cd98 --- /dev/null +++ b/module4-web-application-deployment/web_app/templates/new_book.html @@ -0,0 +1,24 @@ + + + + + + Document + + +

New Book message

+

fill this form

+
+ + + + + + +
+ + \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/templates/new_user.html b/module4-web-application-deployment/web_app/templates/new_user.html new file mode 100644 index 00000000..8df26f44 --- /dev/null +++ b/module4-web-application-deployment/web_app/templates/new_user.html @@ -0,0 +1,18 @@ + + + + + + Document + + +

New user

+

fill this form

+
+ + + + +
+ + \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/templates/user_tweets.html b/module4-web-application-deployment/web_app/templates/user_tweets.html new file mode 100644 index 00000000..7d566366 --- /dev/null +++ b/module4-web-application-deployment/web_app/templates/user_tweets.html @@ -0,0 +1,31 @@ + + + + + + Document + + +

{{ username }} tweets:

+ {% if user_tweets %} +
    + {% for tweets in user_tweets %} +

    +

  • {{ tweets.text }}
  • +

    + {% endfor %} +
+ {% else %} +

no tweets!

+ {% endif %} +

Create tweet

+
+ + + + +
+ users list + + + \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/templates/users.html b/module4-web-application-deployment/web_app/templates/users.html new file mode 100644 index 00000000..78b89975 --- /dev/null +++ b/module4-web-application-deployment/web_app/templates/users.html @@ -0,0 +1,25 @@ + + + + + + Document + + + + {% if users %} + + {% else %} +

no users!

+ {% endif %} + create new users + + + + \ No newline at end of file diff --git a/module4-web-application-deployment/web_app/test_db.db b/module4-web-application-deployment/web_app/test_db.db new file mode 100644 index 00000000..a23e4d7e Binary files /dev/null and b/module4-web-application-deployment/web_app/test_db.db differ