diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..cf82d289 Binary files /dev/null and b/.DS_Store differ diff --git a/mod_1_assign/Pipfile b/mod_1_assign/Pipfile new file mode 100644 index 00000000..776e8a14 --- /dev/null +++ b/mod_1_assign/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.7" diff --git a/mod_1_assign/Pipfile.lock b/mod_1_assign/Pipfile.lock new file mode 100644 index 00000000..ef01ef38 --- /dev/null +++ b/mod_1_assign/Pipfile.lock @@ -0,0 +1,179 @@ +{ + "_meta": { + "hash": { + "sha256": "3c1deb6380d9107dcb7ab44fcdad47a0f792c66a16c3b0f2dec2379d6767243f" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "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/mod_1_assign/README.md b/mod_1_assign/README.md new file mode 100644 index 00000000..3e4ba854 --- /dev/null +++ b/mod_1_assign/README.md @@ -0,0 +1,28 @@ +# mod_1_assign + +## Installation + +TODO: clone the repo + +## Setup + +```sh +# Windows users can omit the "FLASK_APP=web_app" part... + +FLASK_APP=web_app flask db init #> generates app/migrations dir + +# run both when changing the schema: +FLASK_APP=web_app flask db migrate #> creates the db (with "alembic_version" table) +FLASK_APP=web_app flask db upgrade #> creates the specified tables +``` + +## Usage + +```sh +# mac version: +FLASK_APP=web_app flask run + +# windows version: +set FLASK_APP=web_app +flask run +``` \ No newline at end of file diff --git a/mod_1_assign/assign_app.db b/mod_1_assign/assign_app.db new file mode 100644 index 00000000..a9e15d57 Binary files /dev/null and b/mod_1_assign/assign_app.db differ diff --git a/mod_1_assign/migrations/README b/mod_1_assign/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/mod_1_assign/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/mod_1_assign/migrations/alembic.ini b/mod_1_assign/migrations/alembic.ini new file mode 100644 index 00000000..f8ed4801 --- /dev/null +++ b/mod_1_assign/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/mod_1_assign/migrations/env.py b/mod_1_assign/migrations/env.py new file mode 100644 index 00000000..94521792 --- /dev/null +++ b/mod_1_assign/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/mod_1_assign/migrations/script.py.mako b/mod_1_assign/migrations/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/mod_1_assign/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/mod_1_assign/migrations/versions/43739554e67f_.py b/mod_1_assign/migrations/versions/43739554e67f_.py new file mode 100644 index 00000000..e85e67e1 --- /dev/null +++ b/mod_1_assign/migrations/versions/43739554e67f_.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: 43739554e67f +Revises: +Create Date: 2020-05-18 23:40:00.579918 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '43739554e67f' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('handle', sa.String(length=128), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('users') + # ### end Alembic commands ### diff --git a/mod_1_assign/migrations/versions/e77b307c6cf4_.py b/mod_1_assign/migrations/versions/e77b307c6cf4_.py new file mode 100644 index 00000000..e8342fce --- /dev/null +++ b/mod_1_assign/migrations/versions/e77b307c6cf4_.py @@ -0,0 +1,34 @@ +"""empty message + +Revision ID: e77b307c6cf4 +Revises: 43739554e67f +Create Date: 2020-05-19 13:40:41.944248 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'e77b307c6cf4' +down_revision = '43739554e67f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('tweets', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('tweet', sa.String(length=500), nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('tweets') + # ### end Alembic commands ### diff --git a/mod_1_assign/web_app/__init__.py b/mod_1_assign/web_app/__init__.py new file mode 100644 index 00000000..dc19db51 --- /dev/null +++ b/mod_1_assign/web_app/__init__.py @@ -0,0 +1,29 @@ +# web_app/__init__.py + +from flask import Flask + +from web_app.models import db, migrate +from web_app.routes.home_routes import home_routes +from web_app.routes.users_routes import users_routes +from web_app.routes.tweets_routes import tweets_routes + +#DATABASE_URI = "sqlite:///web_app_99.db" # using relative filepath +DATABASE_URI = "sqlite:////Users/Daniel/Desktop/Lambda/3_DS_Data_Engineering/3_Productization_and_Cloud/DS-Unit-3-Sprint-3-Productization-and-Cloud/mod_1_assign/assign_app.db" # using absolute filepath on Mac (recommended) +#DATABASE_URI = "sqlite:///C:\\Users\\Username\\Desktop\\your-repo-name\\web_app_99.db" # using absolute filepath on Windows (recommended) h/t: https://stackoverflow.com/a/19262231/670433 + +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(users_routes) + app.register_blueprint(tweets_routes) + + return app + +if __name__ == "__main__": + my_app = create_app() + my_app.run(debug=True) \ No newline at end of file diff --git a/mod_1_assign/web_app/models.py b/mod_1_assign/web_app/models.py new file mode 100644 index 00000000..e32c60da --- /dev/null +++ b/mod_1_assign/web_app/models.py @@ -0,0 +1,39 @@ +# web_app/models.py + +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate + +db = SQLAlchemy() + +migrate = Migrate() + +class Users(db.Model): + __table_name__ = "users" + id = db.Column(db.Integer, primary_key=True) + handle = db.Column(db.String(128)) + +class Tweets(db.Model): + __table_name__ = "tweets" + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey("users.id")) + tweet = db.Column(db.String(500)) + +def parse_records(database_records): + """ + A helper method for converting a list of database record objects into a list of dictionaries, so they can be returned as JSON + Param: database_records (a list of db.Model instances) + Example: parse_records(User.query.all()) + Returns: a list of dictionaries, each corresponding to a record, like... + [ + {"id": 1, "title": "Book 1"}, + {"id": 2, "title": "Book 2"}, + {"id": 3, "title": "Book 3"}, + ] + """ + parsed_records = [] + for record in database_records: + print(record) + parsed_record = record.__dict__ + del parsed_record["_sa_instance_state"] + parsed_records.append(parsed_record) + return parsed_records \ No newline at end of file diff --git a/mod_1_assign/web_app/routes/home_routes.py b/mod_1_assign/web_app/routes/home_routes.py new file mode 100644 index 00000000..e297cc20 --- /dev/null +++ b/mod_1_assign/web_app/routes/home_routes.py @@ -0,0 +1,13 @@ +# web_app/routes/home_routes.py + +from flask import Blueprint + +home_routes = Blueprint("home_routes", __name__) + +@home_routes.route("/") +def index(): + return "Welcome to our twitoff, hosted by lambda school students!" + +@home_routes.route("/about") +def about(): + return "Here we will be creating a dataframe of usernames and a dataframe of their tweets." \ No newline at end of file diff --git a/mod_1_assign/web_app/routes/tweets_routes.py b/mod_1_assign/web_app/routes/tweets_routes.py new file mode 100644 index 00000000..e601f622 --- /dev/null +++ b/mod_1_assign/web_app/routes/tweets_routes.py @@ -0,0 +1,49 @@ +# web_app/routes/tweets_routes.py + +from flask import Blueprint, jsonify, request, render_template, flash, redirect + +from web_app.models import Tweets, db + +tweets_routes = Blueprint("tweets_routes", __name__) + +@tweets_routes.route("/tweets.json") +@tweets_routes.route("/tweets_endpoint") +def list_tweets(): + tweets = [ + {"id": 1, "user_id" : 1, "tweet": "Boring Company completes 2nd Vegas tunnel https://t.co/13Hy3toyJR"}, + {"id": 2, "user_id" : 2, "tweet": "The best way to honor the legacy of Dr. Martin Luther King Jr. is to ensure kids understand his purpose, message and impact so that they can carry it forward. This is a great way to start teaching them. #MLKDay https://t.co/U5mWmgOFts"}, + {"id": 3, "user_id" : 3, "tweet": "I want to live in a world where a Chicken can cross the road without anybody questioning its motives."}, + ] + return jsonify(tweets) + +@tweets_routes.route("/tweets") +def list_tweets_for_humans(): + # tweets = [ + # {"id": 1, "user_id" : 1, "tweet": "Boring Company completes 2nd Vegas tunnel https://t.co/13Hy3toyJR"}, + # {"id": 2, "user_id" : 2, "tweet": "The best way to honor the legacy of Dr. Martin Luther King Jr. is to ensure kids understand his purpose, message and impact so that they can carry it forward. This is a great way to start teaching them. #MLKDay https://t.co/U5mWmgOFts"}, + # {"id": 3, "user_id" : 3, "tweet": "I want to live in a world where a Chicken can cross the road without anybody questioning its motives."}, + # ] + + tweets_records = Tweets.query.all() + print(tweets_records) + + return render_template("tweets.html", message="Here's some tweets", tweets=tweets_records) + +@tweets_routes.route("/tweets/new") +def new_tweet(): + return render_template("new_tweet.html") + +@tweets_routes.route("/tweets/create", methods=["POST"]) +def create_tweet(): + print("FORM DATA:", dict(request.form)) + + new_tweet = Tweets(tweet=request.form["tweet"]) + db.session.add(new_tweet) + db.session.commit() + + return jsonify({ + "message": "TWEET CREATED OK", + "tweet": dict(request.form) + }) + #flash(f"Book '{new_book.title}' created successfully!", "success") + #return redirect("/books") \ No newline at end of file diff --git a/mod_1_assign/web_app/routes/users_routes.py b/mod_1_assign/web_app/routes/users_routes.py new file mode 100644 index 00000000..4fae3c27 --- /dev/null +++ b/mod_1_assign/web_app/routes/users_routes.py @@ -0,0 +1,49 @@ +# web_app/routes/users_routes.py + +from flask import Blueprint, jsonify, request, render_template, flash, redirect + +from web_app.models import Users, db + +users_routes = Blueprint("users_routes", __name__) + +@users_routes.route("/users.json") +@users_routes.route("/users_endpoint") +def list_users(): + users = [ + {"id": 1, "handle": "@elonmusk"}, + {"id": 2, "handle": "@kobebryant"}, + {"id": 3, "handle": "@neiltyson"}, + ] + return jsonify(users) + +@users_routes.route("/users") +def list_users_for_humans(): + #users = [ + # {"id": 1, "handle": "@elonmusk"}, + # {"id": 2, "handle": "@kobebryant"}, + # {"id": 3, "handle": "@neiltyson"}, + #] + + users_records = Users.query.all() + print(users_records) + + return render_template("users.html", message="Here's some users", users=users_records) + +@users_routes.route("/users/new") +def new_user(): + return render_template("new_user.html") + +@users_routes.route("/users/create", methods=["POST"]) +def create_user(): + print("FORM DATA:", dict(request.form)) + + new_user = Users(handle=request.form["handle"]) + db.session.add(new_user) + db.session.commit() + + return jsonify({ + "message": "USER CREATED OK", + "user": dict(request.form) + }) + #flash(f"Book '{new_book.title}' created successfully!", "success") + #return redirect("/books") \ No newline at end of file diff --git a/mod_1_assign/web_app/templates/new_tweet.html b/mod_1_assign/web_app/templates/new_tweet.html new file mode 100644 index 00000000..97ce8738 --- /dev/null +++ b/mod_1_assign/web_app/templates/new_tweet.html @@ -0,0 +1,29 @@ + + +
+ + +Please fill out the form below to add a new tweet to the database:
+ + + + + + \ No newline at end of file diff --git a/mod_1_assign/web_app/templates/new_user.html b/mod_1_assign/web_app/templates/new_user.html new file mode 100644 index 00000000..a3f3f1f9 --- /dev/null +++ b/mod_1_assign/web_app/templates/new_user.html @@ -0,0 +1,25 @@ + + + + + +Please fill out the form below to add a new user to the database:
+ + + + + + \ No newline at end of file diff --git a/mod_1_assign/web_app/templates/tweets.html b/mod_1_assign/web_app/templates/tweets.html new file mode 100644 index 00000000..34e7976d --- /dev/null +++ b/mod_1_assign/web_app/templates/tweets.html @@ -0,0 +1,29 @@ + + + + + +{{ message }}
+ + {% if tweets %} +Tweets not found.
+ {% endif %} + + + + \ No newline at end of file diff --git a/mod_1_assign/web_app/templates/users.html b/mod_1_assign/web_app/templates/users.html new file mode 100644 index 00000000..f58bfc50 --- /dev/null +++ b/mod_1_assign/web_app/templates/users.html @@ -0,0 +1,29 @@ + + + + + +{{ message }}
+ + {% if users %} +Users not found.
+ {% endif %} + + + + \ No newline at end of file diff --git a/sprint_3_lecture/.DS_Store b/sprint_3_lecture/.DS_Store new file mode 100644 index 00000000..d7b9ccc5 Binary files /dev/null and b/sprint_3_lecture/.DS_Store differ diff --git a/sprint_3_lecture/.gitignore b/sprint_3_lecture/.gitignore new file mode 100644 index 00000000..b96df00f --- /dev/null +++ b/sprint_3_lecture/.gitignore @@ -0,0 +1,9 @@ + + +# ignore dev database stuff: +migrations/ +twitoff_app.db + +# Ignore secret credentials +.env + diff --git a/sprint_3_lecture/Pipfile b/sprint_3_lecture/Pipfile new file mode 100644 index 00000000..ed84cee3 --- /dev/null +++ b/sprint_3_lecture/Pipfile @@ -0,0 +1,21 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +flask = "*" +flask-sqlalchemy = "*" +flask-migrate = "*" +python-dotenv = "*" +requests = "*" +basilica = "*" +tweepy = "*" +scikit-learn = "*" +gunicorn = "*" +psycopg2 = "*" + +[requires] +python_version = "3.7" diff --git a/sprint_3_lecture/Pipfile.lock b/sprint_3_lecture/Pipfile.lock new file mode 100644 index 00000000..51363f3d --- /dev/null +++ b/sprint_3_lecture/Pipfile.lock @@ -0,0 +1,402 @@ +{ + "_meta": { + "hash": { + "sha256": "eabf16ad536fafba212936d1871ee77d1b4ebd61736ded42b62e2840549a42a2" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "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" + ], + "index": "pypi", + "version": "==2.4.1" + }, + "gunicorn": { + "hashes": [ + "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626", + "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c" + ], + "index": "pypi", + "version": "==20.0.4" + }, + "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" + }, + "psycopg2": { + "hashes": [ + "sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535", + "sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7", + "sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c", + "sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080", + "sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81", + "sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72", + "sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52", + "sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e", + "sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf", + "sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9", + "sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb", + "sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055", + "sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818" + ], + "index": "pypi", + "version": "==2.8.5" + }, + "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" + ], + "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/sprint_3_lecture/README.md b/sprint_3_lecture/README.md new file mode 100644 index 00000000..53923a17 --- /dev/null +++ b/sprint_3_lecture/README.md @@ -0,0 +1,22 @@ +# twitoff-14 +## Installation +TODO: clone the repo +## Usage +```sh +# mac version: +FLASK_APP=twitoff_app flask run +# windows version: +set FLASK_APP=twitoff_app +flask run +``` + +# database creation +``` +# Windows users can omit the "FLASK_APP=twitoff_app" part... + +FLASK_APP=twitoff_app flask db init #> generates app/migrations dir + +# run both when changing the schema: +FLASK_APP=twitoff_app flask db migrate #> creates the db (with "alembic_version" table) +FLASK_APP=twitoff_app flask db upgrade #> creates the specified tables +``` \ No newline at end of file diff --git a/sprint_3_lecture/migrations/.DS_Store b/sprint_3_lecture/migrations/.DS_Store new file mode 100644 index 00000000..1cb0f9ab Binary files /dev/null and b/sprint_3_lecture/migrations/.DS_Store differ diff --git a/sprint_3_lecture/migrations/README b/sprint_3_lecture/migrations/README new file mode 100644 index 00000000..98e4f9c4 --- /dev/null +++ b/sprint_3_lecture/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/sprint_3_lecture/migrations/alembic.ini b/sprint_3_lecture/migrations/alembic.ini new file mode 100644 index 00000000..f8ed4801 --- /dev/null +++ b/sprint_3_lecture/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/sprint_3_lecture/migrations/env.py b/sprint_3_lecture/migrations/env.py new file mode 100644 index 00000000..94521792 --- /dev/null +++ b/sprint_3_lecture/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/sprint_3_lecture/migrations/script.py.mako b/sprint_3_lecture/migrations/script.py.mako new file mode 100644 index 00000000..2c015630 --- /dev/null +++ b/sprint_3_lecture/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/sprint_3_lecture/migrations/versions/6a47e2311fd4_.py b/sprint_3_lecture/migrations/versions/6a47e2311fd4_.py new file mode 100644 index 00000000..2b87541f --- /dev/null +++ b/sprint_3_lecture/migrations/versions/6a47e2311fd4_.py @@ -0,0 +1,44 @@ +"""empty message + +Revision ID: 6a47e2311fd4 +Revises: 8a3026cffd18 +Create Date: 2020-05-19 12:37:18.169862 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6a47e2311fd4' +down_revision = '8a3026cffd18' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + 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') + # ### end Alembic commands ### diff --git a/sprint_3_lecture/migrations/versions/8a3026cffd18_.py b/sprint_3_lecture/migrations/versions/8a3026cffd18_.py new file mode 100644 index 00000000..1acbeefd --- /dev/null +++ b/sprint_3_lecture/migrations/versions/8a3026cffd18_.py @@ -0,0 +1,33 @@ +"""empty message + +Revision ID: 8a3026cffd18 +Revises: +Create Date: 2020-05-18 14:11:15.935171 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8a3026cffd18' +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') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('book') + # ### end Alembic commands ### diff --git a/sprint_3_lecture/models/latest_model.pkl b/sprint_3_lecture/models/latest_model.pkl new file mode 100644 index 00000000..8632d8f5 Binary files /dev/null and b/sprint_3_lecture/models/latest_model.pkl differ diff --git a/sprint_3_lecture/twitoff_app.db b/sprint_3_lecture/twitoff_app.db new file mode 100644 index 00000000..058a30f9 Binary files /dev/null and b/sprint_3_lecture/twitoff_app.db differ diff --git a/sprint_3_lecture/twitoff_app/.DS_Store b/sprint_3_lecture/twitoff_app/.DS_Store new file mode 100644 index 00000000..a922b264 Binary files /dev/null and b/sprint_3_lecture/twitoff_app/.DS_Store differ diff --git a/sprint_3_lecture/twitoff_app/__init__.py b/sprint_3_lecture/twitoff_app/__init__.py new file mode 100644 index 00000000..bc784b2f --- /dev/null +++ b/sprint_3_lecture/twitoff_app/__init__.py @@ -0,0 +1,30 @@ +# twitoff_app/__init__.py + +from flask import Flask +from twitoff_app.models import db, migrate +from twitoff_app.routes.home_routes import home_routes +from twitoff_app.routes.book_routes import book_routes +from twitoff_app.routes.twitter_routes import twitter_routes +from twitoff_app.routes.stats_routes import stats_routes + +#DATABASE_URI = "sqlite:///web_app_99.db" # using relative filepath +DATABASE_URI = "sqlite:////Users/Daniel/Desktop/Lambda/3_DS_Data_Engineering/3_Productization_and_Cloud/DS-Unit-3-Sprint-3-Productization-and-Cloud/sprint_3_lecture/twitoff_app.db" # using absolute filepath on Mac (recommended) + +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(twitter_routes) + app.register_blueprint(stats_routes) + + return app + +if __name__ == "__main__": + my_app = create_app() + my_app.run(debug=True) + diff --git a/sprint_3_lecture/twitoff_app/classifier.py b/sprint_3_lecture/twitoff_app/classifier.py new file mode 100644 index 00000000..5058d2f0 --- /dev/null +++ b/sprint_3_lecture/twitoff_app/classifier.py @@ -0,0 +1,47 @@ +# twitoff_app/classifier.py + + +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) #>{{ message }}
+ + {% if books %} +Books not found.
+ {% endif %} + + + + \ No newline at end of file diff --git a/sprint_3_lecture/twitoff_app/templates/bootstrap_layout.html b/sprint_3_lecture/twitoff_app/templates/bootstrap_layout.html new file mode 100644 index 00000000..4158a9a5 --- /dev/null +++ b/sprint_3_lecture/twitoff_app/templates/bootstrap_layout.html @@ -0,0 +1,92 @@ + + + + + + {% block title %} +Please fill out the form below to add a new book to the database:
+ + + + + + \ No newline at end of file diff --git a/sprint_3_lecture/twitoff_app/templates/prediction_form.html b/sprint_3_lecture/twitoff_app/templates/prediction_form.html new file mode 100644 index 00000000..206101e5 --- /dev/null +++ b/sprint_3_lecture/twitoff_app/templates/prediction_form.html @@ -0,0 +1,37 @@ + + + +{% extends "bootstrap_layout.html" %} + +{% block content %} +Use the form below to predict which user is more likely to say a given tweet...
+ + +{% endblock %} \ No newline at end of file diff --git a/sprint_3_lecture/twitoff_app/templates/results.html b/sprint_3_lecture/twitoff_app/templates/results.html new file mode 100644 index 00000000..d2964ef9 --- /dev/null +++ b/sprint_3_lecture/twitoff_app/templates/results.html @@ -0,0 +1,12 @@ + + +{% extends "bootstrap_layout.html" %} + +{% block content %} +Between '@{{ screen_name_a }}' and '@{{ screen_name_b }}', + the user who is most likely to say '{{ tweet_text }}' + is '@{{ screen_name_most_likely }}' +
+{% endblock %} \ No newline at end of file diff --git a/sprint_3_lecture/twitoff_app/templates/user.html b/sprint_3_lecture/twitoff_app/templates/user.html new file mode 100644 index 00000000..b49e2316 --- /dev/null +++ b/sprint_3_lecture/twitoff_app/templates/user.html @@ -0,0 +1,23 @@ + +{% extends "bootstrap_layout.html" %} +{% set active_page = "users" %} + +{% block content %} +Name: {{ user.name }}
+Location: {{ user.location }}
+Followers: {{ user.followers_count }}
+ + {% if tweets %} +No tweets found.
+ {% endif %} + +{% endblock %} \ No newline at end of file