From 1fcae49bb8d178a3b0932dbc3ec9b5f7444be580 Mon Sep 17 00:00:00 2001 From: Whitney Date: Sun, 23 Apr 2023 14:17:30 -0700 Subject: [PATCH 01/18] Wave 01 --- app/__init__.py | 3 +++ app/routes.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/__init__.py b/app/__init__.py index 70b4cabfe..ab9eee40e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -4,4 +4,7 @@ def create_app(test_config=None): app = Flask(__name__) + from .routes import planets_bp + app.register_blueprint(planets_bp) + return app diff --git a/app/routes.py b/app/routes.py index 8e9dfe684..0caa6ac9a 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,2 +1,33 @@ -from flask import Blueprint +from flask import Blueprint, jsonify +class Planet: + def __init__(self, id, name, description, mass): + self.id = id + self.name = name + self.description = description + self.mass = mass + +planet_list = [ + Planet(1, "Mercury", "Small, rocky planet closest to the Sun", 0.330), + Planet(2, "Venus", "Hottest planet in the solar system with a thick atmosphere", 4.87), + Planet(3, "Earth", "Home to a diverse range of life forms, including humans", 5.97), + Planet(4, "Mars", "Red planet with a thin atmosphere and polar ice caps", 0.642), + Planet(5, "Jupiter", "Largest planet in the solar system with a thick atmosphere and many moons", 1898), + Planet(6, "Saturn", "Known for its prominent rings made of ice and dust", 568), + Planet(7, "Uranus", "Blue-green planet with a tilted axis of rotation", 86.8), + Planet(8, "Neptune", "Blue planet with a windy atmosphere and many storms", 102), +] + +planets_bp = Blueprint("planets", __name__, url_prefix="/planets") + +@planets_bp.route("", methods=["GET"]) +def get_planets(): + planets_response = [] + for planet in planet_list: + planets_response.append({ + "id": planet.id, + "name": planet.name, + "description": planet.description, + "mass": planet.mass + }) + return jsonify(planets_response), 200 \ No newline at end of file From c3f4be4aaae82c03799ca761c13055594dd0a920 Mon Sep 17 00:00:00 2001 From: Jessica Date: Mon, 24 Apr 2023 15:45:04 -0600 Subject: [PATCH 02/18] Modify get_planets to use to_dict method --- app/routes.py | 19 +++++--- requirements.txt | 123 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 108 insertions(+), 34 deletions(-) diff --git a/app/routes.py b/app/routes.py index 0caa6ac9a..048b27a6e 100644 --- a/app/routes.py +++ b/app/routes.py @@ -6,6 +6,14 @@ def __init__(self, id, name, description, mass): self.name = name self.description = description self.mass = mass + + def to_dict(self): + return dict( + id = self.id, + name = self.name, + description = self.description, + mass = self.mass + ) planet_list = [ Planet(1, "Mercury", "Small, rocky planet closest to the Sun", 0.330), @@ -24,10 +32,7 @@ def __init__(self, id, name, description, mass): def get_planets(): planets_response = [] for planet in planet_list: - planets_response.append({ - "id": planet.id, - "name": planet.name, - "description": planet.description, - "mass": planet.mass - }) - return jsonify(planets_response), 200 \ No newline at end of file + planets_response.append(planet.to_dict()) + + return jsonify(planets_response), 200 + diff --git a/requirements.txt b/requirements.txt index ae59e7b55..0101e065c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,27 +1,96 @@ -alembic==1.5.4 -autopep8==1.5.5 -blinker==1.4 -certifi==2020.12.5 -chardet==4.0.0 -click==7.1.2 -Flask==1.1.2 -Flask-Migrate==2.6.0 -Flask-SQLAlchemy==2.4.4 -idna==2.10 -itsdangerous==1.1.0 -Jinja2==2.11.3 -Mako==1.1.4 -MarkupSafe==1.1.1 -psycopg2-binary==2.9.5 -pycodestyle==2.6.0 -pytest==7.3.1 -pytest-cov==2.12.1 -python-dateutil==2.8.1 -python-dotenv==0.15.0 -python-editor==1.0.4 -requests==2.25.1 -six==1.15.0 -SQLAlchemy==1.3.23 -toml==0.10.2 -urllib3==1.26.4 -Werkzeug==1.0.1 +anyio==3.6.2 +appnope==0.1.3 +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +arrow==1.2.3 +asttokens==2.2.1 +attrs==22.2.0 +backcall==0.2.0 +beautifulsoup4==4.12.0 +bleach==6.0.0 +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==3.0.1 +click==8.1.3 +comm==0.1.3 +debugpy==1.6.6 +decorator==5.1.1 +defusedxml==0.7.1 +executing==1.2.0 +fastjsonschema==2.16.3 +Flask==2.2.3 +fqdn==1.5.1 +idna==3.4 +iniconfig==2.0.0 +ipykernel==6.22.0 +ipython==8.11.0 +ipython-genutils==0.2.0 +ipywidgets==8.0.6 +isoduration==20.11.0 +itsdangerous==2.1.2 +jedi==0.18.2 +Jinja2==3.1.2 +jsonpointer==2.3 +jsonschema==4.17.3 +jupyter==1.0.0 +jupyter-console==6.6.3 +jupyter-events==0.6.3 +jupyter_client==8.1.0 +jupyter_core==5.3.0 +jupyter_server==2.5.0 +jupyter_server_terminals==0.4.4 +jupyterlab-pygments==0.2.2 +jupyterlab-widgets==3.0.7 +MarkupSafe==2.1.2 +matplotlib-inline==0.1.6 +mistune==2.0.5 +nbclassic==0.5.3 +nbclient==0.7.2 +nbconvert==7.2.10 +nbformat==5.8.0 +nest-asyncio==1.5.6 +notebook==6.5.3 +notebook_shim==0.2.2 +packaging==23.0 +pandocfilters==1.5.0 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +platformdirs==3.2.0 +pluggy==1.0.0 +prometheus-client==0.16.0 +prompt-toolkit==3.0.38 +psutil==5.9.4 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pycparser==2.21 +Pygments==2.14.0 +pyrsistent==0.19.3 +pytest==7.2.2 +python-dateutil==2.8.2 +python-json-logger==2.0.7 +PyYAML==6.0 +pyzmq==25.0.2 +qtconsole==5.4.1 +QtPy==2.3.1 +requests==2.28.2 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +Send2Trash==1.8.0 +six==1.16.0 +sniffio==1.3.0 +soupsieve==2.4 +stack-data==0.6.2 +terminado==0.17.1 +tinycss2==1.2.1 +tornado==6.2 +traitlets==5.9.0 +uri-template==1.2.0 +urllib3==1.26.14 +wcwidth==0.2.6 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.5.1 +Werkzeug==2.2.3 +widgetsnbextension==4.0.7 +wonderwords==2.2.0 From ca6681807bb2fe2882c13ec62bcea491f3cb6ac7 Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 25 Apr 2023 14:21:44 -0600 Subject: [PATCH 03/18] Fix branch name --- app/routes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/routes.py b/app/routes.py index 048b27a6e..ccd812424 100644 --- a/app/routes.py +++ b/app/routes.py @@ -36,3 +36,10 @@ def get_planets(): return jsonify(planets_response), 200 +@planets_bp.route("/", methods=["GET"]) +def get_planet(id): + planet = validate_planet(id) + + return planet.to_dict() + + From fb0721092c7004fbea4a47bdd38852320ef74513 Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 25 Apr 2023 14:34:40 -0600 Subject: [PATCH 04/18] Finish wave 2 --- app/routes.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index ccd812424..e2884d45c 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,4 +1,4 @@ -from flask import Blueprint, jsonify +from flask import Blueprint, jsonify, abort, make_response class Planet: def __init__(self, id, name, description, mass): @@ -42,4 +42,15 @@ def get_planet(id): return planet.to_dict() +def validate_planet(id): + try: + id = int(id) + + except: + abort(make_response({"message": f"Planet {id} is invalid!"}, 400)) + + for planet in planet_list: + if planet.id == id: + return planet + abort(make_response({"message": f"Planet {id} is not found!"}, 404)) \ No newline at end of file From 2cd9ff7837adf14a11012b3319595df5dc8378d9 Mon Sep 17 00:00:00 2001 From: Whitney Date: Fri, 28 Apr 2023 11:24:36 -0700 Subject: [PATCH 05/18] App Routes Progress --- app/__init__.py | 15 ++++- app/models/__init__.py | 0 app/models/planet.py | 10 +++ app/routes.py | 148 +++++++++++++++++++++++++++-------------- 4 files changed, 120 insertions(+), 53 deletions(-) create mode 100644 app/models/__init__.py create mode 100644 app/models/planet.py diff --git a/app/__init__.py b/app/__init__.py index ab9eee40e..ebacedfb0 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,10 +1,21 @@ from flask import Flask +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +db = SQLAlchemy() +migrate = Migrate() -def create_app(test_config=None): +def create_app(): app = Flask(__name__) + app.config['SQALCHEMY_TRACK_MODIFICATIONS'] = False + app.cofig['SQALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development' - from .routes import planets_bp + db.init_app(app) + migrate.init_app(app, db) + + from app.models.planet import Planet + + from .planet_routes 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..fbe3810a5 --- /dev/null +++ b/app/models/planet.py @@ -0,0 +1,10 @@ +from app import db + +class Planet(db.Model): + id = db.Column( + db.Integer, + primary_key=True, + autoincrement=True) + name = db.Column(db.String, nullable=False) + description = db.Column(db.String, nullable=False) + mass = db.Column(db.Integer, nullable=False) \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index e2884d45c..9da582108 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,56 +1,102 @@ -from flask import Blueprint, jsonify, abort, make_response - -class Planet: - def __init__(self, id, name, description, mass): - self.id = id - self.name = name - self.description = description - self.mass = mass - - def to_dict(self): - return dict( - id = self.id, - name = self.name, - description = self.description, - mass = self.mass +from flask import Blueprint, jsonify, abort, make_response, request +from app.models.cat import Cat +from app import db + +cats_bp = Blueprint("cats", __name__, url_prefix="/cats") + +# POST /cats +@cats_bp.route("", methods=["POST"]) +def create_cat(): + request_body = request.get_json() + new_cat = Cat( + name=request_body["name"], + color=request_body["color"], + personality=request_body["personality"], + ) + + db.session.add(new_cat) + db.session.commit() + + message = f"Cat {new_cat.name} successfully created" + return make_response(message, 201) + +# GET /cats +@cats_bp.route("", methods=["GET"]) +def get_all_cats(): + cats = Cat.query.all() + results = [] + for cat in cats: + results.append( + dict( + id=cat.id, + name=cat.name, + color=cat.color, + personality=cat.personality + ) ) + + return jsonify(results) + + + + + + + + +# from flask import Blueprint, jsonify, abort, make_response + +# class Planet: +# def __init__(self, id, name, description, mass): +# self.id = id +# self.name = name +# self.description = description +# self.mass = mass + +# def to_dict(self): +# return dict( +# id = self.id, +# name = self.name, +# description = self.description, +# mass = self.mass +# ) -planet_list = [ - Planet(1, "Mercury", "Small, rocky planet closest to the Sun", 0.330), - Planet(2, "Venus", "Hottest planet in the solar system with a thick atmosphere", 4.87), - Planet(3, "Earth", "Home to a diverse range of life forms, including humans", 5.97), - Planet(4, "Mars", "Red planet with a thin atmosphere and polar ice caps", 0.642), - Planet(5, "Jupiter", "Largest planet in the solar system with a thick atmosphere and many moons", 1898), - Planet(6, "Saturn", "Known for its prominent rings made of ice and dust", 568), - Planet(7, "Uranus", "Blue-green planet with a tilted axis of rotation", 86.8), - Planet(8, "Neptune", "Blue planet with a windy atmosphere and many storms", 102), -] - -planets_bp = Blueprint("planets", __name__, url_prefix="/planets") - -@planets_bp.route("", methods=["GET"]) -def get_planets(): - planets_response = [] - for planet in planet_list: - planets_response.append(planet.to_dict()) - - return jsonify(planets_response), 200 - -@planets_bp.route("/", methods=["GET"]) -def get_planet(id): - planet = validate_planet(id) - - return planet.to_dict() - -def validate_planet(id): - try: - id = int(id) +# planet_list = [ +# Planet(1, "Mercury", "Small, rocky planet closest to the Sun", 0.330), +# Planet(2, "Venus", "Hottest planet in the solar system with a thick atmosphere", 4.87), +# Planet(3, "Earth", "Home to a diverse range of life forms, including humans", 5.97), +# Planet(4, "Mars", "Red planet with a thin atmosphere and polar ice caps", 0.642), +# Planet(5, "Jupiter", "Largest planet in the solar system with a thick atmosphere and many moons", 1898), +# Planet(6, "Saturn", "Known for its prominent rings made of ice and dust", 568), +# Planet(7, "Uranus", "Blue-green planet with a tilted axis of rotation", 86.8), +# Planet(8, "Neptune", "Blue planet with a windy atmosphere and many storms", 102), +# ] + +# planets_bp = Blueprint("planets", __name__, url_prefix="/planets") + +# @planets_bp.route("", methods=["GET"]) +# def get_planets(): +# planets_response = [] +# for planet in planet_list: +# planets_response.append(planet.to_dict()) + +# return jsonify(planets_response), 200 + +# @planets_bp.route("/", methods=["GET"]) +# def get_planet(id): +# planet = validate_planet(id) + +# return planet.to_dict() + +# def validate_planet(id): +# try: +# id = int(id) - except: - abort(make_response({"message": f"Planet {id} is invalid!"}, 400)) +# except: +# abort(make_response({"message": f"Planet {id} is invalid!"}, 400)) - for planet in planet_list: - if planet.id == id: - return planet +# for planet in planet_list: +# if planet.id == id: +# return planet - abort(make_response({"message": f"Planet {id} is not found!"}, 404)) \ No newline at end of file +# abort(make_response({"message": f"Planet {id} is not found!"}, 404)) \ No newline at end of file From 8be00283610317e919ef5c20e132ab4fd8d1051e Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 2 May 2023 12:26:46 -0600 Subject: [PATCH 06/18] Co-authored-by: WhitShake --- app/__init__.py | 4 ++-- app/models/planet.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index ebacedfb0..64df51d70 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -8,14 +8,14 @@ def create_app(): app = Flask(__name__) app.config['SQALCHEMY_TRACK_MODIFICATIONS'] = False - app.cofig['SQALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development' + app.config['SQALCHEMY_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 .planet_routes import planets_bp + from app.routes import planets_bp app.register_blueprint(planets_bp) return app diff --git a/app/models/planet.py b/app/models/planet.py index fbe3810a5..9ff3af29f 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -1,10 +1,10 @@ from app import db class Planet(db.Model): - id = db.Column( - db.Integer, - primary_key=True, - autoincrement=True) - name = db.Column(db.String, nullable=False) - description = db.Column(db.String, nullable=False) - mass = db.Column(db.Integer, nullable=False) \ No newline at end of file + id = db.Column( + db.Integer, + primary_key=True, + autoincrement=True) + name = db.Column(db.String, nullable=False) + description = db.Column(db.String, nullable=False) + mass = db.Column(db.Integer, nullable=False) \ No newline at end of file From de7739e2e6077964bbbbcfef6a2f38c5beae71ff Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 2 May 2023 14:18:19 -0600 Subject: [PATCH 07/18] Broken wave 3 --- app/__init__.py | 11 +++++---- app/models/planet.py | 30 ++++++++++++++++++++---- app/routes.py | 55 +++++++++++++++++++++++++------------------- 3 files changed, 62 insertions(+), 34 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 64df51d70..e92e06adc 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -5,17 +5,18 @@ db = SQLAlchemy() migrate = Migrate() -def create_app(): +def create_app(test_config=None): app = Flask(__name__) - app.config['SQALCHEMY_TRACK_MODIFICATIONS'] = False - app.config['SQALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development' + + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_personal' db.init_app(app) migrate.init_app(app, db) - from app.models.planet import Planet - from app.routes import planets_bp app.register_blueprint(planets_bp) return app + + diff --git a/app/models/planet.py b/app/models/planet.py index 9ff3af29f..44c389b81 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -1,10 +1,30 @@ from app import db class Planet(db.Model): - id = db.Column( - db.Integer, - primary_key=True, - autoincrement=True) + __tablename__ = 'planets' + + id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False) description = db.Column(db.String, nullable=False) - mass = db.Column(db.Integer, nullable=False) \ No newline at end of file + mass = db.Column(db.Numeric(precision=8, scale=3), nullable=False) + # moons = db.Column(db.Integer, nullable=False) + # distance_from_sun = db.Column(db.Numeric(precision=8, scale=3), nullable=False) + # surface_area = db.Column(db.Numeric(precision=8, scale=3), nullable=False) + # namesake = db.Column(db.String, nullable=False) + # visited_by_humans = db.Column(db.Boolean, nullable=False, default=False) + + def __repr__(self): + return f'' + + def to_dict(self): + return { + 'id': self.id, + 'name': self.name, + 'description': self.description, + 'mass': self.mass, + # 'moons': self.moons, + # 'distance_from_sun': self.distance_from_sun, + # 'surface_area': self.surface_area, + # 'namesake': self.namesake, + # 'visited_by_humans': self.visited_by_humans + } \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 9da582108..167873bc2 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,41 +1,48 @@ from flask import Blueprint, jsonify, abort, make_response, request -from app.models.cat import Cat +from app.models.planet import Planet from app import db -cats_bp = Blueprint("cats", __name__, url_prefix="/cats") +planets_bp = Blueprint('planets', __name__, url_prefix='/planets') -# POST /cats -@cats_bp.route("", methods=["POST"]) -def create_cat(): +@planets_bp.route("", methods=['POST']) +def create_planet(): request_body = request.get_json() - new_cat = Cat( - name=request_body["name"], - color=request_body["color"], - personality=request_body["personality"], + new_planet = Planet( + name = request_body["name"], + description = request_body["description"], + mass = request_body["mass"], + # moons = request_body["moons"], + # distance_from_sun = request_body["distance_from_sun"], + # surface_area = request_body["surface_area"], + # namesake = request_body["namesake"], + # visited_by_humans = request_body['visited_by_humans'], ) - db.session.add(new_cat) + db.session.add(new_planet) db.session.commit() - message = f"Cat {new_cat.name} successfully created" + message = f"New planet {new_planet.name} successfully created!" return make_response(message, 201) -# GET /cats -@cats_bp.route("", methods=["GET"]) -def get_all_cats(): - cats = Cat.query.all() - results = [] - for cat in cats: - results.append( +@planets_bp.route("", methods=["GET"]) +def get_planets(): + planets = planets.all() + request_body = [] + for planet in planets: + request_body.append( dict( - id=cat.id, - name=cat.name, - color=cat.color, - personality=cat.personality + id=planet.id, + name=planet.name, + description=planet.description, + mass=planet.mass, + # moons=planet.moons, + # distance_from_sun=planet.distance_from_sun, + # surface_area=planet.surface_area, + # namesake=planet.namesake, + # visited_by_humans=planet.visited_by_humans, ) ) - - return jsonify(results) + return jsonify(request_body), 200 From 340352cdc31c3e8de05f8fe29e77f6baa75686de Mon Sep 17 00:00:00 2001 From: Whitney Date: Tue, 2 May 2023 13:39:26 -0700 Subject: [PATCH 08/18] Wave 3 Complete --- app/__init__.py | 2 +- migrations/README | 1 + migrations/alembic.ini | 50 ++++++++++ migrations/env.py | 91 +++++++++++++++++++ migrations/script.py.mako | 24 +++++ .../3f159e8bd5aa_creates_planet_model.py | 42 +++++++++ 6 files changed, 209 insertions(+), 1 deletion(-) 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/3f159e8bd5aa_creates_planet_model.py diff --git a/app/__init__.py b/app/__init__.py index e92e06adc..6ac270b47 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -9,7 +9,7 @@ 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_personal' + app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system' db.init_app(app) migrate.init_app(app, db) diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..0e0484415 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..ec9d45c26 --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,50 @@ +# 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,flask_migrate + +[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 + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[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..68feded2a --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,91 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +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.get_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 = current_app.extensions['migrate'].db.get_engine() + + 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/3f159e8bd5aa_creates_planet_model.py b/migrations/versions/3f159e8bd5aa_creates_planet_model.py new file mode 100644 index 000000000..441d56247 --- /dev/null +++ b/migrations/versions/3f159e8bd5aa_creates_planet_model.py @@ -0,0 +1,42 @@ +"""creates Planet model + +Revision ID: 3f159e8bd5aa +Revises: +Create Date: 2023-05-02 13:37:36.286901 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3f159e8bd5aa' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('planets', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('name', sa.String(), nullable=False), + sa.Column('description', sa.String(), nullable=False), + sa.Column('mass', sa.Numeric(precision=8, scale=3), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.drop_table('planet') + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('planet', + sa.Column('id', sa.INTEGER(), sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1), autoincrement=True, nullable=False), + sa.Column('name', sa.VARCHAR(length=32), autoincrement=False, nullable=False), + sa.Column('description', sa.VARCHAR(length=256), autoincrement=False, nullable=False), + sa.Column('mass', sa.INTEGER(), autoincrement=False, nullable=False), + sa.PrimaryKeyConstraint('id', name='planet_pkey') + ) + op.drop_table('planets') + # ### end Alembic commands ### From 91d8c5f0cc12dfd602d2c7e7cf9ccd5198024cf5 Mon Sep 17 00:00:00 2001 From: Jessica Date: Tue, 2 May 2023 18:24:47 -0600 Subject: [PATCH 09/18] Wave 4 - broken --- app/models/planet.py | 14 ----- app/routes.py | 134 +++++++++++++++++++------------------------ requirements.txt | 8 +++ 3 files changed, 67 insertions(+), 89 deletions(-) diff --git a/app/models/planet.py b/app/models/planet.py index 44c389b81..9235017c9 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -1,20 +1,11 @@ from app import db class Planet(db.Model): - __tablename__ = 'planets' id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False) description = db.Column(db.String, nullable=False) mass = db.Column(db.Numeric(precision=8, scale=3), nullable=False) - # moons = db.Column(db.Integer, nullable=False) - # distance_from_sun = db.Column(db.Numeric(precision=8, scale=3), nullable=False) - # surface_area = db.Column(db.Numeric(precision=8, scale=3), nullable=False) - # namesake = db.Column(db.String, nullable=False) - # visited_by_humans = db.Column(db.Boolean, nullable=False, default=False) - - def __repr__(self): - return f'' def to_dict(self): return { @@ -22,9 +13,4 @@ def to_dict(self): 'name': self.name, 'description': self.description, 'mass': self.mass, - # 'moons': self.moons, - # 'distance_from_sun': self.distance_from_sun, - # 'surface_area': self.surface_area, - # 'namesake': self.namesake, - # 'visited_by_humans': self.visited_by_humans } \ No newline at end of file diff --git a/app/routes.py b/app/routes.py index 167873bc2..1202e4807 100644 --- a/app/routes.py +++ b/app/routes.py @@ -4,29 +4,30 @@ planets_bp = Blueprint('planets', __name__, url_prefix='/planets') -@planets_bp.route("", methods=['POST']) -def create_planet(): - request_body = request.get_json() - new_planet = Planet( - name = request_body["name"], - description = request_body["description"], - mass = request_body["mass"], - # moons = request_body["moons"], - # distance_from_sun = request_body["distance_from_sun"], - # surface_area = request_body["surface_area"], - # namesake = request_body["namesake"], - # visited_by_humans = request_body['visited_by_humans'], - ) - - db.session.add(new_planet) - db.session.commit() - - message = f"New planet {new_planet.name} successfully created!" - return make_response(message, 201) +# @planets_bp.route("", methods=['POST']) +# def create_planet(): +# request_body = request.get_json() +# new_planet = Planet( +# name = request_body["name"], +# description = request_body["description"], +# mass = request_body["mass"], +# # moons = request_body["moons"], +# # distance_from_sun = request_body["distance_from_sun"], +# # surface_area = request_body["surface_area"], +# # namesake = request_body["namesake"], +# # visited_by_humans = request_body['visited_by_humans'], +# ) + +# db.session.add(new_planet) +# db.session.commit() + +# message = f"New planet {new_planet.name} successfully created, biatch!" + +# return make_response(message, 201) @planets_bp.route("", methods=["GET"]) def get_planets(): - planets = planets.all() + planets = Planet.query.all() request_body = [] for planet in planets: request_body.append( @@ -34,76 +35,59 @@ def get_planets(): id=planet.id, name=planet.name, description=planet.description, - mass=planet.mass, - # moons=planet.moons, - # distance_from_sun=planet.distance_from_sun, - # surface_area=planet.surface_area, - # namesake=planet.namesake, - # visited_by_humans=planet.visited_by_humans, + mass=planet.mass ) ) return jsonify(request_body), 200 +# def validate_planet(planet_id): +# try: +# planet_id = int(planet_id) +# except: +# abort(make_response({"message": f"Planet {planet_id} is invalid, sucker!"}, 400)) + +# planet = Planet.query.get(planet_id) +# if not planet: +# abort(make_response({"message": f"Planet {planet_id} was not found, sucker!"}, 404)) +# return planet +# @planets_bp.route("/", methods=['GET']) +# def read_one_planet(planet_id): +# planet = validate_planet(planet_id) +# return { +# "id" : planet.id, +# "name": planet.name, +# "description": planet.description, +# "mass": planet.mass +# } +# @planets_bp.route("/", methods=['PUT']) +# def update_planet(planet_id): +# planet = validate_planet(planet_id) +# request_body = request.get_json() + +# planet.name = request_body["name"] +# planet.description = request_body["description"] +# planet.mass = request_body["mass"] +# db.session.commit() +# message = f"Planet {planet} succesfully updated, biatch!" -# from flask import Blueprint, jsonify, abort, make_response - -# class Planet: -# def __init__(self, id, name, description, mass): -# self.id = id -# self.name = name -# self.description = description -# self.mass = mass - -# def to_dict(self): -# return dict( -# id = self.id, -# name = self.name, -# description = self.description, -# mass = self.mass -# ) - -# planet_list = [ -# Planet(1, "Mercury", "Small, rocky planet closest to the Sun", 0.330), -# Planet(2, "Venus", "Hottest planet in the solar system with a thick atmosphere", 4.87), -# Planet(3, "Earth", "Home to a diverse range of life forms, including humans", 5.97), -# Planet(4, "Mars", "Red planet with a thin atmosphere and polar ice caps", 0.642), -# Planet(5, "Jupiter", "Largest planet in the solar system with a thick atmosphere and many moons", 1898), -# Planet(6, "Saturn", "Known for its prominent rings made of ice and dust", 568), -# Planet(7, "Uranus", "Blue-green planet with a tilted axis of rotation", 86.8), -# Planet(8, "Neptune", "Blue planet with a windy atmosphere and many storms", 102), -# ] +# return make_response({"message": message}, 200) -# planets_bp = Blueprint("planets", __name__, url_prefix="/planets") +# @planets_bp.route("/", methods=['DELETE']) +# def delete_planet(planet_id): +# planet = validate_planet(planet_id) -# @planets_bp.route("", methods=["GET"]) -# def get_planets(): -# planets_response = [] -# for planet in planet_list: -# planets_response.append(planet.to_dict()) +# db.session.delete(planet) +# db.session.commit() -# return jsonify(planets_response), 200 +# message = f"Planet {planet} succesfully deleted, biatch!" -# @planets_bp.route("/", methods=["GET"]) -# def get_planet(id): -# planet = validate_planet(id) +# return make_response({"message": message}, 200) -# return planet.to_dict() -# def validate_planet(id): -# try: -# id = int(id) - -# except: -# abort(make_response({"message": f"Planet {id} is invalid!"}, 400)) - -# for planet in planet_list: -# if planet.id == id: -# return planet -# abort(make_response({"message": f"Planet {id} is not found!"}, 404)) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0101e065c..e54ac3ae5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +alembic==1.10.4 anyio==3.6.2 appnope==0.1.3 argon2-cffi==21.3.0 @@ -19,6 +20,8 @@ defusedxml==0.7.1 executing==1.2.0 fastjsonschema==2.16.3 Flask==2.2.3 +Flask-Migrate==4.0.4 +Flask-SQLAlchemy==3.0.3 fqdn==1.5.1 idna==3.4 iniconfig==2.0.0 @@ -41,6 +44,7 @@ jupyter_server==2.5.0 jupyter_server_terminals==0.4.4 jupyterlab-pygments==0.2.2 jupyterlab-widgets==3.0.7 +Mako==1.2.4 MarkupSafe==2.1.2 matplotlib-inline==0.1.6 mistune==2.0.5 @@ -61,6 +65,7 @@ pluggy==1.0.0 prometheus-client==0.16.0 prompt-toolkit==3.0.38 psutil==5.9.4 +psycopg2-binary==2.9.6 ptyprocess==0.7.0 pure-eval==0.2.2 pycparser==2.21 @@ -68,6 +73,7 @@ Pygments==2.14.0 pyrsistent==0.19.3 pytest==7.2.2 python-dateutil==2.8.2 +python-dotenv==1.0.0 python-json-logger==2.0.7 PyYAML==6.0 pyzmq==25.0.2 @@ -80,11 +86,13 @@ Send2Trash==1.8.0 six==1.16.0 sniffio==1.3.0 soupsieve==2.4 +SQLAlchemy==2.0.11 stack-data==0.6.2 terminado==0.17.1 tinycss2==1.2.1 tornado==6.2 traitlets==5.9.0 +typing_extensions==4.5.0 uri-template==1.2.0 urllib3==1.26.14 wcwidth==0.2.6 From 73ad927958c6df01a300e462038507ba10be5369 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 3 May 2023 13:36:31 -0600 Subject: [PATCH 10/18] Fixed database issue in code --- app/models/planet.py | 3 +- app/routes.py | 113 +++++++++--------- migrations/README | 1 - migrations/alembic.ini | 50 -------- migrations/env.py | 91 -------------- migrations/script.py.mako | 24 ---- .../3f159e8bd5aa_creates_planet_model.py | 42 ------- requirement.txt | 104 ++++++++++++++++ 8 files changed, 160 insertions(+), 268 deletions(-) delete mode 100644 migrations/README delete mode 100644 migrations/alembic.ini delete mode 100644 migrations/env.py delete mode 100644 migrations/script.py.mako delete mode 100644 migrations/versions/3f159e8bd5aa_creates_planet_model.py create mode 100644 requirement.txt diff --git a/app/models/planet.py b/app/models/planet.py index 9235017c9..673ba2bc6 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -1,11 +1,12 @@ from app import db class Planet(db.Model): + __tablename__ = 'planets' id = db.Column(db.Integer, primary_key=True, autoincrement=True) name = db.Column(db.String, nullable=False) description = db.Column(db.String, nullable=False) - mass = db.Column(db.Numeric(precision=8, scale=3), nullable=False) + mass = db.Column(db.Numeric(precision=16, scale=3), nullable=False) def to_dict(self): return { diff --git a/app/routes.py b/app/routes.py index 1202e4807..455c7a3f8 100644 --- a/app/routes.py +++ b/app/routes.py @@ -4,26 +4,21 @@ planets_bp = Blueprint('planets', __name__, url_prefix='/planets') -# @planets_bp.route("", methods=['POST']) -# def create_planet(): -# request_body = request.get_json() -# new_planet = Planet( -# name = request_body["name"], -# description = request_body["description"], -# mass = request_body["mass"], -# # moons = request_body["moons"], -# # distance_from_sun = request_body["distance_from_sun"], -# # surface_area = request_body["surface_area"], -# # namesake = request_body["namesake"], -# # visited_by_humans = request_body['visited_by_humans'], -# ) - -# db.session.add(new_planet) -# db.session.commit() - -# message = f"New planet {new_planet.name} successfully created, biatch!" - -# return make_response(message, 201) +@planets_bp.route("", methods=['POST']) +def create_planet(): + request_body = request.get_json() + new_planet = Planet( + name = request_body["name"], + description = request_body["description"], + mass = request_body["mass"], + ) + + db.session.add(new_planet) + db.session.commit() + + message = f"New planet {new_planet.name} successfully created, biatch!" + + return make_response(message, 201) @planets_bp.route("", methods=["GET"]) def get_planets(): @@ -40,54 +35,54 @@ def get_planets(): ) return jsonify(request_body), 200 -# def validate_planet(planet_id): -# try: -# planet_id = int(planet_id) -# except: -# abort(make_response({"message": f"Planet {planet_id} is invalid, sucker!"}, 400)) +def validate_planet(planet_id): + try: + planet_id = int(planet_id) + except: + abort(make_response({"message": f"Planet {planet_id} is invalid, sucker!"}, 400)) -# planet = Planet.query.get(planet_id) - -# if not planet: -# abort(make_response({"message": f"Planet {planet_id} was not found, sucker!"}, 404)) - -# return planet - -# @planets_bp.route("/", methods=['GET']) -# def read_one_planet(planet_id): -# planet = validate_planet(planet_id) -# return { -# "id" : planet.id, -# "name": planet.name, -# "description": planet.description, -# "mass": planet.mass -# } - -# @planets_bp.route("/", methods=['PUT']) -# def update_planet(planet_id): -# planet = validate_planet(planet_id) -# request_body = request.get_json() + planet = Planet.query.get(planet_id) + + if not planet: + abort(make_response({"message": f"Planet {planet_id} was not found, sucker!"}, 404)) + + return planet + +@planets_bp.route("/", methods=['GET']) +def read_one_planet(planet_id): + planet = validate_planet(planet_id) + return { + "id" : planet.id, + "name": planet.name, + "description": planet.description, + "mass": planet.mass + } + +@planets_bp.route("/", methods=['PUT']) +def update_planet(planet_id): + planet = validate_planet(planet_id) + request_body = request.get_json() -# planet.name = request_body["name"] -# planet.description = request_body["description"] -# planet.mass = request_body["mass"] + planet.name = request_body["name"] + planet.description = request_body["description"] + planet.mass = request_body["mass"] -# db.session.commit() + db.session.commit() -# message = f"Planet {planet} succesfully updated, biatch!" + message = f"Planet {planet} succesfully updated, biatch!" -# return make_response({"message": message}, 200) + return make_response({"message": message}, 200) -# @planets_bp.route("/", methods=['DELETE']) -# def delete_planet(planet_id): -# planet = validate_planet(planet_id) +@planets_bp.route("/", methods=['DELETE']) +def delete_planet(planet_id): + planet = validate_planet(planet_id) -# db.session.delete(planet) -# db.session.commit() + db.session.delete(planet) + db.session.commit() -# message = f"Planet {planet} succesfully deleted, biatch!" + message = f"Planet {planet} succesfully deleted, biatch!" -# return make_response({"message": message}, 200) + return make_response({"message": message}, 200) diff --git a/migrations/README b/migrations/README deleted file mode 100644 index 0e0484415..000000000 --- a/migrations/README +++ /dev/null @@ -1 +0,0 @@ -Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini deleted file mode 100644 index ec9d45c26..000000000 --- a/migrations/alembic.ini +++ /dev/null @@ -1,50 +0,0 @@ -# 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,flask_migrate - -[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 - -[logger_flask_migrate] -level = INFO -handlers = -qualname = flask_migrate - -[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 deleted file mode 100644 index 68feded2a..000000000 --- a/migrations/env.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import with_statement - -import logging -from logging.config import fileConfig - -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.get_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 = current_app.extensions['migrate'].db.get_engine() - - 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 deleted file mode 100644 index 2c0156303..000000000 --- a/migrations/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${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/3f159e8bd5aa_creates_planet_model.py b/migrations/versions/3f159e8bd5aa_creates_planet_model.py deleted file mode 100644 index 441d56247..000000000 --- a/migrations/versions/3f159e8bd5aa_creates_planet_model.py +++ /dev/null @@ -1,42 +0,0 @@ -"""creates Planet model - -Revision ID: 3f159e8bd5aa -Revises: -Create Date: 2023-05-02 13:37:36.286901 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '3f159e8bd5aa' -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('planets', - sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), - sa.Column('name', sa.String(), nullable=False), - sa.Column('description', sa.String(), nullable=False), - sa.Column('mass', sa.Numeric(precision=8, scale=3), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.drop_table('planet') - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('planet', - sa.Column('id', sa.INTEGER(), sa.Identity(always=True, start=1, increment=1, minvalue=1, maxvalue=2147483647, cycle=False, cache=1), autoincrement=True, nullable=False), - sa.Column('name', sa.VARCHAR(length=32), autoincrement=False, nullable=False), - sa.Column('description', sa.VARCHAR(length=256), autoincrement=False, nullable=False), - sa.Column('mass', sa.INTEGER(), autoincrement=False, nullable=False), - sa.PrimaryKeyConstraint('id', name='planet_pkey') - ) - op.drop_table('planets') - # ### end Alembic commands ### diff --git a/requirement.txt b/requirement.txt new file mode 100644 index 000000000..e54ac3ae5 --- /dev/null +++ b/requirement.txt @@ -0,0 +1,104 @@ +alembic==1.10.4 +anyio==3.6.2 +appnope==0.1.3 +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +arrow==1.2.3 +asttokens==2.2.1 +attrs==22.2.0 +backcall==0.2.0 +beautifulsoup4==4.12.0 +bleach==6.0.0 +certifi==2022.12.7 +cffi==1.15.1 +charset-normalizer==3.0.1 +click==8.1.3 +comm==0.1.3 +debugpy==1.6.6 +decorator==5.1.1 +defusedxml==0.7.1 +executing==1.2.0 +fastjsonschema==2.16.3 +Flask==2.2.3 +Flask-Migrate==4.0.4 +Flask-SQLAlchemy==3.0.3 +fqdn==1.5.1 +idna==3.4 +iniconfig==2.0.0 +ipykernel==6.22.0 +ipython==8.11.0 +ipython-genutils==0.2.0 +ipywidgets==8.0.6 +isoduration==20.11.0 +itsdangerous==2.1.2 +jedi==0.18.2 +Jinja2==3.1.2 +jsonpointer==2.3 +jsonschema==4.17.3 +jupyter==1.0.0 +jupyter-console==6.6.3 +jupyter-events==0.6.3 +jupyter_client==8.1.0 +jupyter_core==5.3.0 +jupyter_server==2.5.0 +jupyter_server_terminals==0.4.4 +jupyterlab-pygments==0.2.2 +jupyterlab-widgets==3.0.7 +Mako==1.2.4 +MarkupSafe==2.1.2 +matplotlib-inline==0.1.6 +mistune==2.0.5 +nbclassic==0.5.3 +nbclient==0.7.2 +nbconvert==7.2.10 +nbformat==5.8.0 +nest-asyncio==1.5.6 +notebook==6.5.3 +notebook_shim==0.2.2 +packaging==23.0 +pandocfilters==1.5.0 +parso==0.8.3 +pexpect==4.8.0 +pickleshare==0.7.5 +platformdirs==3.2.0 +pluggy==1.0.0 +prometheus-client==0.16.0 +prompt-toolkit==3.0.38 +psutil==5.9.4 +psycopg2-binary==2.9.6 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pycparser==2.21 +Pygments==2.14.0 +pyrsistent==0.19.3 +pytest==7.2.2 +python-dateutil==2.8.2 +python-dotenv==1.0.0 +python-json-logger==2.0.7 +PyYAML==6.0 +pyzmq==25.0.2 +qtconsole==5.4.1 +QtPy==2.3.1 +requests==2.28.2 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +Send2Trash==1.8.0 +six==1.16.0 +sniffio==1.3.0 +soupsieve==2.4 +SQLAlchemy==2.0.11 +stack-data==0.6.2 +terminado==0.17.1 +tinycss2==1.2.1 +tornado==6.2 +traitlets==5.9.0 +typing_extensions==4.5.0 +uri-template==1.2.0 +urllib3==1.26.14 +wcwidth==0.2.6 +webcolors==1.13 +webencodings==0.5.1 +websocket-client==1.5.1 +Werkzeug==2.2.3 +widgetsnbextension==4.0.7 +wonderwords==2.2.0 From 983fe4be1690ac8cd1a0988eb63e482e70532840 Mon Sep 17 00:00:00 2001 From: Jessica Date: Wed, 3 May 2023 13:42:03 -0600 Subject: [PATCH 11/18] fix requirements issues --- requirement.txt | 104 ------------------------------------------------ 1 file changed, 104 deletions(-) delete mode 100644 requirement.txt diff --git a/requirement.txt b/requirement.txt deleted file mode 100644 index e54ac3ae5..000000000 --- a/requirement.txt +++ /dev/null @@ -1,104 +0,0 @@ -alembic==1.10.4 -anyio==3.6.2 -appnope==0.1.3 -argon2-cffi==21.3.0 -argon2-cffi-bindings==21.2.0 -arrow==1.2.3 -asttokens==2.2.1 -attrs==22.2.0 -backcall==0.2.0 -beautifulsoup4==4.12.0 -bleach==6.0.0 -certifi==2022.12.7 -cffi==1.15.1 -charset-normalizer==3.0.1 -click==8.1.3 -comm==0.1.3 -debugpy==1.6.6 -decorator==5.1.1 -defusedxml==0.7.1 -executing==1.2.0 -fastjsonschema==2.16.3 -Flask==2.2.3 -Flask-Migrate==4.0.4 -Flask-SQLAlchemy==3.0.3 -fqdn==1.5.1 -idna==3.4 -iniconfig==2.0.0 -ipykernel==6.22.0 -ipython==8.11.0 -ipython-genutils==0.2.0 -ipywidgets==8.0.6 -isoduration==20.11.0 -itsdangerous==2.1.2 -jedi==0.18.2 -Jinja2==3.1.2 -jsonpointer==2.3 -jsonschema==4.17.3 -jupyter==1.0.0 -jupyter-console==6.6.3 -jupyter-events==0.6.3 -jupyter_client==8.1.0 -jupyter_core==5.3.0 -jupyter_server==2.5.0 -jupyter_server_terminals==0.4.4 -jupyterlab-pygments==0.2.2 -jupyterlab-widgets==3.0.7 -Mako==1.2.4 -MarkupSafe==2.1.2 -matplotlib-inline==0.1.6 -mistune==2.0.5 -nbclassic==0.5.3 -nbclient==0.7.2 -nbconvert==7.2.10 -nbformat==5.8.0 -nest-asyncio==1.5.6 -notebook==6.5.3 -notebook_shim==0.2.2 -packaging==23.0 -pandocfilters==1.5.0 -parso==0.8.3 -pexpect==4.8.0 -pickleshare==0.7.5 -platformdirs==3.2.0 -pluggy==1.0.0 -prometheus-client==0.16.0 -prompt-toolkit==3.0.38 -psutil==5.9.4 -psycopg2-binary==2.9.6 -ptyprocess==0.7.0 -pure-eval==0.2.2 -pycparser==2.21 -Pygments==2.14.0 -pyrsistent==0.19.3 -pytest==7.2.2 -python-dateutil==2.8.2 -python-dotenv==1.0.0 -python-json-logger==2.0.7 -PyYAML==6.0 -pyzmq==25.0.2 -qtconsole==5.4.1 -QtPy==2.3.1 -requests==2.28.2 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -Send2Trash==1.8.0 -six==1.16.0 -sniffio==1.3.0 -soupsieve==2.4 -SQLAlchemy==2.0.11 -stack-data==0.6.2 -terminado==0.17.1 -tinycss2==1.2.1 -tornado==6.2 -traitlets==5.9.0 -typing_extensions==4.5.0 -uri-template==1.2.0 -urllib3==1.26.14 -wcwidth==0.2.6 -webcolors==1.13 -webencodings==0.5.1 -websocket-client==1.5.1 -Werkzeug==2.2.3 -widgetsnbextension==4.0.7 -wonderwords==2.2.0 From f9b00d78b8b3c0565ec3b06a1279810f317c3697 Mon Sep 17 00:00:00 2001 From: Jessica Date: Thu, 4 May 2023 16:01:42 -0600 Subject: [PATCH 12/18] Setup tests files --- migrations/README | 1 + migrations/alembic.ini | 50 +++++++++++++++++ migrations/env.py | 110 ++++++++++++++++++++++++++++++++++++++ migrations/script.py.mako | 24 +++++++++ tests/__init__.py | 0 tests/conftest.py | 23 ++++++++ tests/test_routes.py | 0 7 files changed, 208 insertions(+) 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 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_routes.py diff --git a/migrations/README b/migrations/README new file mode 100644 index 000000000..0e0484415 --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Single-database configuration for Flask. diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 000000000..ec9d45c26 --- /dev/null +++ b/migrations/alembic.ini @@ -0,0 +1,50 @@ +# 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,flask_migrate + +[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 + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[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..89f80b211 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,110 @@ +import logging +from logging.config import fileConfig + +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') + + +def get_engine(): + try: + # this works with Flask-SQLAlchemy<3 and Alchemical + return current_app.extensions['migrate'].db.get_engine() + except TypeError: + # this works with Flask-SQLAlchemy>=3 + return current_app.extensions['migrate'].db.engine + + +def get_engine_url(): + try: + return get_engine().url.render_as_string(hide_password=False).replace( + '%', '%%') + except AttributeError: + return str(get_engine().url).replace('%', '%%') + + +# 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', get_engine_url()) +target_db = current_app.extensions['migrate'].db + +# 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 get_metadata(): + if hasattr(target_db, 'metadatas'): + return target_db.metadatas[None] + return target_db.metadata + + +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=get_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 = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_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/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..a0b845504 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,23 @@ +import pytest +from app import create_app, db +from flask.signals import request_finished +from app.models.planet import Book + +@pytest.fixture +def app(): + app = create_app({"TESTING": True}) + + @request_finished.connect_via(app) + def expire_session(sender, response, **extra): + db.session.remove() + + with app.app_context(): + db.create_all() + yield app + + with app.app_context(): + db.drop_all() + +@pytest.fixture +def client(app): + return app.test_client() \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py new file mode 100644 index 000000000..e69de29bb From be03c4cc89c8fb8812f8afce1bd84d2ebfef8471 Mon Sep 17 00:00:00 2001 From: Whitney Date: Thu, 4 May 2023 15:42:05 -0700 Subject: [PATCH 13/18] Co-authored-by: Yiskah-S --- app/__init__.py | 14 ++++++++++++-- tests/conftest.py | 16 +++++++++++++-- tests/test_routes.py | 46 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 6ac270b47..b0b05851e 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,19 +1,29 @@ from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate +from dotenv import load_dotenv +import os db = SQLAlchemy() migrate = Migrate() +load_dotenv() 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' + + 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) + from app.models.planet import Planet + + from app.routes import planets_bp app.register_blueprint(planets_bp) diff --git a/tests/conftest.py b/tests/conftest.py index a0b845504..ec74fd910 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import pytest from app import create_app, db from flask.signals import request_finished -from app.models.planet import Book +from app.models.planet import Planet @pytest.fixture def app(): @@ -20,4 +20,16 @@ def expire_session(sender, response, **extra): @pytest.fixture def client(app): - return app.test_client() \ No newline at end of file + return app.test_client() + +@pytest.fixture +def one_planet(app): + planet = Planet( + name = "test_name", + description = "test_description", + mass = "test_mass" + ) + db.session.add(planet) + db.session.commit() + + return planet \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py index e69de29bb..5fb956c54 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -0,0 +1,46 @@ +from app.models.planet import Planet + +def test_get_all_returns_empty_list_when_database_is_empty(client): + + #Act + response = client.get("/planets") + + #Assert + assert response.status_code == 200 + assert response.get_json() == [] + + +def test_get_one_planet_returns_seeded_planet(client, one_planet): + + #Act + response = client.get(f"/planets/{one_planet.id}") + response_body = response.get_json() + + #Assert + assert response.status_code == 200 + assert response_body["id"] == one_planet.id + assert response_body["name"] == one_planet.name + assert response_body["description"] == one_planet.description + assert response_body["mass"] == one_planet.mass + + +def test_create_planet_happy_path(client): + + #Arrange + EXPECTED_PLANET = { + "name": "test_name", + "description": "test_description", + "mass": "mass"} + + #Assert + response = client.post("/planets", json=EXPECTED_PLANET) + response_body = response.get_data(as_text=True) + + actual_planet = Planet.query.get(1) + assert response.status_code == 201 + assert response_body == f"Planets {EXPECTED_PLANET["name"]} successfully created" + assert actual_planet.name == EXPECTED_PLANET["name"] + assert actual_planet.description == EXPECTED_PLANET["description"] + assert actual_planet.mass == EXPECTED_PLANET["mass"] + + From a1ad3e0243337f247d510716aff2b5a28999a4e8 Mon Sep 17 00:00:00 2001 From: Jessica Date: Thu, 4 May 2023 18:07:27 -0600 Subject: [PATCH 14/18] wave 6 some tests --- project-directions/wave_06.md | 28 ++++++++++++++++++++++++++++ requirements.txt | 1 + tests/conftest.py | 6 +++--- tests/test_routes.py | 7 ++++--- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/project-directions/wave_06.md b/project-directions/wave_06.md index 7d854f465..03a5ff62d 100644 --- a/project-directions/wave_06.md +++ b/project-directions/wave_06.md @@ -1,3 +1,31 @@ +## Writing Tests + +Create test fixtures and unit tests for the following test cases: + +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` + + +## Code Coverage + +Check your code coverage using `pytest-cov`. Review the [code coverage exercise](https://github.com/adaGold/code-coverage-exercise) on how to use `pytest-cov` to generate a code coverage report. We will need to change the directory where the application code is located from `student` to `app`. + +`pytest --cov=app --cov-report html --cov-report term` + +For this project, we will not expect to have high test coverage because we have not tested all of our CRUD routes. Still, it is helpful to practice checking coverage and reading reports of the code which detail the code that is tested, and the code that is not tested. + + + + + + + + + + + # Wave 06: Writing Tests ## Setup diff --git a/requirements.txt b/requirements.txt index e54ac3ae5..1bb1716f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ attrs==22.2.0 backcall==0.2.0 beautifulsoup4==4.12.0 bleach==6.0.0 +blinker==1.6.2 certifi==2022.12.7 cffi==1.15.1 charset-normalizer==3.0.1 diff --git a/tests/conftest.py b/tests/conftest.py index ec74fd910..4d0f4e540 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -25,9 +25,9 @@ def client(app): @pytest.fixture def one_planet(app): planet = Planet( - name = "test_name", - description = "test_description", - mass = "test_mass" + name = "test_name_1", + description = "test_description_1", + mass = 1.000 ) db.session.add(planet) db.session.commit() diff --git a/tests/test_routes.py b/tests/test_routes.py index 5fb956c54..f6735c55e 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -21,7 +21,7 @@ def test_get_one_planet_returns_seeded_planet(client, one_planet): assert response_body["id"] == one_planet.id assert response_body["name"] == one_planet.name assert response_body["description"] == one_planet.description - assert response_body["mass"] == one_planet.mass + assert response_body["mass"] == str(one_planet.mass) def test_create_planet_happy_path(client): @@ -30,7 +30,8 @@ def test_create_planet_happy_path(client): EXPECTED_PLANET = { "name": "test_name", "description": "test_description", - "mass": "mass"} + "mass": 1, + } #Assert response = client.post("/planets", json=EXPECTED_PLANET) @@ -38,7 +39,7 @@ def test_create_planet_happy_path(client): actual_planet = Planet.query.get(1) assert response.status_code == 201 - assert response_body == f"Planets {EXPECTED_PLANET["name"]} successfully created" + assert response_body == f"New planet {EXPECTED_PLANET['name']} successfully created, biatch!" assert actual_planet.name == EXPECTED_PLANET["name"] assert actual_planet.description == EXPECTED_PLANET["description"] assert actual_planet.mass == EXPECTED_PLANET["mass"] From a5d6b178bb064d9aaa306657bcdb8cb25847cadb Mon Sep 17 00:00:00 2001 From: Jessica Date: Thu, 4 May 2023 20:32:07 -0600 Subject: [PATCH 15/18] Wave 6 --- app/models/planet.py | 3 +- project-directions/wave_06.md | 7 ---- tests/conftest.py | 22 +++++++++---- tests/test_routes.py | 60 ++++++++++++++++++++++++++--------- 4 files changed, 62 insertions(+), 30 deletions(-) diff --git a/app/models/planet.py b/app/models/planet.py index 673ba2bc6..0bfc5cc3e 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -14,4 +14,5 @@ def to_dict(self): 'name': self.name, 'description': self.description, 'mass': self.mass, - } \ No newline at end of file + } + \ No newline at end of file diff --git a/project-directions/wave_06.md b/project-directions/wave_06.md index 03a5ff62d..6a74fea86 100644 --- a/project-directions/wave_06.md +++ b/project-directions/wave_06.md @@ -1,11 +1,4 @@ -## Writing Tests - -Create test fixtures and unit tests for the following test cases: -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` ## Code Coverage diff --git a/tests/conftest.py b/tests/conftest.py index 4d0f4e540..0325fc2a1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,12 +24,20 @@ def client(app): @pytest.fixture def one_planet(app): - planet = Planet( - name = "test_name_1", - description = "test_description_1", - mass = 1.000 - ) - db.session.add(planet) + return { + "name" : "test_name_1", + "description" : "test_description_1", + "mass" : 1.000} + + +@pytest.fixture +def get_planets(app): + test_planet_2 = Planet(name = "test_name_2", description = "test_description_2", mass = 2.000) + test_planet_3 = Planet(name = "test_name_3", description = "test_description_3", mass = 3.000) + + test_planets = [test_planet_2, test_planet_3] + + db.session.add_all(test_planets) db.session.commit() - return planet \ No newline at end of file + return test_planets \ No newline at end of file diff --git a/tests/test_routes.py b/tests/test_routes.py index f6735c55e..268a70e6e 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -1,39 +1,27 @@ from app.models.planet import Planet def test_get_all_returns_empty_list_when_database_is_empty(client): - - #Act response = client.get("/planets") - #Assert assert response.status_code == 200 assert response.get_json() == [] -def test_get_one_planet_returns_seeded_planet(client, one_planet): - - #Act - response = client.get(f"/planets/{one_planet.id}") +def test_get_one_planet_returns_planet(client, get_planets): + response = client.get(f"/planets/1") response_body = response.get_json() - #Assert assert response.status_code == 200 - assert response_body["id"] == one_planet.id - assert response_body["name"] == one_planet.name - assert response_body["description"] == one_planet.description - assert response_body["mass"] == str(one_planet.mass) + assert response_body == {"id" : 1, "name" : "test_name_2", "description" : "test_description_2", "mass" : "2.000"} def test_create_planet_happy_path(client): - - #Arrange EXPECTED_PLANET = { "name": "test_name", "description": "test_description", "mass": 1, } - #Assert response = client.post("/planets", json=EXPECTED_PLANET) response_body = response.get_data(as_text=True) @@ -45,3 +33,45 @@ def test_create_planet_happy_path(client): assert actual_planet.mass == EXPECTED_PLANET["mass"] +def test_get_none_existing_planet_returns_404(client, get_planets): + response = client.get("/planets/404") + + response_body = response.get_json() + + assert response.status_code == 404 + assert response_body == {'message': 'Planet 404 was not found, sucker!'} + +def test_get_invalid_planet_returns_400(client, get_planets): + response = client.get("/planets/noob") + + response_body = response.get_json() + + assert response.status_code == 400 + assert response_body == {'message': 'Planet noob is invalid, sucker!'} + +def test_get_all_planets_returns_array_of_planets_and_200(client, get_planets): + response = client.get("/planets") + response_body = response.get_json() + + assert response.status_code == 200 + assert len(response_body) == 2 + assert response_body == [{"id" : 1, "name" : "test_name_2", "description" : "test_description_2", "mass" : "2.000"}, + {"id" : 2, "name" : "test_name_3", "description" : "test_description_3", "mass" : "3.000"}] + +def test_delete_one_planet(client, get_planets): + response = client.delete("/planets/1") + response_body = response.get_json() + + assert response.status_code == 200 + assert response_body == {'message': 'Planet succesfully deleted, biatch!'} + +def test_update_one_planet(client, get_planets, one_planet): + response = client.put("/planets/2", json=one_planet) + response_body = response.get_json() + response_array = client.get("/planets") + updated_array = response_array.get_json() + + assert response.status_code == 200 + assert response_body == {'message': 'Planet succesfully updated, biatch!'} + assert updated_array == [{'id': 1, 'name': 'test_name_2', 'description': 'test_description_2','mass': '2.000'}, + {'id': 2, 'name': 'test_name_1', 'description': 'test_description_1', 'mass': '1.000'}] \ No newline at end of file From ab2196ddf8379628d9e13fec4536868ba707d799 Mon Sep 17 00:00:00 2001 From: Jessica Date: Thu, 4 May 2023 20:53:21 -0600 Subject: [PATCH 16/18] Wave 6 finished plus query params --- app/routes.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index 455c7a3f8..8329b68d7 100644 --- a/app/routes.py +++ b/app/routes.py @@ -22,7 +22,15 @@ def create_planet(): @planets_bp.route("", methods=["GET"]) def get_planets(): - planets = Planet.query.all() + mass = request.args.get("mass") + + planets = Planet.query + + if mass: + planets = planets.filter_by(mass=mass) + + planets = planets.all() + request_body = [] for planet in planets: request_body.append( From b03bd1887e63a02fdc7a21bf5645213fd0c2246f Mon Sep 17 00:00:00 2001 From: Whitney Date: Thu, 4 May 2023 20:28:31 -0700 Subject: [PATCH 17/18] All tests passing --- app/routes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/routes.py b/app/routes.py index 8329b68d7..5c6158dc8 100644 --- a/app/routes.py +++ b/app/routes.py @@ -23,11 +23,19 @@ def create_planet(): @planets_bp.route("", methods=["GET"]) def get_planets(): mass = request.args.get("mass") + min_mass = request.args.get("min_mass") + max_mass = request.args.get("max_mass") planets = Planet.query if mass: planets = planets.filter_by(mass=mass) + + if min_mass: + planets = planets.filter_by(mass=min_mass) + + if max_mass: + planets = planets.filter_by(mass=max_mass) planets = planets.all() From fc669b6eec3b8fb359d39c2d6b0bf5c5b9b8c0ae Mon Sep 17 00:00:00 2001 From: Jessica Date: Thu, 4 May 2023 21:42:02 -0600 Subject: [PATCH 18/18] Final version --- app/routes.py | 8 +++++++- tests/test_routes.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/routes.py b/app/routes.py index 8329b68d7..85c531060 100644 --- a/app/routes.py +++ b/app/routes.py @@ -23,11 +23,17 @@ def create_planet(): @planets_bp.route("", methods=["GET"]) def get_planets(): mass = request.args.get("mass") + # min_mass = request.args.get("min_mass") + # max_mass = request.args.get("max_mass") planets = Planet.query if mass: - planets = planets.filter_by(mass=mass) + planets = planets.filter_by(mass = mass) + # if min_mass: + # planets = planets.filter(Planet.mass >= min_mass) + # if min_mass: + # planets = planets.filter_by(Planet.mass <= max_mass) planets = planets.all() diff --git a/tests/test_routes.py b/tests/test_routes.py index 268a70e6e..29885cbc5 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -68,6 +68,7 @@ def test_delete_one_planet(client, get_planets): def test_update_one_planet(client, get_planets, one_planet): response = client.put("/planets/2", json=one_planet) response_body = response.get_json() + response_array = client.get("/planets") updated_array = response_array.get_json()