From a7a02e1e446b8bee6e63be22a0edc33fe88064e3 Mon Sep 17 00:00:00 2001
From: modares
Date: Mon, 18 May 2020 16:02:05 -0700
Subject: [PATCH 1/9] module retro
---
Pipfile | 14 ++
Pipfile.lock | 179 ++++++++++++++++++
.../web_app/__init__.py | 16 ++
.../web_app/routes/book_routes.py | 35 ++++
.../web_app/routes/home_routes.py | 14 ++
.../web_app/templates/books.html | 14 ++
6 files changed, 272 insertions(+)
create mode 100644 Pipfile
create mode 100644 Pipfile.lock
create mode 100644 module1-web-application-development-with-flask/web_app/__init__.py
create mode 100644 module1-web-application-development-with-flask/web_app/routes/book_routes.py
create mode 100644 module1-web-application-development-with-flask/web_app/routes/home_routes.py
create mode 100644 module1-web-application-development-with-flask/web_app/templates/books.html
diff --git a/Pipfile b/Pipfile
new file mode 100644
index 00000000..5aef96e4
--- /dev/null
+++ b/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/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 00000000..cb425b6b
--- /dev/null
+++ b/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..1b2601e4
--- /dev/null
+++ b/module1-web-application-development-with-flask/web_app/__init__.py
@@ -0,0 +1,16 @@
+from flask import Flask
+
+from web_app.routes.home_routes import home_routes
+from web_app.routes.book_routes import book_routes
+
+
+def create_app():
+ app = Flask(__name__)
+ app.register_blueprint(home_routes)
+ app.register_blueprint(book_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/routes/book_routes.py b/module1-web-application-development-with-flask/web_app/routes/book_routes.py
new file mode 100644
index 00000000..39edf639
--- /dev/null
+++ b/module1-web-application-development-with-flask/web_app/routes/book_routes.py
@@ -0,0 +1,35 @@
+
+from flask import Blueprint, jsonify, request, render_template
+
+book_routes = Blueprint("book_routes", __name__)
+
+
+@book_routes.route("/books.json")
+def list_books():
+ books = [
+ {"id": 1, "title": "Book 1"},
+ {"id": 2, "title": "Book 2"},
+ {"id": 3, "title": "Book 3"},
+ ]
+ return jsonify(books)
+
+
+@book_routes.route("/books")
+def list_books():
+ books = [
+ {"id": 1, "title": "Book 1"},
+ {"id": 2, "title": "Book 2"},
+ {"id": 3, "title": "Book 3"},
+ ]
+ return render_template("books.html", message="Here's some books", books=books)
+
+
+# @book_routes.route("/books/new")
+# def new_book():
+# return render_template("new_book.html")
+
+
+
+
+
+
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/templates/books.html b/module1-web-application-development-with-flask/web_app/templates/books.html
new file mode 100644
index 00000000..47415050
--- /dev/null
+++ b/module1-web-application-development-with-flask/web_app/templates/books.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+ Document
+
+
+
+ aaaaaaa
+
+
+
+
\ No newline at end of file
From 3b7a06eb08a52d8422ecdc0133c0cf6963350ff8 Mon Sep 17 00:00:00 2001
From: modares
Date: Tue, 19 May 2020 09:01:57 -0700
Subject: [PATCH 2/9] finished replicating
---
.../.gitignore | 3 +
.../Pipfile | 14 ++
.../Pipfile.lock | 179 ++++++++++++++++++
.../web_app/__init__.py | 9 +
.../web_app/models.py | 21 ++
.../web_app/routes/book_routes.py | 40 ++--
.../web_app/templates/books.html | 12 +-
.../web_app/templates/new_book.html | 24 +++
8 files changed, 285 insertions(+), 17 deletions(-)
create mode 100644 module1-web-application-development-with-flask/.gitignore
create mode 100644 module1-web-application-development-with-flask/Pipfile
create mode 100644 module1-web-application-development-with-flask/Pipfile.lock
create mode 100644 module1-web-application-development-with-flask/web_app/models.py
create mode 100644 module1-web-application-development-with-flask/web_app/templates/new_book.html
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
index 1b2601e4..3b00395e 100644
--- a/module1-web-application-development-with-flask/web_app/__init__.py
+++ b/module1-web-application-development-with-flask/web_app/__init__.py
@@ -2,10 +2,19 @@
from web_app.routes.home_routes import home_routes
from web_app.routes.book_routes import book_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)
return app
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..b10d72b7
--- /dev/null
+++ b/module1-web-application-development-with-flask/web_app/models.py
@@ -0,0 +1,21 @@
+
+
+from flask_sqlalchemy import SQLAlchemy
+from flask_migrate import Migrate
+
+
+db = SQLAlchemy()
+
+migrate = Migrate()
+
+
+class Book(db.Model):
+ __table_name__ = "books"
+ 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__ = "users"
+
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
index 39edf639..d13d4eb0 100644
--- 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
@@ -1,33 +1,41 @@
-from flask import Blueprint, jsonify, request, render_template
+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():
- books = [
- {"id": 1, "title": "Book 1"},
- {"id": 2, "title": "Book 2"},
- {"id": 3, "title": "Book 3"},
- ]
+def list_books_json():
+ book_records = Book.query.all()
return jsonify(books)
@book_routes.route("/books")
def list_books():
- books = [
- {"id": 1, "title": "Book 1"},
- {"id": 2, "title": "Book 2"},
- {"id": 3, "title": "Book 3"},
- ]
- return render_template("books.html", message="Here's some books", books=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/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))
+ # st()
+ new_book = Book(title=request.form["Book_title"], author_id=request.form["author_name"])
+ db.session.add(new_book)
+ db.session.commit()
+ # return jsonify({
+ # "message": "BOOK CREATED OK",
+ # "book": dict(request.form)
+ # })
+ return redirect(f"/books")
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
index 47415050..d6737885 100644
--- a/module1-web-application-development-with-flask/web_app/templates/books.html
+++ b/module1-web-application-development-with-flask/web_app/templates/books.html
@@ -7,7 +7,17 @@
- aaaaaaa
+ {% if books %}
+
+ {% for book in books %}
+
+
- {{ book.title }}
+
+ {% endfor %}
+
+ {% else %}
+ no books!
+ {% endif %}
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
From b30a008439f4514f332f7ec9846ab26ebb865c24 Mon Sep 17 00:00:00 2001
From: modares
Date: Tue, 19 May 2020 13:41:31 -0700
Subject: [PATCH 3/9] module1 works now
---
.../web_app/__init__.py | 2 +
.../web_app/models.py | 12 +-
.../web_app/routes/book_routes.py | 18 +--
.../web_app/routes/user_routes.py | 21 ++++
.../web_app/templates/new_user.html | 18 +++
module2-consuming-data-from-an-api/.gitignore | 1 +
.../Pipfile | 5 +-
.../Pipfile.lock | 111 +++++++++++++++++-
.../web_app/__init__.py | 25 ++++
.../web_app/models.py | 22 ++++
.../web_app/routes/book_routes.py | 43 +++++++
.../web_app/routes/home_routes.py | 14 +++
.../web_app/services/basilica_service.py | 17 +++
.../web_app/templates/books.html | 24 ++++
.../web_app/templates/new_book.html | 24 ++++
.../web_app/test_db.db | Bin 0 -> 16384 bytes
16 files changed, 337 insertions(+), 20 deletions(-)
create mode 100644 module1-web-application-development-with-flask/web_app/routes/user_routes.py
create mode 100644 module1-web-application-development-with-flask/web_app/templates/new_user.html
create mode 100644 module2-consuming-data-from-an-api/.gitignore
rename Pipfile => module2-consuming-data-from-an-api/Pipfile (72%)
rename Pipfile.lock => module2-consuming-data-from-an-api/Pipfile.lock (64%)
create mode 100644 module2-consuming-data-from-an-api/web_app/__init__.py
create mode 100644 module2-consuming-data-from-an-api/web_app/models.py
create mode 100644 module2-consuming-data-from-an-api/web_app/routes/book_routes.py
create mode 100644 module2-consuming-data-from-an-api/web_app/routes/home_routes.py
create mode 100644 module2-consuming-data-from-an-api/web_app/services/basilica_service.py
create mode 100644 module2-consuming-data-from-an-api/web_app/templates/books.html
create mode 100644 module2-consuming-data-from-an-api/web_app/templates/new_book.html
create mode 100644 module2-consuming-data-from-an-api/web_app/test_db.db
diff --git a/module1-web-application-development-with-flask/web_app/__init__.py b/module1-web-application-development-with-flask/web_app/__init__.py
index 3b00395e..3f38b651 100644
--- a/module1-web-application-development-with-flask/web_app/__init__.py
+++ b/module1-web-application-development-with-flask/web_app/__init__.py
@@ -2,6 +2,7 @@
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
@@ -17,6 +18,7 @@ def create_app():
app.register_blueprint(home_routes)
app.register_blueprint(book_routes)
+ app.register_blueprint(user_routes)
return app
diff --git a/module1-web-application-development-with-flask/web_app/models.py b/module1-web-application-development-with-flask/web_app/models.py
index b10d72b7..c716e68c 100644
--- a/module1-web-application-development-with-flask/web_app/models.py
+++ b/module1-web-application-development-with-flask/web_app/models.py
@@ -10,12 +10,18 @@
class Book(db.Model):
- __table_name__ = "books"
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__ = "users"
-
+ # __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
index d13d4eb0..5f645447 100644
--- 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
@@ -9,7 +9,7 @@
@book_routes.route("/books.json")
def list_books_json():
book_records = Book.query.all()
- return jsonify(books)
+ return jsonify(book_records)
@book_routes.route("/books")
@@ -26,18 +26,8 @@ def new_book():
@book_routes.route("/books/create", methods=["POST"])
def create_book():
print("FORM DATA:", dict(request.form))
- # st()
- new_book = Book(title=request.form["Book_title"], author_id=request.form["author_name"])
+ new_book = Book(title=request.form["book_title"], author_id=request.form["author_name"])
+
db.session.add(new_book)
db.session.commit()
-
- # return jsonify({
- # "message": "BOOK CREATED OK",
- # "book": dict(request.form)
- # })
- return redirect(f"/books")
-
-
-
-
-
+ return redirect(f"/books")
\ 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..3370a3a0
--- /dev/null
+++ b/module1-web-application-development-with-flask/web_app/routes/user_routes.py
@@ -0,0 +1,21 @@
+
+from flask import Blueprint, jsonify, request, render_template, redirect, flash
+from pdb import set_trace as st
+from web_app.models import User, db
+
+user_routes = Blueprint("user_routes", __name__)
+
+
+@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/new")
\ 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/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/Pipfile b/module2-consuming-data-from-an-api/Pipfile
similarity index 72%
rename from Pipfile
rename to module2-consuming-data-from-an-api/Pipfile
index 5aef96e4..db207b85 100644
--- a/Pipfile
+++ b/module2-consuming-data-from-an-api/Pipfile
@@ -6,8 +6,11 @@ verify_ssl = true
[dev-packages]
[packages]
+python-dotenv = "*"
+requests = "*"
+basilica = "*"
+tweepy = "*"
flask = "*"
-flask-sqlalchemy = "*"
flask-migrate = "*"
[requires]
diff --git a/Pipfile.lock b/module2-consuming-data-from-an-api/Pipfile.lock
similarity index 64%
rename from Pipfile.lock
rename to module2-consuming-data-from-an-api/Pipfile.lock
index cb425b6b..049829f3 100644
--- a/Pipfile.lock
+++ b/module2-consuming-data-from-an-api/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "0d0656ea395efb10f6c8c22e3976488f199c8929a15bcaa17f644ef4ec76bdd6"
+ "sha256": "8a95d2cb6f3681bad4e791c08fae5a42a54bf81565d3f42ae10847341ffc101d"
},
"pipfile-spec": 6,
"requires": {
@@ -22,6 +22,27 @@
],
"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",
@@ -50,9 +71,15 @@
"sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327",
"sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d"
],
- "index": "pypi",
"version": "==2.4.1"
},
+ "idna": {
+ "hashes": [
+ "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
+ "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
+ ],
+ "version": "==2.9"
+ },
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
@@ -112,6 +139,48 @@
],
"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",
@@ -119,6 +188,14 @@
],
"version": "==2.8.1"
},
+ "python-dotenv": {
+ "hashes": [
+ "sha256:25c0ff1a3e12f4bde8d592cc254ab075cfe734fc5dd989036716fd17ee7e5ec7",
+ "sha256:3b9909bc96b0edc6b01586e1eed05e71174ef4e04c71da5786370cebea53ad74"
+ ],
+ "index": "pypi",
+ "version": "==0.13.0"
+ },
"python-editor": {
"hashes": [
"sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d",
@@ -127,6 +204,21 @@
],
"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",
@@ -167,6 +259,21 @@
],
"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",
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..3b00395e
--- /dev/null
+++ b/module2-consuming-data-from-an-api/web_app/__init__.py
@@ -0,0 +1,25 @@
+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.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)
+ 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..1d06460b
--- /dev/null
+++ b/module2-consuming-data-from-an-api/web_app/models.py
@@ -0,0 +1,22 @@
+
+
+from flask_sqlalchemy import SQLAlchemy
+from flask_migrate import Migrate
+
+
+db = SQLAlchemy()
+
+migrate = Migrate()
+
+
+class Book(db.Model):
+ __table_name__ = "books"
+ 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__ = "users"
+ id = db.Column(db.Integer, primary_key=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..d13d4eb0
--- /dev/null
+++ b/module2-consuming-data-from-an-api/web_app/routes/book_routes.py
@@ -0,0 +1,43 @@
+
+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(books)
+
+
+@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))
+ # st()
+ new_book = Book(title=request.form["Book_title"], author_id=request.form["author_name"])
+ db.session.add(new_book)
+ db.session.commit()
+
+ # return jsonify({
+ # "message": "BOOK CREATED OK",
+ # "book": dict(request.form)
+ # })
+ return redirect(f"/books")
+
+
+
+
+
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..e8cee3bd
--- /dev/null
+++ b/module2-consuming-data-from-an-api/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/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..68b954d4
--- /dev/null
+++ b/module2-consuming-data-from-an-api/web_app/services/basilica_service.py
@@ -0,0 +1,17 @@
+import basilica
+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...",
+]
+
+with basilica.Connection(BASILICA_API_KEY) as c:
+ embeddings = list(c.embed_sentences(sentences))
+
+print(embeddings)
\ 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/test_db.db b/module2-consuming-data-from-an-api/web_app/test_db.db
new file mode 100644
index 0000000000000000000000000000000000000000..b8b9f7f72bea3a12473d1402048cd9a38c6bfdfa
GIT binary patch
literal 16384
zcmeI%K~IA)7zW@L&f2CC+q8Ur=%wN8)C!Y;wWEI5PgsS_6H3W0V}y_?o+X~YlE5Fv
z>-4MRhjNOnE|{U8h;9Ii}&7%r*8blcg=Jq{v~5)os_@HyvglyUaeftQDrnNtkp3c40WR1H&ol
z>swmd>fa?-QQzZZvOOnG{m5`G*`ayKN?}V=w1Gx*#d4WG%EKu9PS9(hh=8)ZmyebBE+%zc&t0Zbn&!IN!V)z
z5Akh>pW;2f)@WGwvXkkV29a;InRL75d8X
Date: Tue, 19 May 2020 14:51:03 -0700
Subject: [PATCH 4/9] assignment 1 finished
---
.../web_app/routes/user_routes.py | 33 +++++++++++++++++--
.../web_app/templates/user_tweets.html | 31 +++++++++++++++++
.../web_app/templates/users.html | 25 ++++++++++++++
3 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 module1-web-application-development-with-flask/web_app/templates/user_tweets.html
create mode 100644 module1-web-application-development-with-flask/web_app/templates/users.html
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
index 3370a3a0..77e4e28c 100644
--- 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
@@ -1,11 +1,40 @@
from flask import Blueprint, jsonify, request, render_template, redirect, flash
from pdb import set_trace as st
-from web_app.models import User, db
+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")
@@ -18,4 +47,4 @@ def create_user():
db.session.add(new_user)
db.session.commit()
# flash("asdf")
- return redirect("/users/new")
\ No newline at end of file
+ return redirect("/users")
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 %}
+
+ {% 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/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
From 510fd41155a45fa279d0ee15294595f4c5eb9dee Mon Sep 17 00:00:00 2001
From: modares
Date: Tue, 19 May 2020 14:52:29 -0700
Subject: [PATCH 5/9] missed one single '/'
---
.../web_app/routes/user_routes.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
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
index 77e4e28c..132e9c3e 100644
--- 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
@@ -34,8 +34,7 @@ def user_create_new_tweet(username=None):
return redirect(f"/users/{username}/")
-
-@user_routes.route("/users/new")
+@user_routes.route("/users/new/")
def new_user():
return render_template("new_user.html")
From 3e740f051413381d0183bdb252d56a9b100056f7 Mon Sep 17 00:00:00 2001
From: modares
Date: Wed, 20 May 2020 21:24:03 -0700
Subject: [PATCH 6/9] module 2 retro
---
Pipfile | 11 ++
.../migrations/README | 1 +
.../migrations/alembic.ini | 45 ++++++++
.../migrations/env.py | 96 ++++++++++++++++++
.../migrations/script.py.mako | 24 +++++
.../migrations/versions/153f1fd5ddf9_.py | 51 ++++++++++
.../web_app/__init__.py | 4 +
.../web_app/models.py | 30 +++++-
.../web_app/routes/book_routes.py | 18 +---
.../web_app/routes/twitter_routes.py | 48 +++++++++
.../web_app/routes/user_routes.py | 49 +++++++++
.../web_app/services/basilica_service.py | 15 +--
.../web_app/services/twitter_service.py | 29 ++++++
.../web_app/templates/new_user.html | 18 ++++
.../web_app/templates/user_tweets.html | 31 ++++++
.../web_app/templates/users.html | 25 +++++
.../web_app/test_db.db | Bin 16384 -> 32768 bytes
17 files changed, 471 insertions(+), 24 deletions(-)
create mode 100644 Pipfile
create mode 100644 module2-consuming-data-from-an-api/migrations/README
create mode 100644 module2-consuming-data-from-an-api/migrations/alembic.ini
create mode 100644 module2-consuming-data-from-an-api/migrations/env.py
create mode 100644 module2-consuming-data-from-an-api/migrations/script.py.mako
create mode 100644 module2-consuming-data-from-an-api/migrations/versions/153f1fd5ddf9_.py
create mode 100644 module2-consuming-data-from-an-api/web_app/routes/twitter_routes.py
create mode 100644 module2-consuming-data-from-an-api/web_app/routes/user_routes.py
create mode 100644 module2-consuming-data-from-an-api/web_app/services/twitter_service.py
create mode 100644 module2-consuming-data-from-an-api/web_app/templates/new_user.html
create mode 100644 module2-consuming-data-from-an-api/web_app/templates/user_tweets.html
create mode 100644 module2-consuming-data-from-an-api/web_app/templates/users.html
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/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
index 3b00395e..b3d92c97 100644
--- a/module2-consuming-data-from-an-api/web_app/__init__.py
+++ b/module2-consuming-data-from-an-api/web_app/__init__.py
@@ -2,6 +2,8 @@
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
@@ -17,6 +19,8 @@ def create_app():
app.register_blueprint(home_routes)
app.register_blueprint(book_routes)
+ app.register_blueprint(user_routes)
+ app.register_blueprint(twitter_routes)
return app
diff --git a/module2-consuming-data-from-an-api/web_app/models.py b/module2-consuming-data-from-an-api/web_app/models.py
index 1d06460b..93c93ee4 100644
--- a/module2-consuming-data-from-an-api/web_app/models.py
+++ b/module2-consuming-data-from-an-api/web_app/models.py
@@ -10,13 +10,35 @@
class Book(db.Model):
- __table_name__ = "books"
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):
- __table_name__ = "users"
- id = db.Column(db.Integer, primary_key=True)
-
+ 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
index d13d4eb0..5f645447 100644
--- 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
@@ -9,7 +9,7 @@
@book_routes.route("/books.json")
def list_books_json():
book_records = Book.query.all()
- return jsonify(books)
+ return jsonify(book_records)
@book_routes.route("/books")
@@ -26,18 +26,8 @@ def new_book():
@book_routes.route("/books/create", methods=["POST"])
def create_book():
print("FORM DATA:", dict(request.form))
- # st()
- new_book = Book(title=request.form["Book_title"], author_id=request.form["author_name"])
+ new_book = Book(title=request.form["book_title"], author_id=request.form["author_name"])
+
db.session.add(new_book)
db.session.commit()
-
- # return jsonify({
- # "message": "BOOK CREATED OK",
- # "book": dict(request.form)
- # })
- return redirect(f"/books")
-
-
-
-
-
+ return redirect(f"/books")
\ 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..77bd0d25
--- /dev/null
+++ b/module2-consuming-data-from-an-api/web_app/routes/twitter_routes.py
@@ -0,0 +1,48 @@
+from flask import Blueprint, render_template, jsonify
+from web_app.services.twitter_service import twitter_api
+from web_app.services.basilica_service import Connection as basilica_connection
+from web_app.models import User, Tweet, db
+
+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]
+ embeddings = list(basilica_connection.embed_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..3c94e5ef
--- /dev/null
+++ b/module2-consuming-data-from-an-api/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/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
index 68b954d4..11bced6a 100644
--- 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
@@ -1,4 +1,6 @@
-import basilica
+
+from basilica import Connection
+from pdb import set_trace as st
import os
from dotenv import load_dotenv
@@ -10,8 +12,9 @@
"This is a similar sentence!",
"I don't think this sentence is very similar at all...",
]
-
-with basilica.Connection(BASILICA_API_KEY) as c:
- embeddings = list(c.embed_sentences(sentences))
-
-print(embeddings)
\ No newline at end of file
+# x = 12
+connection = Connection('2d39d84b-a0e9-717a-361e-8ce5d55d7123')
+# 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/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/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
index b8b9f7f72bea3a12473d1402048cd9a38c6bfdfa..808295bab26e05a6eb606f7b0237a917e38a4548 100644
GIT binary patch
delta 701
zcmZo@U~Fh$njkI6&cMLH0mLxCGEv7^nw>#UW&bbv4`iq9bC;PO~;
za#3n(UVL6+ZYta(hDH{eD85I?0(}AUPfmVvVo7Fx9!v{3K=N~P^2<|;isO^>OY=$;
zfI;Z$?h00p?tLyzbyhZUYf+GoxIylVHw2Mvo7?$v1$hijjnfR%QcP1)(kwSAeBl>D
z7T^U%zQATdf&2UuCrI;vxJ>+882In=ZvhIf;E&K`Vq=k1muKd+_`~a1T3nKv2O$+4
zb8?DOQ$jLQ75o#6Gm9C&8p|`XvB=8vvM?=5(oN0D&&w?>&UOXT3cf%F6QA^PF0kqE
k8TfzjzlT^Ez#`7U%)$sVoQa*8g%hL|)k$n1O$ZkP0Im)DG*QP`n3X|KW&Ct8|qQux9zfh@oaG>D1+83X?p{%1fvxA>)am{=Gj1rv)?(uxxu
O4Y?Ru7(~H5M?(PbMI875
From 129399357521bcea80188bd0468f24ec0fce838f Mon Sep 17 00:00:00 2001
From: modares
Date: Wed, 20 May 2020 22:12:30 -0700
Subject: [PATCH 7/9] finished assigment module 2
---
.../web_app/__init__.py | 4 ++--
.../web_app/routes/twitter_routes.py | 10 +++++----
.../web_app/routes/user_routes.py | 21 +++++++++++-------
.../web_app/services/basilica_service.py | 18 ++++++++++-----
.../web_app/test_db.db | Bin 32768 -> 163840 bytes
5 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/module2-consuming-data-from-an-api/web_app/__init__.py b/module2-consuming-data-from-an-api/web_app/__init__.py
index b3d92c97..6d233d93 100644
--- a/module2-consuming-data-from-an-api/web_app/__init__.py
+++ b/module2-consuming-data-from-an-api/web_app/__init__.py
@@ -17,8 +17,8 @@ def create_app():
db.init_app(app)
migrate.init_app(app, db)
- app.register_blueprint(home_routes)
- app.register_blueprint(book_routes)
+ # app.register_blueprint(home_routes)
+ # app.register_blueprint(book_routes)
app.register_blueprint(user_routes)
app.register_blueprint(twitter_routes)
return app
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
index 77bd0d25..ea1d6ef7 100644
--- 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
@@ -1,12 +1,13 @@
from flask import Blueprint, render_template, jsonify
from web_app.services.twitter_service import twitter_api
-from web_app.services.basilica_service import Connection as basilica_connection
+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/")
+@twitter_routes.route("/users//")
def get_user(screen_name=None):
print(screen_name)
api = twitter_api()
@@ -24,9 +25,10 @@ def get_user(screen_name=None):
#breakpoint()
all_tweet_texts = [status.full_text for status in statuses]
- embeddings = list(basilica_connection.embed_sentences(all_tweet_texts, model="twitter"))
+ 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:
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
index 3c94e5ef..014f0435 100644
--- 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
@@ -8,19 +8,24 @@
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//")
+# 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"])
@@ -41,7 +46,7 @@ def new_user():
@user_routes.route("/users/create", methods=["POST"])
def create_user():
- new_user = User(username=request.form["username"])
+ new_user = User(screen_name=request.form["username"])
print("FORM DATA:", dict(request.form))
db.session.add(new_user)
db.session.commit()
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
index 11bced6a..ad22e155 100644
--- 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
@@ -7,13 +7,19 @@
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...",
-]
+# 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('2d39d84b-a0e9-717a-361e-8ce5d55d7123')
+# 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)
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
index 808295bab26e05a6eb606f7b0237a917e38a4548..40b47583a2fe006fa94adbd223659ce71fcca330 100644
GIT binary patch
literal 163840
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 |