From 9d3b7c3ce8e67fb119d950072db551f3867ed5fd Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Fri, 21 Oct 2022 11:29:08 -0400 Subject: [PATCH 01/15] wave_1 --- app/planets.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 app/planets.py diff --git a/app/planets.py b/app/planets.py new file mode 100644 index 000000000..d79befb9f --- /dev/null +++ b/app/planets.py @@ -0,0 +1,30 @@ +from turtle import color +from unicodedata import name +from flask import Blueprint,jsonify + +class Planets: + def __init__(self, id, name, color, description): + self.id = id + self.name = name + self.color = color + self.description = description + + Planets = [ + Planets (1, 'Dink', 'Green', 'Fluffy'), + Planets (2, 'Blorp', 'Purple', 'Stinky'), + Planets (3, 'Florpus', 'Red', 'Shy') + ] + +def get_all_planets(): + x = [] + for planet in Planets: + x.append({ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + + }) + + + return jsonify(x) \ No newline at end of file From 2040eba996a6e6c66b016e06c970cd8f55dc5ab0 Mon Sep 17 00:00:00 2001 From: Farrah Date: Fri, 21 Oct 2022 11:30:35 -0400 Subject: [PATCH 02/15] pulling --- app/planets.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 app/planets.py diff --git a/app/planets.py b/app/planets.py new file mode 100644 index 000000000..f6f344e67 --- /dev/null +++ b/app/planets.py @@ -0,0 +1,8 @@ +from flask import Blueprint + +class Planet: + def __init__(self, id, name, description, color): + self.id = id + self.name = name + self.description = description + self.color = color From be5126f4b1c099b0aacff81a069ddff300e3b6eb Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Mon, 24 Oct 2022 12:07:44 -0400 Subject: [PATCH 03/15] session_2 --- app/__init__.py | 3 +++ app/planets.py | 10 +++++++--- app/routes.py | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 70b4cabfe..951b33d5e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,4 +4,7 @@ def create_app(test_config=None): app = Flask(__name__) + from .routes.planets import planets_bp + app.register_blueprint(planets_bp) + return app diff --git a/app/planets.py b/app/planets.py index d79befb9f..a0c950366 100644 --- a/app/planets.py +++ b/app/planets.py @@ -2,6 +2,7 @@ from unicodedata import name from flask import Blueprint,jsonify + class Planets: def __init__(self, id, name, color, description): self.id = id @@ -15,10 +16,13 @@ def __init__(self, id, name, color, description): Planets (3, 'Florpus', 'Red', 'Shy') ] +planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') + +@planets_bp.route('', methods=['GET']) def get_all_planets(): - x = [] + planet_response = [] for planet in Planets: - x.append({ + planet_response.append({ "id": planet.id, "name": planet.name, "color": planet.color, @@ -27,4 +31,4 @@ def get_all_planets(): }) - return jsonify(x) \ No newline at end of file + return jsonify(planet_response) diff --git a/app/routes.py b/app/routes.py index 8e9dfe684..ff2bd36a2 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,2 +1,4 @@ from flask import Blueprint + + From 0f3e6225b6f5a460fb36738403953946ef0f86ac Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Mon, 24 Oct 2022 12:13:23 -0400 Subject: [PATCH 04/15] session 2 part2 --- app/__init__.py | 2 +- app/planets.py | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 951b33d5e..8adc59baa 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,7 +4,7 @@ def create_app(test_config=None): app = Flask(__name__) - from .routes.planets import planets_bp + from .planets import planets_bp app.register_blueprint(planets_bp) return app diff --git a/app/planets.py b/app/planets.py index a0c950366..5bf06a68c 100644 --- a/app/planets.py +++ b/app/planets.py @@ -1,5 +1,3 @@ -from turtle import color -from unicodedata import name from flask import Blueprint,jsonify @@ -10,10 +8,10 @@ def __init__(self, id, name, color, description): self.color = color self.description = description - Planets = [ - Planets (1, 'Dink', 'Green', 'Fluffy'), - Planets (2, 'Blorp', 'Purple', 'Stinky'), - Planets (3, 'Florpus', 'Red', 'Shy') +Planets_list = [ + Planets(1, 'Dink', 'Green', 'Fluffy'), + Planets(2, 'Blorp', 'Purple', 'Stinky'), + Planets(3, 'Florpus', 'Red', 'Shy') ] planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') From 753fcbbabebf5cfefee74379d8f1e4f34951a5ba Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Mon, 24 Oct 2022 12:24:00 -0400 Subject: [PATCH 05/15] session 2/3 --- app/planets.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/planets.py b/app/planets.py index 5bf06a68c..328de43e1 100644 --- a/app/planets.py +++ b/app/planets.py @@ -18,15 +18,16 @@ def __init__(self, id, name, color, description): @planets_bp.route('', methods=['GET']) def get_all_planets(): - planet_response = [] - for planet in Planets: - planet_response.append({ - "id": planet.id, - "name": planet.name, - "color": planet.color, - "description": planet.description - - }) + planet_response = [vars(planet) for planet in Planets_list] + # for planet in Planets_list: + + # planet_response.append({ + # "id": planet.id, + # "name": planet.name, + # "color": planet.color, + # "description": planet.description + + # }) return jsonify(planet_response) From f2473bd05c5f62452f7e5e12ad072141615235e5 Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Mon, 24 Oct 2022 13:09:36 -0400 Subject: [PATCH 06/15] wave2/1 --- app/planets.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/app/planets.py b/app/planets.py index 328de43e1..88efbcea8 100644 --- a/app/planets.py +++ b/app/planets.py @@ -1,4 +1,5 @@ -from flask import Blueprint,jsonify +from flask import Blueprint,jsonify,abort,make_response + class Planets: @@ -19,15 +20,29 @@ def __init__(self, id, name, color, description): @planets_bp.route('', methods=['GET']) def get_all_planets(): planet_response = [vars(planet) for planet in Planets_list] - # for planet in Planets_list: - - # planet_response.append({ - # "id": planet.id, - # "name": planet.name, - # "color": planet.color, - # "description": planet.description - # }) + return jsonify(planet_response) +@planets_bp.route('/', methods=['GET']) +def get_one_planet(id): + planet_id = int(id) + for planet in Planets_list: + if planet.id == planet_id: + return vars(planet) + planet_id = validate_planet(id) + return planet_id + +def validate_planet(id): + try: + planet_id = int(id) + except ValueError: + return { + "message": 'invalid id' + }, 400 + + for planet in Planets_list: + if planet.id == planet_id: + return vars(planet) - return jsonify(planet_response) + + abort(make_response(jsonify(description ="Resource not found"),404)) \ No newline at end of file From ce87bcec99a5a782f99b44ebe261f012b5a28471 Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Mon, 24 Oct 2022 16:06:07 -0400 Subject: [PATCH 07/15] final wave2 --- app/planets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/planets.py b/app/planets.py index 88efbcea8..99b36a123 100644 --- a/app/planets.py +++ b/app/planets.py @@ -25,10 +25,10 @@ def get_all_planets(): @planets_bp.route('/', methods=['GET']) def get_one_planet(id): - planet_id = int(id) - for planet in Planets_list: - if planet.id == planet_id: - return vars(planet) + # planet_id = int(id) + # for planet in Planets_list: + # if planet.id == planet_id: + # return vars(planet) planet_id = validate_planet(id) return planet_id From 782b4f855f2d924f4b332bd1a4979e70f7f04a70 Mon Sep 17 00:00:00 2001 From: Farrah Date: Mon, 24 Oct 2022 16:06:44 -0400 Subject: [PATCH 08/15] wave 2 --- app/planets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/planets.py b/app/planets.py index 88efbcea8..05ee9c88d 100644 --- a/app/planets.py +++ b/app/planets.py @@ -1,7 +1,6 @@ from flask import Blueprint,jsonify,abort,make_response - class Planets: def __init__(self, id, name, color, description): self.id = id From 0103c59bc3a35017206d191eefa7fa209fa53d2c Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Tue, 1 Nov 2022 10:59:49 -0400 Subject: [PATCH 09/15] put, delete:single planet --- app/__init__.py | 14 ++ app/models/__init__.py | 0 app/models/planet.py | 7 + app/planets.py | 136 ++++++++++++------ migrations/README | 1 + migrations/alembic.ini | 45 ++++++ migrations/env.py | 96 +++++++++++++ migrations/script.py.mako | 24 ++++ .../40e7b59395db_adds_planet_model.py | 34 +++++ 9 files changed, 317 insertions(+), 40 deletions(-) create mode 100644 app/models/__init__.py create mode 100644 app/models/planet.py create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/40e7b59395db_adds_planet_model.py diff --git a/app/__init__.py b/app/__init__.py index 8adc59baa..30ff5b310 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,10 +1,24 @@ from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +import sqlalchemy +db = SQLAlchemy() +migrate = Migrate() def create_app(test_config=None): app = Flask(__name__) + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development' + + db.init_app(app) + migrate.init_app(app, db) + + from app.models.planet import Planet + from .planets import planets_bp app.register_blueprint(planets_bp) + return app diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/models/planet.py b/app/models/planet.py new file mode 100644 index 000000000..142a20cc2 --- /dev/null +++ b/app/models/planet.py @@ -0,0 +1,7 @@ +from app import db + +class Planet(db.Model): + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + name = db.Column(db.String) + color = db.Column(db.String) + description = db.Column(db.String) \ No newline at end of file diff --git a/app/planets.py b/app/planets.py index 99b36a123..753995725 100644 --- a/app/planets.py +++ b/app/planets.py @@ -1,48 +1,104 @@ -from flask import Blueprint,jsonify,abort,make_response +from flask import Blueprint,jsonify,abort,make_response,request +from app import db +from app.models.planet import Planet +# class Planets: +# def __init__(self, id, name, color, description): +# self.id = id +# self.name = name +# self.color = color +# self.description = description -class Planets: - def __init__(self, id, name, color, description): - self.id = id - self.name = name - self.color = color - self.description = description - -Planets_list = [ - Planets(1, 'Dink', 'Green', 'Fluffy'), - Planets(2, 'Blorp', 'Purple', 'Stinky'), - Planets(3, 'Florpus', 'Red', 'Shy') - ] +# Planets_list = [ +# Planets(1, 'Dink', 'Green', 'Fluffy'), +# Planets(2, 'Blorp', 'Purple', 'Stinky'), +# Planets(3, 'Florpus', 'Red', 'Shy') +# ] planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') -@planets_bp.route('', methods=['GET']) -def get_all_planets(): - planet_response = [vars(planet) for planet in Planets_list] - - return jsonify(planet_response) - -@planets_bp.route('/', methods=['GET']) -def get_one_planet(id): - # planet_id = int(id) - # for planet in Planets_list: - # if planet.id == planet_id: - # return vars(planet) - planet_id = validate_planet(id) - return planet_id - -def validate_planet(id): - try: - planet_id = int(id) - except ValueError: - return { - "message": 'invalid id' - }, 400 - - for planet in Planets_list: - if planet.id == planet_id: - return vars(planet) +@planets_bp.route('', methods=['GET','POST']) +def handle_planet(): + if request.method == "GET": + planets = Planet.query.all() + planets_response = [] + for planet in planets: + planets_response.append({ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + }) + return jsonify(planets_response) + elif request.method == "POST": + request_body = request.get_json() + new_planet = Planet(name=request_body["name"], + color=request_body["color"], + description=request_body["description"]) + + db.session.add(new_planet) + db.session.commit() + + + return make_response(f"Planet {new_planet.name} successfully created", 201) + + +@planets_bp.route("/", methods=['GET','PUT','DELETE']) + +def handle_1_planet(id): + planets = Planet.query.get(id) + + + if request.method == "GET": + return{ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + } + elif request.method == "PUT": + request_body = request.get_json() + + planet.name = request_body["name"] + planet.color = request_body["color"] + planet.description = request_body["description"] + + + db.session.commit() + return make_response(f"planet color {planet.color} succesfully updated",200) + + elif request.method == "DELETE": + db.session.delete(planet) + db.session.commit() + + return make_response(f"planet color {planet.color} succesfully deleted", 200) + + + + + +# def get_all_planets(): +# planet_response = [vars(planet) for planet in Planets_list] + +# return jsonify(planet_response) + +# @planets_bp.route('/', methods=['GET']) +# def get_one_planet(id): +# planet_id = validate_planet(id) +# return planet_id + +# def validate_planet(id): +# try: +# planet_id = int(id) +# except ValueError: +# return { +# "message": 'invalid id' +# }, 400 + +# for planet in Planets_list: +# if planet.id == planet_id: +# return vars(planet) - abort(make_response(jsonify(description ="Resource not found"),404)) \ No newline at end of file +# abort(make_response(jsonify(description ="Resource not found"),404)) \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..98e4f9c44 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..f8ed4801f --- /dev/null +++ b/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/migrations/env.py b/migrations/env.py new file mode 100644 index 000000000..8b3fb3353 --- /dev/null +++ b/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 flask import current_app + +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 +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/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/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/migrations/versions/40e7b59395db_adds_planet_model.py b/migrations/versions/40e7b59395db_adds_planet_model.py new file mode 100644 index 000000000..86d454a1b --- /dev/null +++ b/migrations/versions/40e7b59395db_adds_planet_model.py @@ -0,0 +1,34 @@ +"""adds planet model + +Revision ID: 40e7b59395db +Revises: +Create Date: 2022-10-30 19:08:57.908052 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '40e7b59395db' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('planet', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('color', sa.String(), nullable=True), + sa.Column('description', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('planet') + # ### end Alembic commands ### From 22a7cae714f4e4410fb32eaf200e9f178d0e5a6f Mon Sep 17 00:00:00 2001 From: Farrah Date: Tue, 1 Nov 2022 11:00:18 -0400 Subject: [PATCH 10/15] pulling --- app/__init__.py | 13 ++- app/models/__init__.py | 0 app/models/planet.py | 7 ++ app/planets.py | 106 +++++++++++------- migrations/README | 1 + migrations/alembic.ini | 45 ++++++++ migrations/env.py | 96 ++++++++++++++++ migrations/script.py.mako | 24 ++++ .../961ca395c403_adds_planet_model.py | 34 ++++++ 9 files changed, 286 insertions(+), 40 deletions(-) create mode 100644 app/models/__init__.py create mode 100644 app/models/planet.py create mode 100644 migrations/README create mode 100644 migrations/alembic.ini create mode 100644 migrations/env.py create mode 100644 migrations/script.py.mako create mode 100644 migrations/versions/961ca395c403_adds_planet_model.py diff --git a/app/__init__.py b/app/__init__.py index 166566c53..0f800efe9 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,11 +1,22 @@ from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +db = SQLAlchemy() +migrate = Migrate() def create_app(test_config=None): app = Flask(__name__) + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development' + + db.init_app(app) + migrate.init_app(app, db) + + from app.models.planet import Planet # dot notation can mean they're in different levels - from .planets import planets_bp + from .planets import planets_bp #(.routes)? app.register_blueprint(planets_bp) return app diff --git a/app/models/__init__.py b/app/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/models/planet.py b/app/models/planet.py new file mode 100644 index 000000000..142a20cc2 --- /dev/null +++ b/app/models/planet.py @@ -0,0 +1,7 @@ +from app import db + +class Planet(db.Model): + id = db.Column(db.Integer, primary_key=True, autoincrement=True) + name = db.Column(db.String) + color = db.Column(db.String) + description = db.Column(db.String) \ No newline at end of file diff --git a/app/planets.py b/app/planets.py index cc424f954..266aa1d7b 100644 --- a/app/planets.py +++ b/app/planets.py @@ -1,47 +1,75 @@ -from flask import Blueprint,jsonify,abort,make_response +from flask import Blueprint,jsonify,abort,make_response, request +from app import db +from app.models.planet import Planet -class Planets: - def __init__(self, id, name, color, description): - self.id = id - self.name = name - self.color = color - self.description = description -Planets_list = [ - Planets(1, 'Dink', 'Green', 'Fluffy'), - Planets(2, 'Blorp', 'Purple', 'Stinky'), - Planets(3, 'Florpus', 'Red', 'Shy') - ] +# class Planets: +# def __init__(self, id, name, color, description): +# self.id = id +# self.name = name +# self.color = color +# self.description = description + +# Planets_list = [ +# Planets(1, 'Dink', 'Green', 'Fluffy'), +# Planets(2, 'Blorp', 'Purple', 'Stinky'), +# Planets(3, 'Florpus', 'Red', 'Shy') +# ] planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') -@planets_bp.route('', methods=['GET']) -def get_all_planets(): - planet_response = [vars(planet) for planet in Planets_list] - - return jsonify(planet_response) - -@planets_bp.route('/', methods=['GET']) -def get_one_planet(id): - # planet_id = int(id) - # for planet in Planets_list: - # if planet.id == planet_id: - # return vars(planet) - planet_id = validate_planet(id) - return planet_id - -def validate_planet(id): - try: - planet_id = int(id) - except ValueError: - return { - "message": 'invalid id' - }, 400 - - for planet in Planets_list: - if planet.id == planet_id: - return vars(planet) +@planets_bp.route("", methods=['GET','POST']) +def handle_planet(): + + if request.method == "GET": + planets = Planet.query.all() + planets_response = [] + for planet in planets: + planets_response.append({ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + }) + return jsonify(planets_response) + elif request.method == "POST": + request_body = request.get_json() + new_planet = Planet(name=request_body["name"], + color=request_body["color"], + description=request_body["description"]) + + db.session.add(new_planet) + db.session.commit() + + return make_response(f"Planet {new_planet.name} successfully created", 201) + +# @planets_bp.route('', methods=['GET']) +# def get_all_planets(): +# planet_response = [vars(planet) for planet in Planets_list] + +# return jsonify(planet_response) + +# @planets_bp.route('/', methods=['GET']) +# def get_one_planet(id): +# # planet_id = int(id) +# # for planet in Planets_list: +# # if planet.id == planet_id: +# # return vars(planet) +# planet_id = validate_planet(id) +# return planet_id + +# def validate_planet(id): +# try: +# planet_id = int(id) +# except ValueError: +# return { +# "message": 'invalid id' +# }, 400 + +# for planet in Planets_list: +# if planet.id == planet_id: +# return vars(planet) - abort(make_response(jsonify(description ="Resource not found"),404)) \ No newline at end of file +# abort(make_response(jsonify(description ="Resource not found"),404)) \ No newline at end of file diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..98e4f9c44 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..f8ed4801f --- /dev/null +++ b/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/migrations/env.py b/migrations/env.py new file mode 100644 index 000000000..8b3fb3353 --- /dev/null +++ b/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 flask import current_app + +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 +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/migrations/script.py.mako b/migrations/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/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/migrations/versions/961ca395c403_adds_planet_model.py b/migrations/versions/961ca395c403_adds_planet_model.py new file mode 100644 index 000000000..e147fffa2 --- /dev/null +++ b/migrations/versions/961ca395c403_adds_planet_model.py @@ -0,0 +1,34 @@ +"""adds planet model + +Revision ID: 961ca395c403 +Revises: +Create Date: 2022-10-29 12:37:08.047362 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '961ca395c403' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('planet', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('color', sa.String(), nullable=True), + sa.Column('description', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('planet') + # ### end Alembic commands ### From ded4fc51657119f9977e5fbf4fa44a88759dd151 Mon Sep 17 00:00:00 2001 From: Farrah Date: Wed, 2 Nov 2022 10:37:40 -0400 Subject: [PATCH 11/15] bug fixed --- app/planets.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/app/planets.py b/app/planets.py index 753995725..ccb0e9842 100644 --- a/app/planets.py +++ b/app/planets.py @@ -47,32 +47,32 @@ def handle_planet(): @planets_bp.route("/", methods=['GET','PUT','DELETE']) def handle_1_planet(id): - planets = Planet.query.get(id) + planet = Planet.query.get(id) - if request.method == "GET": - return{ - "id": planet.id, - "name": planet.name, - "color": planet.color, - "description": planet.description - } - elif request.method == "PUT": - request_body = request.get_json() - - planet.name = request_body["name"] - planet.color = request_body["color"] - planet.description = request_body["description"] + if request.method == "GET": + return{ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + } + elif request.method == "PUT": + request_body = request.get_json() + + planet.name = request_body["name"] + planet.color = request_body["color"] + planet.description = request_body["description"] - - db.session.commit() - return make_response(f"planet color {planet.color} succesfully updated",200) + + db.session.commit() + return make_response(f"planet color {planet.color} succesfully updated",200) - elif request.method == "DELETE": - db.session.delete(planet) - db.session.commit() + elif request.method == "DELETE": + db.session.delete(planet) + db.session.commit() - return make_response(f"planet color {planet.color} succesfully deleted", 200) + return make_response(f"planet color {planet.color} succesfully deleted", 200) From 818695f451be49a4695fe4b1c0e8612e28220dc5 Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Wed, 2 Nov 2022 11:26:21 -0400 Subject: [PATCH 12/15] get keys --- app/planets.py | 64 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/app/planets.py b/app/planets.py index ccb0e9842..f8ee16b41 100644 --- a/app/planets.py +++ b/app/planets.py @@ -18,30 +18,54 @@ planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') -@planets_bp.route('', methods=['GET','POST']) +@planets_bp.route('', methods=['GET']) def handle_planet(): - if request.method == "GET": + planet_query = Planet.query + # if request.method == "GET": + # planet_query = Planet.query + + description_query = request.args.get("description") + if description_query: + planet_query = planet_query.filter_by(description=description_query) + else: planets = Planet.query.all() - planets_response = [] - for planet in planets: - planets_response.append({ - "id": planet.id, - "name": planet.name, - "color": planet.color, - "description": planet.description - }) - return jsonify(planets_response) - elif request.method == "POST": - request_body = request.get_json() - new_planet = Planet(name=request_body["name"], - color=request_body["color"], - description=request_body["description"]) - - db.session.add(new_planet) - db.session.commit() + + color_query = request.args.get("color") + if color_query: + planet_query = planet_query.filter_by(color=color_query) + else: + planets = Planet.query.all() + + name_query = request.args.get("name") + if name_query: + planet_query = planet_query.filter_by(name=name_query) + + planets = planet_query.all() + + + planets_response = [] + for planet in planets: + planets_response.append({ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + }) + if not planets_response: + return make_response(jsonify(f"There are no {planet_query} planets")) + return jsonify(planets_response) + + # elif request.method == "POST": + # request_body = request.get_json() + # new_planet = Planet(name=request_body["name"], + # color=request_body["color"], + # description=request_body["description"]) + + # db.session.add(new_planet) + # db.session.commit() - return make_response(f"Planet {new_planet.name} successfully created", 201) + # return make_response(f"Planet {new_planet.name} successfully created", 201) @planets_bp.route("/", methods=['GET','PUT','DELETE']) From f8cd7caa6bc1d2fe9e454281591202678b99dca3 Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Thu, 3 Nov 2022 11:27:43 -0400 Subject: [PATCH 13/15] test_get_all_get_one --- .gitignore | 1 + .planetsenv | 2 ++ app/__init__.py | 15 +++++++++-- app/tests/__init__.py | 0 app/tests/conftest.py | 56 ++++++++++++++++++++++++++++++++++++++++ app/tests/test_routes.py | 31 ++++++++++++++++++++++ 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 .planetsenv create mode 100644 app/tests/__init__.py create mode 100644 app/tests/conftest.py create mode 100644 app/tests/test_routes.py diff --git a/.gitignore b/.gitignore index 4e9b18359..30584e5c1 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ celerybeat.pid # Environments .env +.flaskend .venv env/ venv/ diff --git a/.planetsenv b/.planetsenv new file mode 100644 index 000000000..8064aaae8 --- /dev/null +++ b/.planetsenv @@ -0,0 +1,2 @@ +FLASK_ENV=development +FLASK_DEBUG=1 \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py index 30ff5b310..242dccd0b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,8 +1,14 @@ +import os +from symbol import import_stmt from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate import sqlalchemy +from dotenv import load_dotenv +import os +load_dotenv() + db = SQLAlchemy() migrate = Migrate() @@ -10,8 +16,13 @@ def create_app(test_config=None): app = Flask(__name__) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development' - + if not test_config: + app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get( + "SQLALCHEMY_DATABASE_URI") + else: + app.config["TESTING"] = True + app.config["SQLALCHEMY_DATABASE_URI"] = os.environ.get( + "SQLALCHEMY_TEST_DATABASE_URI") db.init_app(app) migrate.init_app(app, db) diff --git a/app/tests/__init__.py b/app/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/tests/conftest.py b/app/tests/conftest.py new file mode 100644 index 000000000..570194521 --- /dev/null +++ b/app/tests/conftest.py @@ -0,0 +1,56 @@ +import pytest +from app import create_app +from app import db +#create a new database session after a request as described below. +from flask.signals import request_finished +from app.models.planet import Planet + +# Create test versions of our flask app and database +@pytest.fixture +def app(): + app = create_app({"TESTING": True}) + + #will be invoked after any request is completed + @request_finished.connect_via(app) + def expire_session(sender, response, **extra): + db.session.remove() + + with app.app_context(): + # sets up database + db.create_all() + yield app + + with app.app_context(): + db.drop_all() + + +@pytest.fixture +def client(app): + # depends on app that references another fixture + # holds the reference to the test interface + return app.test_client() + +@pytest.fixture +def two_saved_planets(app): + # Arrange + blorp = Planet + { + "id": 2, + "name": 'Blorp', + "color": 'Purple', + "description": "Stinky" + } + florpus = Planet + { + "id": 3, + "name": 'Florpus', + "color": 'Red', + "description": "Shy" + } + db.session.add_all([blorp, florpus]) + # Alternatively, we could do + # db.session.add(winston) + # db.session.add(winter) + db.session.commit() + + \ No newline at end of file diff --git a/app/tests/test_routes.py b/app/tests/test_routes.py new file mode 100644 index 000000000..4ac03d5d4 --- /dev/null +++ b/app/tests/test_routes.py @@ -0,0 +1,31 @@ +from app.models.planet import Planet + +def test_get_all_planets_with_empty_db_return_empty_list(client): + + response = client.get('/planets') + response_body = response.get_json() + + assert response_body == [] + assert response.status_code == 200 + +def test_get_one_planet(client,two_saved_planets): + + response = client.get('/planets/2') + response_body = response.get_json() + + planet2 = { + 'id':2, + 'name': 'Blorp', + 'color': 'Purple', + 'description': 'Stinky' + } + + # florpus ={ + # "id": 3, + # "name": 'Florpus', + # "color": 'Red', + # "description": "Shy" + # } + + assert response.status_code == 200 + assert response_body == planet2 From 8e516a05f8e259858bd46bc1484312b65e9b796f Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Mon, 28 Nov 2022 16:59:25 -0500 Subject: [PATCH 14/15] tests update --- app/__init__.py | 1 - app/planets.py | 56 +++-------------------------------- app/tests/test_routes.py | 63 +++++++++++++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 242dccd0b..16461c8db 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,5 +1,4 @@ import os -from symbol import import_stmt from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate diff --git a/app/planets.py b/app/planets.py index f8ee16b41..4242a143f 100644 --- a/app/planets.py +++ b/app/planets.py @@ -3,26 +3,13 @@ from app.models.planet import Planet -# class Planets: -# def __init__(self, id, name, color, description): -# self.id = id -# self.name = name -# self.color = color -# self.description = description - -# Planets_list = [ -# Planets(1, 'Dink', 'Green', 'Fluffy'), -# Planets(2, 'Blorp', 'Purple', 'Stinky'), -# Planets(3, 'Florpus', 'Red', 'Shy') -# ] planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') @planets_bp.route('', methods=['GET']) def handle_planet(): planet_query = Planet.query - # if request.method == "GET": - # planet_query = Planet.query + description_query = request.args.get("description") if description_query: @@ -51,21 +38,10 @@ def handle_planet(): "color": planet.color, "description": planet.description }) - if not planets_response: - return make_response(jsonify(f"There are no {planet_query} planets")) - return jsonify(planets_response) - - # elif request.method == "POST": - # request_body = request.get_json() - # new_planet = Planet(name=request_body["name"], - # color=request_body["color"], - # description=request_body["description"]) - - # db.session.add(new_planet) - # db.session.commit() - - # return make_response(f"Planet {new_planet.name} successfully created", 201) + if not planets_response: + return make_response(jsonify(f"There are no {planet_query} planets")) + return jsonify(planets_response) @planets_bp.route("/", methods=['GET','PUT','DELETE']) @@ -102,27 +78,3 @@ def handle_1_planet(id): -# def get_all_planets(): -# planet_response = [vars(planet) for planet in Planets_list] - -# return jsonify(planet_response) - -# @planets_bp.route('/', methods=['GET']) -# def get_one_planet(id): -# planet_id = validate_planet(id) -# return planet_id - -# def validate_planet(id): -# try: -# planet_id = int(id) -# except ValueError: -# return { -# "message": 'invalid id' -# }, 400 - -# for planet in Planets_list: -# if planet.id == planet_id: -# return vars(planet) - - -# abort(make_response(jsonify(description ="Resource not found"),404)) \ No newline at end of file diff --git a/app/tests/test_routes.py b/app/tests/test_routes.py index 4ac03d5d4..9c5c22c42 100644 --- a/app/tests/test_routes.py +++ b/app/tests/test_routes.py @@ -20,12 +20,61 @@ def test_get_one_planet(client,two_saved_planets): 'description': 'Stinky' } - # florpus ={ - # "id": 3, - # "name": 'Florpus', - # "color": 'Red', - # "description": "Shy" - # } - assert response.status_code == 200 assert response_body == planet2 + +def test_no_data_return_failure(client): + + response = client.get("/planets/1") + response_body = response.get_json() + + assert response.status_code == 404 + +def test_return_array(client): +# Act + response = client.get("/planets") + response_body = response.get_json() + + # Assert + assert response.status_code == 200 + assert len(response_body) == 1 + assert response_body == [ + { + "id": 1, + "name": 'Dink', + "color": 'Green', + "description": 'Fluffy' + }, + { + "id": 2, + "name": 'Blorp', + "color": 'Purple', + "description": 'Stinky' + }, + { + "id": 3, + "name": 'Florpus', + "color": 'Red', + "description": 'Shy' + } + ] + +def test_request_body_json(client): +# Act + response = client.post("/planets", json={ + "name": "Cripper" + }) + response_body = response.get_json() + + # Assert + assert response.status_code == 201 + + assert Planet.query.all() == [] + + + + +# 1. `GET` `/planets/1` returns a response body that matches our fixture +# 1. `GET` `/planets/1` with no data in test database (no fixture) returns a `404` +# 1. `GET` `/planets` with valid test data (fixtures) returns a `200` with an array including appropriate test data +# 1. `POST` `/planets` with a JSON request body returns a `201` \ No newline at end of file From 46930f4e3893c1e4704930d1b7860412f9931b2c Mon Sep 17 00:00:00 2001 From: Kallie Gannon Date: Sun, 22 Jan 2023 10:47:34 -0500 Subject: [PATCH 15/15] submission --- app/planets.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/app/planets.py b/app/planets.py index 4242a143f..3624170c9 100644 --- a/app/planets.py +++ b/app/planets.py @@ -3,6 +3,9 @@ from app.models.planet import Planet +if title_query: + title_query = title_query.filter_by(title=title_query) + planets_bp = Blueprint('planets_bp', __name__, url_prefix='/planets') @@ -12,6 +15,7 @@ def handle_planet(): description_query = request.args.get("description") + if description_query: planet_query = planet_query.filter_by(description=description_query) else: @@ -43,6 +47,19 @@ def handle_planet(): return make_response(jsonify(f"There are no {planet_query} planets")) return jsonify(planets_response) + board_response = [] + for board in boards: + board_response.append({ + "id": planet.id, + "name": planet.name, + "color": planet.color, + "description": planet.description + }) + + if not planets_response: + return make_response(jsonify(f"There are no {planet_query} planets")) + return jsonify(planets_response) + @planets_bp.route("/", methods=['GET','PUT','DELETE']) @@ -76,5 +93,15 @@ def handle_1_planet(id): +@board_bp.route(/title, methods=["GET"]) +# GET /task/id +def handle_task(title): + # Query our db to grab the task that has the id we want: + + task = Task.query.get(title) + if task.goal_id is not None: + return{"task": task.to_dict_in_goal()} + else: + return {"task": task.to_dict()}