From fe0c62ab7223aeadaa935a37f34bd348e8280491 Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 18 Oct 2024 12:00:32 -0700 Subject: [PATCH 01/31] Finish wave 1 together --- app/__init__.py | 3 +++ app/models/__init__.py | 0 app/models/planets.py | 17 +++++++++++++++++ app/routes.py | 2 -- app/routes/__init__.py | 0 app/routes/planets_routes.py | 16 ++++++++++++++++ 6 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 app/models/__init__.py create mode 100644 app/models/planets.py delete mode 100644 app/routes.py create mode 100644 app/routes/__init__.py create mode 100644 app/routes/planets_routes.py diff --git a/app/__init__.py b/app/__init__.py index 70b4cabfe..c44764e9d 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,7 +1,10 @@ from flask import Flask +from app.routes.planets_routes import planet_bp def create_app(test_config=None): app = Flask(__name__) + + app.register_blueprint(planet_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/planets.py b/app/models/planets.py new file mode 100644 index 000000000..fbeb892aa --- /dev/null +++ b/app/models/planets.py @@ -0,0 +1,17 @@ +class Planet: + def __init__(self, id, name, description): + self.id = id + self.name = name + self.description = description + + +planets = [ + Planet(1, "Mercury", "Smallest planet and closest to the Sun."), + Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere."), + Planet(3, "Earth", "The only planet known to support life."), + Planet(4, "Mars", "The Red Planet, known for its volcanoes."), + Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot."), + Planet(6, "Saturn", "Known for its stunning rings."), + Planet(7, "Uranus", "An ice giant that rotates on its side."), + Planet(8, "Neptune", "The farthest planet, known for strong winds.") +] \ No newline at end of file diff --git a/app/routes.py b/app/routes.py deleted file mode 100644 index 8e9dfe684..000000000 --- a/app/routes.py +++ /dev/null @@ -1,2 +0,0 @@ -from flask import Blueprint - diff --git a/app/routes/__init__.py b/app/routes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py new file mode 100644 index 000000000..1e1b89ddd --- /dev/null +++ b/app/routes/planets_routes.py @@ -0,0 +1,16 @@ +from flask import Blueprint +from app.models.planets import planets + +planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") + +@planet_bp.get("") +def get_all_planets(): + planets_response = [] + for planet in planets: + planets_response.append(dict( + id=planet.id, + name=planet.name, + description=planet.description + )) + + return planets_response \ No newline at end of file From 67a0ce792cc98520d40aa365e7edf74d0c4459a4 Mon Sep 17 00:00:00 2001 From: Liubov Dav Date: Fri, 18 Oct 2024 15:09:15 -0400 Subject: [PATCH 02/31] cann't resolve conflict --- app/__init__.py | 2 ++ app/models/__init__.py | 0 app/models/planets.py | 17 +++++++++++++++++ app/routes.py | 2 -- app/routes/__init__.py | 0 app/routes/planets_routes.py | 23 +++++++++++++++++++++++ 6 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 app/models/__init__.py create mode 100644 app/models/planets.py delete mode 100644 app/routes.py create mode 100644 app/routes/__init__.py create mode 100644 app/routes/planets_routes.py diff --git a/app/__init__.py b/app/__init__.py index 70b4cabfe..27f314674 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,7 +1,9 @@ from flask import Flask +from app.routes.planets_routes import planet_bp def create_app(test_config=None): app = Flask(__name__) + app.register_blueprint(planet_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/planets.py b/app/models/planets.py new file mode 100644 index 000000000..5f9f49670 --- /dev/null +++ b/app/models/planets.py @@ -0,0 +1,17 @@ +class Planet: + + def __init__(self, id, name, description): + self.id = id + self.name = name + self.description = description + +planets = [ + Planet(1, "Mercury", "Smallest planet and closest to the Sun."), + Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere."), + Planet(3, "Earth", "The only planet known to support life."), + Planet(4, "Mars", "The Red Planet, known for its volcanoes."), + Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot."), + Planet(6, "Saturn", "Known for its stunning rings."), + Planet(7, "Uranus", "An ice giant that rotates on its side."), + Planet(8, "Neptune", "The farthest planet, known for strong winds.") +] diff --git a/app/routes.py b/app/routes.py deleted file mode 100644 index 8e9dfe684..000000000 --- a/app/routes.py +++ /dev/null @@ -1,2 +0,0 @@ -from flask import Blueprint - diff --git a/app/routes/__init__.py b/app/routes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py new file mode 100644 index 000000000..143931a11 --- /dev/null +++ b/app/routes/planets_routes.py @@ -0,0 +1,23 @@ +from flask import Blueprint +from app.models.planets import planets + + +planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") + +@planet_bp.get("") +def get_all_planets(): + planets_response = [] + for planet in planets: + planets_response.append(dict( + id=planet.id, + name=planet.name, + description=planet.description + )) + + return planets_response + + + + + + From fa4fef43416487d7b2647f8e9283f213d765a733 Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 18 Oct 2024 12:18:41 -0700 Subject: [PATCH 03/31] Conflict resolved --- app/routes/planets_routes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 1e1b89ddd..f0b96b923 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -13,4 +13,6 @@ def get_all_planets(): description=planet.description )) - return planets_response \ No newline at end of file + return planets_response + + From 3165fdf6d96d48933b96ebcbd6498dbd8dc42c03 Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 18 Oct 2024 12:21:12 -0700 Subject: [PATCH 04/31] Resolved conflict --- app/routes/planets_routes.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index ca9e8db6c..4bcca60f8 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,10 +1,6 @@ from flask import Blueprint from app.models.planets import planets -<<<<<<< HEAD - -======= ->>>>>>> fe0c62ab7223aeadaa935a37f34bd348e8280491 planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") @planet_bp.get("") From 763417e98f9e7b29e726a13f947323e80df83f9c Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 18 Oct 2024 12:24:28 -0700 Subject: [PATCH 05/31] Resolved conflicts --- app/routes/planets_routes.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 4bcca60f8..bd929c719 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -12,16 +12,5 @@ def get_all_planets(): name=planet.name, description=planet.description )) - -<<<<<<< HEAD + return planets_response - - - - - - -======= - return planets_response - - From 2eb21ed3fa86b64361fa3aa420b5785ccbbdd566 Mon Sep 17 00:00:00 2001 From: Liubov Dav Date: Fri, 18 Oct 2024 15:27:50 -0400 Subject: [PATCH 06/31] Working on resolve conflict --- app/routes/planets_routes.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index ca9e8db6c..5f5d03d5f 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,10 +1,6 @@ from flask import Blueprint from app.models.planets import planets -<<<<<<< HEAD - -======= ->>>>>>> fe0c62ab7223aeadaa935a37f34bd348e8280491 planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") @planet_bp.get("") @@ -16,16 +12,8 @@ def get_all_planets(): name=planet.name, description=planet.description )) - -<<<<<<< HEAD + return planets_response - - - -======= - return planets_response - - From 6945df65e64464674703c5da8280f88ddbc6a2b7 Mon Sep 17 00:00:00 2001 From: anhtran Date: Sun, 20 Oct 2024 22:40:08 -0700 Subject: [PATCH 07/31] Update get one planet and handeling errors --- app/routes/planets_routes.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index bd929c719..10e92697f 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,4 +1,4 @@ -from flask import Blueprint +from flask import Blueprint, abort, make_response from app.models.planets import planets planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") @@ -11,6 +11,32 @@ def get_all_planets(): id=planet.id, name=planet.name, description=planet.description + )) return planets_response + +@planet_bp.get("/") +def get_one_planet(planet_id): + planet = validate_palnet(planet_id) + + return { + "id": planet.id, + "name": planet.name, + "description": planet.description, + } + + +def validate_palnet(planet_id): + try: + planet_id = int(planet_id) + except: + response = {"message": f"planet {planet_id} invalid"} + abort(make_response(response, 400)) + + for planet in planets: + if planet.id == planet_id: + return planet + + response = {"message": f"planet {planet_id} not found"} + abort(make_response(response, 404)) \ No newline at end of file From 240d4900341f468f852b5adf3360b7f9923e46c3 Mon Sep 17 00:00:00 2001 From: anhtran Date: Mon, 21 Oct 2024 11:28:37 -0700 Subject: [PATCH 08/31] Created do_dict function and return in planet_id --- app/models/planets.py | 7 +++++++ app/routes/planets_routes.py | 6 +----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/models/planets.py b/app/models/planets.py index fbeb892aa..96d3836fc 100644 --- a/app/models/planets.py +++ b/app/models/planets.py @@ -4,6 +4,13 @@ def __init__(self, id, name, description): self.name = name self.description = description + def to_dict(self): + return dict( + id=self.id, + name=self.name, + description=self.description + ) + planets = [ Planet(1, "Mercury", "Smallest planet and closest to the Sun."), diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 10e92697f..bb217f0a2 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -20,11 +20,7 @@ def get_all_planets(): def get_one_planet(planet_id): planet = validate_palnet(planet_id) - return { - "id": planet.id, - "name": planet.name, - "description": planet.description, - } + return planet.to_dict(planet_id) def validate_palnet(planet_id): From ce5ab0d24f41941c32e29a953c8e7a848b095798 Mon Sep 17 00:00:00 2001 From: anhtran Date: Mon, 21 Oct 2024 12:43:06 -0700 Subject: [PATCH 09/31] adding code 200 in line 23 of routes --- app/routes/planets_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index bb217f0a2..2b2422dd3 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -20,7 +20,7 @@ def get_all_planets(): def get_one_planet(planet_id): planet = validate_palnet(planet_id) - return planet.to_dict(planet_id) + return planet.to_dict(), 200 def validate_palnet(planet_id): From c42ffb353a4cbc74ab903bfe8d21863c16850035 Mon Sep 17 00:00:00 2001 From: anhtran Date: Thu, 24 Oct 2024 20:35:01 -0700 Subject: [PATCH 10/31] Fix the typo on validate and add diameter and number_of_moons to attributes --- app/models/planets.py | 25 ++++++++++++++----------- app/routes/planets_routes.py | 9 +++++---- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/app/models/planets.py b/app/models/planets.py index 96d3836fc..554b99a78 100644 --- a/app/models/planets.py +++ b/app/models/planets.py @@ -1,24 +1,27 @@ class Planet: - def __init__(self, id, name, description): + def __init__(self, id, name, description, diameter, number_of_moons): self.id = id self.name = name self.description = description + self.diameter = diameter + self.number_of_moons = number_of_moons def to_dict(self): return dict( id=self.id, name=self.name, - description=self.description + description=self.description, + diameter=self.diameter, + number_of_moons=self.number_of_moons ) - planets = [ - Planet(1, "Mercury", "Smallest planet and closest to the Sun."), - Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere."), - Planet(3, "Earth", "The only planet known to support life."), - Planet(4, "Mars", "The Red Planet, known for its volcanoes."), - Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot."), - Planet(6, "Saturn", "Known for its stunning rings."), - Planet(7, "Uranus", "An ice giant that rotates on its side."), - Planet(8, "Neptune", "The farthest planet, known for strong winds.") + Planet(1, "Mercury", "Smallest planet and closest to the Sun.", 4879, 0), + Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere.", 12104, 0), + Planet(3, "Earth", "The only planet known to support life.", 12742, 1), + Planet(4, "Mars", "The Red Planet, known for its volcanoes.", 6779, 2), + Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot.", 139820, 79), + Planet(6, "Saturn", "Known for its stunning rings.", 116460, 83), + Planet(7, "Uranus", "An ice giant that rotates on its side.", 50724, 27), + Planet(8, "Neptune", "The farthest planet, known for strong winds.", 49244, 14) ] \ No newline at end of file diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 2b2422dd3..fc0689c82 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -10,20 +10,21 @@ def get_all_planets(): planets_response.append(dict( id=planet.id, name=planet.name, - description=planet.description - + description=planet.description, + diameter=planet.diameter, + number_of_moons=planet.number_of_moons )) return planets_response @planet_bp.get("/") def get_one_planet(planet_id): - planet = validate_palnet(planet_id) + planet = validate_planet(planet_id) return planet.to_dict(), 200 -def validate_palnet(planet_id): +def validate_planet(planet_id): try: planet_id = int(planet_id) except: From 40d6289f43bf35402c89fa33b2fbd6ab34b6a830 Mon Sep 17 00:00:00 2001 From: anhtran Date: Thu, 24 Oct 2024 20:37:27 -0700 Subject: [PATCH 11/31] Adding Pluto --- app/models/planets.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/planets.py b/app/models/planets.py index 554b99a78..623bd8b0e 100644 --- a/app/models/planets.py +++ b/app/models/planets.py @@ -23,5 +23,6 @@ def to_dict(self): Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot.", 139820, 79), Planet(6, "Saturn", "Known for its stunning rings.", 116460, 83), Planet(7, "Uranus", "An ice giant that rotates on its side.", 50724, 27), - Planet(8, "Neptune", "The farthest planet, known for strong winds.", 49244, 14) + Planet(8, "Neptune", "The farthest planet, known for strong winds.", 49244, 14), + Planet(9, "Pluto", "Dwarf planet known for its complex orbit and atmosphere.", 2376, 5) ] \ No newline at end of file From 32cb34a9e6da1a9d09f88bde7029e20820a608aa Mon Sep 17 00:00:00 2001 From: anhtran Date: Thu, 24 Oct 2024 20:47:00 -0700 Subject: [PATCH 12/31] using to_dict() in get_all_planets and update list comprehension --- app/routes/planets_routes.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index fc0689c82..5ccea278e 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -5,23 +5,14 @@ @planet_bp.get("") def get_all_planets(): - planets_response = [] - for planet in planets: - planets_response.append(dict( - id=planet.id, - name=planet.name, - description=planet.description, - diameter=planet.diameter, - number_of_moons=planet.number_of_moons - )) - + planets_response = [planet.to_dict() for planet in planets ] return planets_response @planet_bp.get("/") def get_one_planet(planet_id): planet = validate_planet(planet_id) - return planet.to_dict(), 200 + return planet.to_dict() def validate_planet(planet_id): From 7a86581a85d7e201624c7d447ee2a26e62ab9d2d Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 25 Oct 2024 11:51:47 -0700 Subject: [PATCH 13/31] create base and db. Update migration and connect db Planet model generate migration --- app/__init__.py | 6 ++ app/db.py | 6 ++ app/models/base.py | 4 ++ app/models/planets.py | 80 +++++++++++++++++-------- app/routes/planets_routes.py | 40 ++++++------- migrations/README | 1 + migrations/alembic.ini | 50 ++++++++++++++++ migrations/env.py | 113 +++++++++++++++++++++++++++++++++++ migrations/script.py.mako | 24 ++++++++ 9 files changed, 278 insertions(+), 46 deletions(-) create mode 100644 app/db.py create mode 100644 app/models/base.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 diff --git a/app/__init__.py b/app/__init__.py index c44764e9d..121eb6271 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,9 +1,15 @@ from flask import Flask +from app.db import db, migrate from app.routes.planets_routes import planet_bp 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) app.register_blueprint(planet_bp) diff --git a/app/db.py b/app/db.py new file mode 100644 index 000000000..6266713f2 --- /dev/null +++ b/app/db.py @@ -0,0 +1,6 @@ +from flask_sqlalchemy import SQLAlchemy +from flask_migrate import Migrate +from .models.base import Base + +db = SQLAlchemy(model_class=Base) +migrate = Migrate() diff --git a/app/models/base.py b/app/models/base.py new file mode 100644 index 000000000..227841686 --- /dev/null +++ b/app/models/base.py @@ -0,0 +1,4 @@ +from sqlalchemy.orm import DeclarativeBase + +class Base(DeclarativeBase): + pass \ No newline at end of file diff --git a/app/models/planets.py b/app/models/planets.py index 623bd8b0e..b444a533d 100644 --- a/app/models/planets.py +++ b/app/models/planets.py @@ -1,28 +1,56 @@ -class Planet: - def __init__(self, id, name, description, diameter, number_of_moons): - self.id = id - self.name = name - self.description = description - self.diameter = diameter - self.number_of_moons = number_of_moons +from sqlalchemy.orm import Mapped, mapped_column +from app.db import db + +class Planet(db.Model): + id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) + name: Mapped[str] + description: Mapped[str] + diameter: Mapped[int] + number_of_moons: Mapped[int] + +# def to_dict(self): +# return dict( +# id=self.id, +# name=self.name, +# description=self.description, +# diameter=self.diameter, +# number_of_moons=self.number_of_moons +# ) + + + + + + + + + + +# class Planet: +# def __init__(self, id, name, description, diameter, number_of_moons): +# self.id = id +# self.name = name +# self.description = description +# self.diameter = diameter +# self.number_of_moons = number_of_moons - def to_dict(self): - return dict( - id=self.id, - name=self.name, - description=self.description, - diameter=self.diameter, - number_of_moons=self.number_of_moons - ) +# def to_dict(self): +# return dict( +# id=self.id, +# name=self.name, +# description=self.description, +# diameter=self.diameter, +# number_of_moons=self.number_of_moons +# ) -planets = [ - Planet(1, "Mercury", "Smallest planet and closest to the Sun.", 4879, 0), - Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere.", 12104, 0), - Planet(3, "Earth", "The only planet known to support life.", 12742, 1), - Planet(4, "Mars", "The Red Planet, known for its volcanoes.", 6779, 2), - Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot.", 139820, 79), - Planet(6, "Saturn", "Known for its stunning rings.", 116460, 83), - Planet(7, "Uranus", "An ice giant that rotates on its side.", 50724, 27), - Planet(8, "Neptune", "The farthest planet, known for strong winds.", 49244, 14), - Planet(9, "Pluto", "Dwarf planet known for its complex orbit and atmosphere.", 2376, 5) -] \ No newline at end of file +# planets = [ +# Planet(1, "Mercury", "Smallest planet and closest to the Sun.", 4879, 0), +# Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere.", 12104, 0), +# Planet(3, "Earth", "The only planet known to support life.", 12742, 1), +# Planet(4, "Mars", "The Red Planet, known for its volcanoes.", 6779, 2), +# Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot.", 139820, 79), +# Planet(6, "Saturn", "Known for its stunning rings.", 116460, 83), +# Planet(7, "Uranus", "An ice giant that rotates on its side.", 50724, 27), +# Planet(8, "Neptune", "The farthest planet, known for strong winds.", 49244, 14), +# Planet(9, "Pluto", "Dwarf planet known for its complex orbit and atmosphere.", 2376, 5) +# ] \ No newline at end of file diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 5ccea278e..ff68aacd3 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,30 +1,30 @@ from flask import Blueprint, abort, make_response -from app.models.planets import planets +# from app.models.planets import planets planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") -@planet_bp.get("") -def get_all_planets(): - planets_response = [planet.to_dict() for planet in planets ] - return planets_response +# @planet_bp.get("") +# def get_all_planets(): +# planets_response = [planet.to_dict() for planet in planets ] +# return planets_response -@planet_bp.get("/") -def get_one_planet(planet_id): - planet = validate_planet(planet_id) +# @planet_bp.get("/") +# def get_one_planet(planet_id): +# planet = validate_planet(planet_id) - return planet.to_dict() +# return planet.to_dict() -def validate_planet(planet_id): - try: - planet_id = int(planet_id) - except: - response = {"message": f"planet {planet_id} invalid"} - abort(make_response(response, 400)) +# def validate_planet(planet_id): +# try: +# planet_id = int(planet_id) +# except: +# response = {"message": f"planet {planet_id} invalid"} +# abort(make_response(response, 400)) - for planet in planets: - if planet.id == planet_id: - return planet +# for planet in planets: +# if planet.id == planet_id: +# return planet - response = {"message": f"planet {planet_id} not found"} - abort(make_response(response, 404)) \ No newline at end of file +# response = {"message": f"planet {planet_id} not found"} +# abort(make_response(response, 404)) \ No newline at end of file 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..4c9709271 --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,113 @@ +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, AttributeError): + # 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.') + + conf_args = current_app.extensions['migrate'].configure_args + if conf_args.get("process_revision_directives") is None: + conf_args["process_revision_directives"] = process_revision_directives + + connectable = get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=get_metadata(), + **conf_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"} From 52c0b7d9d5ea6e3fd3560c80477482a6c65b66c1 Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 25 Oct 2024 14:40:53 -0700 Subject: [PATCH 14/31] create POST request for Planet table and add 3 planets --- app/__init__.py | 1 + app/models/planets.py | 16 ++++----- app/routes/planets_routes.py | 24 +++++++++++-- .../b0952940e36d_adds_planet_model.py | 36 +++++++++++++++++++ 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 migrations/versions/b0952940e36d_adds_planet_model.py diff --git a/app/__init__.py b/app/__init__.py index 121eb6271..48082c0d7 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,5 +1,6 @@ from flask import Flask from app.db import db, migrate +from app.models import planets from app.routes.planets_routes import planet_bp diff --git a/app/models/planets.py b/app/models/planets.py index b444a533d..d20f8a4c1 100644 --- a/app/models/planets.py +++ b/app/models/planets.py @@ -8,14 +8,14 @@ class Planet(db.Model): diameter: Mapped[int] number_of_moons: Mapped[int] -# def to_dict(self): -# return dict( -# id=self.id, -# name=self.name, -# description=self.description, -# diameter=self.diameter, -# number_of_moons=self.number_of_moons -# ) + def to_dict(self): + return dict( + id=self.id, + name=self.name, + description=self.description, + diameter=self.diameter, + number_of_moons=self.number_of_moons + ) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index ff68aacd3..e5e662d82 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,8 +1,28 @@ -from flask import Blueprint, abort, make_response -# from app.models.planets import planets +from flask import Blueprint, abort, make_response, request +from app.models.planets import Planet +from ..db import db planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") +@planet_bp.post("") +def create_planet(): + request_body = request.get_json() + id = request_body["id"] + name = request_body["name"] + description = request_body["description"] + diameter = request_body["diameter"] + number_of_moons = request_body["number_of_moons"] + + new_planet = Planet(id=id, name=name, description=description, diameter=diameter, number_of_moons=number_of_moons) + db.session.add(new_planet) + db.session.commit() + + response = new_planet.to_dict() + return response, 201 + + + + # @planet_bp.get("") # def get_all_planets(): # planets_response = [planet.to_dict() for planet in planets ] diff --git a/migrations/versions/b0952940e36d_adds_planet_model.py b/migrations/versions/b0952940e36d_adds_planet_model.py new file mode 100644 index 000000000..7be24ffe3 --- /dev/null +++ b/migrations/versions/b0952940e36d_adds_planet_model.py @@ -0,0 +1,36 @@ +"""adds +Planet model + +Revision ID: b0952940e36d +Revises: +Create Date: 2024-10-25 14:26:03.446066 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'b0952940e36d' +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=False), + sa.Column('description', sa.String(), nullable=False), + sa.Column('diameter', sa.Integer(), nullable=False), + sa.Column('number_of_moons', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('planet') + # ### end Alembic commands ### From af930b64c8ab817103ce928d5d0eb07db02a33b7 Mon Sep 17 00:00:00 2001 From: anhtran Date: Fri, 25 Oct 2024 15:11:48 -0700 Subject: [PATCH 15/31] Create get method for all planets --- app/routes/planets_routes.py | 47 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index e5e662d82..4888ad756 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -20,6 +20,31 @@ def create_planet(): response = new_planet.to_dict() return response, 201 +@planet_bp.get("") +def get_all_planets(): + query = db.select(Planet).order_by(Planet.id) + planets = db.session.scalars(query) + + response_body = [planet.to_dict() for planet in planets] + + return response_body, 200 + + + + +# def validate_planet(planet_id): +# try: +# planet_id = int(planet_id) +# except: +# response = {"message": f"planet {planet_id} invalid"} +# abort(make_response(response, 400)) + +# for planet in planets: +# if planet.id == planet_id: +# return planet + +# response = {"message": f"planet {planet_id} not found"} +# abort(make_response(response, 404)) @@ -35,16 +60,16 @@ def create_planet(): # return planet.to_dict() -# def validate_planet(planet_id): -# try: -# planet_id = int(planet_id) -# except: -# response = {"message": f"planet {planet_id} invalid"} -# abort(make_response(response, 400)) +def validate_planet(planet_id): + try: + planet_id = int(planet_id) + except: + response = {"message": f"planet {planet_id} invalid"} + abort(make_response(response, 400)) -# for planet in planets: -# if planet.id == planet_id: -# return planet + for planet in planets: + if planet.id == planet_id: + return planet -# response = {"message": f"planet {planet_id} not found"} -# abort(make_response(response, 404)) \ No newline at end of file + response = {"message": f"planet {planet_id} not found"} + abort(make_response(response, 404)) \ No newline at end of file From 13ff70fb9d44af579163d7991f89ee7f62a6ad02 Mon Sep 17 00:00:00 2001 From: Liubov Dav Date: Fri, 25 Oct 2024 15:17:27 -0700 Subject: [PATCH 16/31] Nothing changed --- app/routes/planets_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index e5e662d82..1446cf518 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -19,7 +19,7 @@ def create_planet(): response = new_planet.to_dict() return response, 201 - + From 1a2fd507905ed4132f0fa933ea829a046b791b5b Mon Sep 17 00:00:00 2001 From: anhtran Date: Tue, 29 Oct 2024 11:05:46 -0700 Subject: [PATCH 17/31] remove id in POST method --- app/routes/planets_routes.py | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index f63897ee9..c46bb558a 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -7,13 +7,13 @@ @planet_bp.post("") def create_planet(): request_body = request.get_json() - id = request_body["id"] + name = request_body["name"] description = request_body["description"] diameter = request_body["diameter"] number_of_moons = request_body["number_of_moons"] - new_planet = Planet(id=id, name=name, description=description, diameter=diameter, number_of_moons=number_of_moons) + new_planet = Planet(name=name, description=description, diameter=diameter, number_of_moons=number_of_moons) db.session.add(new_planet) db.session.commit() @@ -32,29 +32,3 @@ def get_all_planets(): -# def validate_planet(planet_id): -# try: -# planet_id = int(planet_id) -# except: -# response = {"message": f"planet {planet_id} invalid"} -# abort(make_response(response, 400)) - -# for planet in planets: -# if planet.id == planet_id: -# return planet - -# response = {"message": f"planet {planet_id} not found"} -# abort(make_response(response, 404)) - - - -# @planet_bp.get("") -# def get_all_planets(): -# planets_response = [planet.to_dict() for planet in planets ] -# return planets_response - -# @planet_bp.get("/") -# def get_one_planet(planet_id): -# planet = validate_planet(planet_id) - -# return planet.to_dict() From ab90026f5b6fe77c57c7c308ef998b5463c47f5a Mon Sep 17 00:00:00 2001 From: Liubov Dav Date: Tue, 29 Oct 2024 12:06:36 -0700 Subject: [PATCH 18/31] create get single planet, update and deleted --- app/routes/planets_routes.py | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index c46bb558a..1f80d312b 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,4 +1,4 @@ -from flask import Blueprint, abort, make_response, request +from flask import Blueprint, abort, make_response, request, Response from app.models.planets import Planet from ..db import db @@ -28,7 +28,57 @@ def get_all_planets(): response_body = [planet.to_dict() for planet in planets] return response_body, 200 - + +@planet_bp.get("/") +def get_single_planet(planet_id): + planet = validate_planet(planet_id) + + return planet.to_dict() + + +def validate_planet(planet_id): + try: + planet_id = int(planet_id) + except: + response = { + "message": f"Planet {planet_id} invalid" + } + abort(make_response(response, 400)) + + query = db.select(Planet).where(Planet.id == planet_id) + planet = db.session.scalar(query) + + if not planet: + response = { + "message": f"Planet {planet_id} not found" + } + abort(make_response(response, 404)) + + return planet + +@planet_bp.put("/") +def update_single_planet(planet_id): + planet = validate_planet(planet_id) + request_body = request.get_json() + + planet.name = request_body["name"] + planet.description = request_body["description"] + planet.diameter = request_body["diameter"] + planet.number_of_moons = request_body["number_of_moons"] + + db.session.commit() + + return Response(status=204, mimetype='application/json') + +@planet_bp.delete("/") +def delete_single_planet(planet_id): + planet = validate_planet(planet_id) + + db.session.delete(planet) + db.session.commit() + + return Response(status=204, mimetype='application/json') + From 91a07dbf54f459fe8d6bd627f3121f60af7e51d8 Mon Sep 17 00:00:00 2001 From: anhtran Date: Tue, 29 Oct 2024 12:07:50 -0700 Subject: [PATCH 19/31] there were nothing change --- app/routes/planets_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index c46bb558a..05ef3fee4 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -7,7 +7,7 @@ @planet_bp.post("") def create_planet(): request_body = request.get_json() - + name = request_body["name"] description = request_body["description"] diameter = request_body["diameter"] From c28922ef1a60cb7d8f31e2fbb7959fde13b61a07 Mon Sep 17 00:00:00 2001 From: anhtran Date: Wed, 30 Oct 2024 11:08:52 -0700 Subject: [PATCH 20/31] Update get all planets with add query params for description and number of moons --- app/routes/planets_routes.py | 57 +++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index abc9c8c9d..ecd86c046 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -22,12 +22,23 @@ def create_planet(): @planet_bp.get("") def get_all_planets(): - query = db.select(Planet).order_by(Planet.id) + query = db.select(Planet) + + description_param = request.args.get["description"] + if description_param: + query = query.where(Planet.description.ilike(f"%{description_param}%")) + + number_of_moons_param = request.args.get["number_of_moons"] + if number_of_moons_param: + query = query.where(Planet.number_of_moons.ilike(f"%{number_of_moons_param}%")) + + query = query.order_by(Planet.id) + planets = db.session.scalars(query) response_body = [planet.to_dict() for planet in planets] - return response_body, 200 + return response_body @planet_bp.get("/") def get_single_planet(planet_id): @@ -35,27 +46,6 @@ def get_single_planet(planet_id): return planet.to_dict() - -def validate_planet(planet_id): - try: - planet_id = int(planet_id) - except: - response = { - "message": f"Planet {planet_id} invalid" - } - abort(make_response(response, 400)) - - query = db.select(Planet).where(Planet.id == planet_id) - planet = db.session.scalar(query) - - if not planet: - response = { - "message": f"Planet {planet_id} not found" - } - abort(make_response(response, 404)) - - return planet - @planet_bp.put("/") def update_single_planet(planet_id): planet = validate_planet(planet_id) @@ -79,6 +69,25 @@ def delete_single_planet(planet_id): return Response(status=204, mimetype='application/json') - + +def validate_planet(planet_id): + try: + planet_id = int(planet_id) + except: + response = { + "message": f"Planet {planet_id} invalid" + } + abort(make_response(response, 400)) + + query = db.select(Planet).where(Planet.id == planet_id) + planet = db.session.scalar(query) + + if not planet: + response = { + "message": f"Planet {planet_id} not found" + } + abort(make_response(response, 404)) + + return planet From 461683d6f429d30e268fe3ca157d69a34abb167e Mon Sep 17 00:00:00 2001 From: anhtran Date: Wed, 30 Oct 2024 11:11:40 -0700 Subject: [PATCH 21/31] change the .get[] to .get() --- app/routes/planets_routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index ecd86c046..a18d62962 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -24,11 +24,11 @@ def create_planet(): def get_all_planets(): query = db.select(Planet) - description_param = request.args.get["description"] + description_param = request.args.get("description") if description_param: query = query.where(Planet.description.ilike(f"%{description_param}%")) - number_of_moons_param = request.args.get["number_of_moons"] + number_of_moons_param = request.args.get("number_of_moons") if number_of_moons_param: query = query.where(Planet.number_of_moons.ilike(f"%{number_of_moons_param}%")) From 65162335e1df3cad3a157f9366e57de09131a52a Mon Sep 17 00:00:00 2001 From: anhtran Date: Wed, 30 Oct 2024 11:23:08 -0700 Subject: [PATCH 22/31] Update the query for number_of_moons_param that take record set for number_of_moons --- app/routes/planets_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index a18d62962..1264676f0 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -30,7 +30,7 @@ def get_all_planets(): number_of_moons_param = request.args.get("number_of_moons") if number_of_moons_param: - query = query.where(Planet.number_of_moons.ilike(f"%{number_of_moons_param}%")) + query = query.where(Planet.number_of_moons == number_of_moons_param) query = query.order_by(Planet.id) From 7bccff08f749bfe7ddeef102450d186282958352 Mon Sep 17 00:00:00 2001 From: anhtran Date: Thu, 31 Oct 2024 12:21:19 -0700 Subject: [PATCH 23/31] Create tests environment and update all unit tests and conftest.py --- app/__init__.py | 9 ++++-- app/routes/planets_routes.py | 2 +- requirements.txt | 9 ++++-- tests/__init__.py | 0 tests/conftest.py | 40 +++++++++++++++++++++++ tests/test_planets_routes.py | 63 ++++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/test_planets_routes.py diff --git a/app/__init__.py b/app/__init__.py index 48082c0d7..85e97f383 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -2,13 +2,18 @@ from app.db import db, migrate from app.models import planets from app.routes.planets_routes import planet_bp +import os -def create_app(test_config=None): +def create_app(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' + app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('SQLALCHEMY_DATABASE_URI') + if config: + app.config.update(config) + + db.init_app(app) migrate.init_app(app, db) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 1264676f0..d7d3c2552 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -31,7 +31,7 @@ def get_all_planets(): number_of_moons_param = request.args.get("number_of_moons") if number_of_moons_param: query = query.where(Planet.number_of_moons == number_of_moons_param) - + query = query.order_by(Planet.id) planets = db.session.scalars(query) diff --git a/requirements.txt b/requirements.txt index 24c7e56f8..b87519d9e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,21 @@ alembic==1.13.1 autopep8==1.5.5 -blinker==1.7 +blinker==1.7.0 certifi==2020.12.5 chardet==4.0.0 click==8.1.7 +coverage==7.6.3 Flask==3.0.2 Flask-Migrate==4.0.5 Flask-SQLAlchemy==3.1.1 idna==2.10 +iniconfig==2.0.0 itsdangerous==2.1.2 Jinja2==3.1.3 Mako==1.1.4 MarkupSafe==2.1.5 +packaging==24.1 +pluggy==1.5.0 psycopg2-binary==2.9.9 pycodestyle==2.6.0 pytest==8.0.0 @@ -23,5 +27,6 @@ requests==2.25.1 six==1.15.0 SQLAlchemy==2.0.25 toml==0.10.2 +typing_extensions==4.12.2 urllib3==1.26.4 -Werkzeug==3.0.1 \ No newline at end of file +Werkzeug==3.0.1 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..1562caf4e --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,40 @@ +import pytest +from app import create_app +from app.db import db +from flask.signals import request_finished +from dotenv import load_dotenv +import os +from app.models.planets import Planet + +load_dotenv() + +@pytest.fixture +def app(): + test_config = { + "TESTING": True, + "SQLALCHEMY_DATABASE_URI": os.environ.get('SQLALCHEMY_DATABASE_URI') + } + app = create_app(test_config) + + @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() + +@pytest.fixture +def two_save_planets(app): + planet_1 = Planet(name="Pluto", description="Dwarf planet known for its complex orbit and atmosphere.", diameter=2376, number_of_moons=5) + planet_2 = Planet(name="Neptune", description="The farthest planet, known for strong winds", diameter=49244, number_of_moons=14) + + db.session.add_all([planet_1, planet_2]) + db.session.commit() \ No newline at end of file diff --git a/tests/test_planets_routes.py b/tests/test_planets_routes.py new file mode 100644 index 000000000..94d8efa00 --- /dev/null +++ b/tests/test_planets_routes.py @@ -0,0 +1,63 @@ +def test_get_all_planets_with_no_records(client): + #Act + response = client.get("/planets") + response_body = response.get_json() + + #Assert + assert response.status_code == 200 + assert response_body == [] + +def test_get_one_planet(client, two_save_planets): + #Act + response = client.get("/planets/1") + response_body = response.get_json() + + #Assert + assert response.status_code == 200 + assert response_body == { + "id": 1, + "name": "Pluto", + "description": "Dwarf planet known for its complex orbit and atmosphere.", + "diameter": 2376, + "number_of_moons": 5 + } + +def test_create_one_planet_in_empty_database(client): + #Act + response = client.post("/planets", json={ + "name": "Pluto", + "description": "Dwarf planet known for its complex orbit and atmosphere.", + "diameter": 2376, + "number_of_moons": 5 + }) + + response_body = response.get_json() + + assert response.status_code == 201 + assert response_body == { + "id": 1, + "name": "Pluto", + "description": "Dwarf planet known for its complex orbit and atmosphere.", + "diameter": 2376, + "number_of_moons": 5 + } + +def test_create_one_planet_already_in_database(client, two_save_planets): + #Act + response = client.post("/planets", json={ + "name": "Pluto", + "description": "Dwarf planet known for its complex orbit and atmosphere.", + "diameter": 2376, + "number_of_moons": 5 + }) + + response_body = response.get_json() + + assert response.status_code == 201 + assert response_body == { + "id": 3, + "name": "Pluto", + "description": "Dwarf planet known for its complex orbit and atmosphere.", + "diameter": 2376, + "number_of_moons": 5 + } \ No newline at end of file From 026ee7637642e17611e097e4e6fc4c594de897fc Mon Sep 17 00:00:00 2001 From: anhtran Date: Thu, 31 Oct 2024 13:33:16 -0700 Subject: [PATCH 24/31] changing the name for test in database variable name in .env and conftest.py --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1562caf4e..581947391 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,7 +12,7 @@ def app(): test_config = { "TESTING": True, - "SQLALCHEMY_DATABASE_URI": os.environ.get('SQLALCHEMY_DATABASE_URI') + "SQLALCHEMY_DATABASE_URI": os.environ.get('SQLALCHEMY_TEST_DATABASE_URI') } app = create_app(test_config) From ae2d925ac027a3f80729efb96be6ed4c1d760c29 Mon Sep 17 00:00:00 2001 From: anhtran Date: Sun, 3 Nov 2024 19:43:29 -0800 Subject: [PATCH 25/31] change the name of model file to macth the name of class. Remove the planets module in __init__.py since we have Planet model imported from the routes --- app/__init__.py | 1 - app/models/{planets.py => planet.py} | 0 app/routes/planets_routes.py | 2 +- tests/conftest.py | 6 ++++-- 4 files changed, 5 insertions(+), 4 deletions(-) rename app/models/{planets.py => planet.py} (100%) diff --git a/app/__init__.py b/app/__init__.py index 85e97f383..8381cfaed 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,6 +1,5 @@ from flask import Flask from app.db import db, migrate -from app.models import planets from app.routes.planets_routes import planet_bp import os diff --git a/app/models/planets.py b/app/models/planet.py similarity index 100% rename from app/models/planets.py rename to app/models/planet.py diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index d7d3c2552..c70dd6bc2 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -1,5 +1,5 @@ from flask import Blueprint, abort, make_response, request, Response -from app.models.planets import Planet +from app.models.planet import Planet from ..db import db planet_bp = Blueprint("planet_bp", __name__, url_prefix="/planets") diff --git a/tests/conftest.py b/tests/conftest.py index 581947391..6d5545686 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from flask.signals import request_finished from dotenv import load_dotenv import os -from app.models.planets import Planet +from app.models.planet import Planet load_dotenv() @@ -37,4 +37,6 @@ def two_save_planets(app): planet_2 = Planet(name="Neptune", description="The farthest planet, known for strong winds", diameter=49244, number_of_moons=14) db.session.add_all([planet_1, planet_2]) - db.session.commit() \ No newline at end of file + db.session.commit() + +# \ No newline at end of file From c9a39dfa8a540426ddbdcf4524ddd58e47f5cdd4 Mon Sep 17 00:00:00 2001 From: anhtran Date: Sun, 3 Nov 2024 19:54:22 -0800 Subject: [PATCH 26/31] change name of column to diameter_in-km --- app/models/planet.py | 4 +-- app/routes/planets_routes.py | 6 ++-- ...3a935fd2e4c9_adding__in_km_for_diameter.py | 34 +++++++++++++++++++ tests/conftest.py | 4 +-- tests/test_planets_routes.py | 10 +++--- 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py diff --git a/app/models/planet.py b/app/models/planet.py index d20f8a4c1..837cfaa90 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -5,7 +5,7 @@ class Planet(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] description: Mapped[str] - diameter: Mapped[int] + diameter_in_km: Mapped[int] number_of_moons: Mapped[int] def to_dict(self): @@ -13,7 +13,7 @@ def to_dict(self): id=self.id, name=self.name, description=self.description, - diameter=self.diameter, + diameter_in_km=self.diameter_in_km, number_of_moons=self.number_of_moons ) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index c70dd6bc2..90c6045b1 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -10,10 +10,10 @@ def create_planet(): name = request_body["name"] description = request_body["description"] - diameter = request_body["diameter"] + diameter_in_km = request_body["diameter"] number_of_moons = request_body["number_of_moons"] - new_planet = Planet(name=name, description=description, diameter=diameter, number_of_moons=number_of_moons) + new_planet = Planet(name=name, description=description, diameter_in_km=diameter_in_km, number_of_moons=number_of_moons) db.session.add(new_planet) db.session.commit() @@ -53,7 +53,7 @@ def update_single_planet(planet_id): planet.name = request_body["name"] planet.description = request_body["description"] - planet.diameter = request_body["diameter"] + planet.diameter_in_km = request_body["diameter_in_km"] planet.number_of_moons = request_body["number_of_moons"] db.session.commit() diff --git a/migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py b/migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py new file mode 100644 index 000000000..e4e988c4a --- /dev/null +++ b/migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py @@ -0,0 +1,34 @@ +"""adding _in_km for diameter + +Revision ID: 3a935fd2e4c9 +Revises: b0952940e36d +Create Date: 2024-11-03 19:46:38.191252 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3a935fd2e4c9' +down_revision = 'b0952940e36d' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('planet', schema=None) as batch_op: + batch_op.add_column(sa.Column('diameter_in_km', sa.Integer(), nullable=False)) + batch_op.drop_column('diameter') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('planet', schema=None) as batch_op: + batch_op.add_column(sa.Column('diameter', sa.INTEGER(), autoincrement=False, nullable=False)) + batch_op.drop_column('diameter_in_km') + + # ### end Alembic commands ### diff --git a/tests/conftest.py b/tests/conftest.py index 6d5545686..27870420f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,8 +33,8 @@ def client(app): @pytest.fixture def two_save_planets(app): - planet_1 = Planet(name="Pluto", description="Dwarf planet known for its complex orbit and atmosphere.", diameter=2376, number_of_moons=5) - planet_2 = Planet(name="Neptune", description="The farthest planet, known for strong winds", diameter=49244, number_of_moons=14) + planet_1 = Planet(name="Pluto", description="Dwarf planet known for its complex orbit and atmosphere.", diameter_in_km=2376, number_of_moons=5) + planet_2 = Planet(name="Neptune", description="The farthest planet, known for strong winds", diameter_in_km=49244, number_of_moons=14) db.session.add_all([planet_1, planet_2]) db.session.commit() diff --git a/tests/test_planets_routes.py b/tests/test_planets_routes.py index 94d8efa00..6e6412805 100644 --- a/tests/test_planets_routes.py +++ b/tests/test_planets_routes.py @@ -18,7 +18,7 @@ def test_get_one_planet(client, two_save_planets): "id": 1, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter": 2376, + "diameter_in_km": 2376, "number_of_moons": 5 } @@ -27,7 +27,7 @@ def test_create_one_planet_in_empty_database(client): response = client.post("/planets", json={ "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter": 2376, + "diameter_in_km": 2376, "number_of_moons": 5 }) @@ -38,7 +38,7 @@ def test_create_one_planet_in_empty_database(client): "id": 1, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter": 2376, + "diameter_in_km": 2376, "number_of_moons": 5 } @@ -47,7 +47,7 @@ def test_create_one_planet_already_in_database(client, two_save_planets): response = client.post("/planets", json={ "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter": 2376, + "diameter_in_km": 2376, "number_of_moons": 5 }) @@ -58,6 +58,6 @@ def test_create_one_planet_already_in_database(client, two_save_planets): "id": 3, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter": 2376, + "diameter_in_km": 2376, "number_of_moons": 5 } \ No newline at end of file From 50f37485b7baa8977ebf2df9d2ae84420185c27b Mon Sep 17 00:00:00 2001 From: anhtran Date: Sun, 3 Nov 2024 20:17:00 -0800 Subject: [PATCH 27/31] return 2 planets in two_saved_planets --- app/models/planet.py | 34 +--------------------------------- app/routes/planets_routes.py | 2 +- tests/conftest.py | 2 +- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/app/models/planet.py b/app/models/planet.py index 837cfaa90..89260e32c 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -5,7 +5,7 @@ class Planet(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] description: Mapped[str] - diameter_in_km: Mapped[int] + diameter_in_km: Mapped[int] number_of_moons: Mapped[int] def to_dict(self): @@ -22,35 +22,3 @@ def to_dict(self): - - - - -# class Planet: -# def __init__(self, id, name, description, diameter, number_of_moons): -# self.id = id -# self.name = name -# self.description = description -# self.diameter = diameter -# self.number_of_moons = number_of_moons - -# def to_dict(self): -# return dict( -# id=self.id, -# name=self.name, -# description=self.description, -# diameter=self.diameter, -# number_of_moons=self.number_of_moons -# ) - -# planets = [ -# Planet(1, "Mercury", "Smallest planet and closest to the Sun.", 4879, 0), -# Planet(2, "Venus", "Similar to Earth but with a thick, toxic atmosphere.", 12104, 0), -# Planet(3, "Earth", "The only planet known to support life.", 12742, 1), -# Planet(4, "Mars", "The Red Planet, known for its volcanoes.", 6779, 2), -# Planet(5, "Jupiter", "The largest planet, famous for its Great Red Spot.", 139820, 79), -# Planet(6, "Saturn", "Known for its stunning rings.", 116460, 83), -# Planet(7, "Uranus", "An ice giant that rotates on its side.", 50724, 27), -# Planet(8, "Neptune", "The farthest planet, known for strong winds.", 49244, 14), -# Planet(9, "Pluto", "Dwarf planet known for its complex orbit and atmosphere.", 2376, 5) -# ] \ No newline at end of file diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 90c6045b1..39319c46e 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -10,7 +10,7 @@ def create_planet(): name = request_body["name"] description = request_body["description"] - diameter_in_km = request_body["diameter"] + diameter_in_km = request_body["diameter_in_km"] number_of_moons = request_body["number_of_moons"] new_planet = Planet(name=name, description=description, diameter_in_km=diameter_in_km, number_of_moons=number_of_moons) diff --git a/tests/conftest.py b/tests/conftest.py index 27870420f..b655ab587 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -39,4 +39,4 @@ def two_save_planets(app): db.session.add_all([planet_1, planet_2]) db.session.commit() -# \ No newline at end of file + return [planet_1, planet_2] From 3020969deb7b32456588394ca2db60d49618c96e Mon Sep 17 00:00:00 2001 From: anhtran Date: Sun, 3 Nov 2024 20:19:42 -0800 Subject: [PATCH 28/31] update tests id after return the values of two planets --- tests/test_planets_routes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_planets_routes.py b/tests/test_planets_routes.py index 6e6412805..e888d4d2a 100644 --- a/tests/test_planets_routes.py +++ b/tests/test_planets_routes.py @@ -9,13 +9,13 @@ def test_get_all_planets_with_no_records(client): def test_get_one_planet(client, two_save_planets): #Act - response = client.get("/planets/1") + response = client.get(f"/planets/{two_save_planets[0].id}") response_body = response.get_json() #Assert assert response.status_code == 200 assert response_body == { - "id": 1, + "id": two_save_planets[0].id, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", "diameter_in_km": 2376, From 343eb550ff0020f446b67efb4db04cfd18acbc0d Mon Sep 17 00:00:00 2001 From: anhtran Date: Mon, 4 Nov 2024 11:08:40 -0800 Subject: [PATCH 29/31] change the name of clumn diameter --- app/models/planet.py | 4 ++-- app/routes/planets_routes.py | 6 +++--- tests/conftest.py | 4 ++-- tests/test_planets_routes.py | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/models/planet.py b/app/models/planet.py index 89260e32c..b73100275 100644 --- a/app/models/planet.py +++ b/app/models/planet.py @@ -5,7 +5,7 @@ class Planet(db.Model): id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) name: Mapped[str] description: Mapped[str] - diameter_in_km: Mapped[int] + diameter: Mapped[int] #in kilometers number_of_moons: Mapped[int] def to_dict(self): @@ -13,7 +13,7 @@ def to_dict(self): id=self.id, name=self.name, description=self.description, - diameter_in_km=self.diameter_in_km, + diameter=self.diameter, number_of_moons=self.number_of_moons ) diff --git a/app/routes/planets_routes.py b/app/routes/planets_routes.py index 39319c46e..c70dd6bc2 100644 --- a/app/routes/planets_routes.py +++ b/app/routes/planets_routes.py @@ -10,10 +10,10 @@ def create_planet(): name = request_body["name"] description = request_body["description"] - diameter_in_km = request_body["diameter_in_km"] + diameter = request_body["diameter"] number_of_moons = request_body["number_of_moons"] - new_planet = Planet(name=name, description=description, diameter_in_km=diameter_in_km, number_of_moons=number_of_moons) + new_planet = Planet(name=name, description=description, diameter=diameter, number_of_moons=number_of_moons) db.session.add(new_planet) db.session.commit() @@ -53,7 +53,7 @@ def update_single_planet(planet_id): planet.name = request_body["name"] planet.description = request_body["description"] - planet.diameter_in_km = request_body["diameter_in_km"] + planet.diameter = request_body["diameter"] planet.number_of_moons = request_body["number_of_moons"] db.session.commit() diff --git a/tests/conftest.py b/tests/conftest.py index b655ab587..367530c02 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,8 +33,8 @@ def client(app): @pytest.fixture def two_save_planets(app): - planet_1 = Planet(name="Pluto", description="Dwarf planet known for its complex orbit and atmosphere.", diameter_in_km=2376, number_of_moons=5) - planet_2 = Planet(name="Neptune", description="The farthest planet, known for strong winds", diameter_in_km=49244, number_of_moons=14) + planet_1 = Planet(name="Pluto", description="Dwarf planet known for its complex orbit and atmosphere.", diameter=2376, number_of_moons=5) + planet_2 = Planet(name="Neptune", description="The farthest planet, known for strong winds", diameter=49244, number_of_moons=14) db.session.add_all([planet_1, planet_2]) db.session.commit() diff --git a/tests/test_planets_routes.py b/tests/test_planets_routes.py index e888d4d2a..a368e092b 100644 --- a/tests/test_planets_routes.py +++ b/tests/test_planets_routes.py @@ -18,7 +18,7 @@ def test_get_one_planet(client, two_save_planets): "id": two_save_planets[0].id, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter_in_km": 2376, + "diameter": 2376, "number_of_moons": 5 } @@ -27,7 +27,7 @@ def test_create_one_planet_in_empty_database(client): response = client.post("/planets", json={ "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter_in_km": 2376, + "diameter": 2376, "number_of_moons": 5 }) @@ -38,7 +38,7 @@ def test_create_one_planet_in_empty_database(client): "id": 1, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter_in_km": 2376, + "diameter": 2376, "number_of_moons": 5 } @@ -47,7 +47,7 @@ def test_create_one_planet_already_in_database(client, two_save_planets): response = client.post("/planets", json={ "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter_in_km": 2376, + "diameter": 2376, "number_of_moons": 5 }) @@ -58,6 +58,6 @@ def test_create_one_planet_already_in_database(client, two_save_planets): "id": 3, "name": "Pluto", "description": "Dwarf planet known for its complex orbit and atmosphere.", - "diameter_in_km": 2376, + "diameter": 2376, "number_of_moons": 5 } \ No newline at end of file From 062e52ebe6b032bdefe98cdd01b775ad3cc44a53 Mon Sep 17 00:00:00 2001 From: anhtran Date: Wed, 6 Nov 2024 10:53:07 -0800 Subject: [PATCH 30/31] delete migration folder --- ...3a935fd2e4c9_adding__in_km_for_diameter.py | 34 ------------------- 1 file changed, 34 deletions(-) delete mode 100644 migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py diff --git a/migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py b/migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py deleted file mode 100644 index e4e988c4a..000000000 --- a/migrations/versions/3a935fd2e4c9_adding__in_km_for_diameter.py +++ /dev/null @@ -1,34 +0,0 @@ -"""adding _in_km for diameter - -Revision ID: 3a935fd2e4c9 -Revises: b0952940e36d -Create Date: 2024-11-03 19:46:38.191252 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = '3a935fd2e4c9' -down_revision = 'b0952940e36d' -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('planet', schema=None) as batch_op: - batch_op.add_column(sa.Column('diameter_in_km', sa.Integer(), nullable=False)) - batch_op.drop_column('diameter') - - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - with op.batch_alter_table('planet', schema=None) as batch_op: - batch_op.add_column(sa.Column('diameter', sa.INTEGER(), autoincrement=False, nullable=False)) - batch_op.drop_column('diameter_in_km') - - # ### end Alembic commands ### From 6bfb32b7f564575e94a70d310ef492c61e4acdd6 Mon Sep 17 00:00:00 2001 From: anhtran Date: Wed, 6 Nov 2024 10:58:04 -0800 Subject: [PATCH 31/31] install gunicorn and add to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index b87519d9e..f8803b28f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ coverage==7.6.3 Flask==3.0.2 Flask-Migrate==4.0.5 Flask-SQLAlchemy==3.1.1 +gunicorn==23.0.0 idna==2.10 iniconfig==2.0.0 itsdangerous==2.1.2