Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5bf5eec
completes wave 1&2
LRyder17 Apr 25, 2023
abe7809
completed
izzybusy5 Apr 25, 2023
0f71bd2
Merge branch 'main' of https://github.com/LRyder17/solar-system-api
izzybusy5 Apr 25, 2023
09d3931
creates POST endpoint
LRyder17 Apr 28, 2023
cc4e408
Merge branch 'main' of https://github.com/LRyder17/solar-system-api
LRyder17 Apr 28, 2023
956f55f
handles single planet
LRyder17 Apr 28, 2023
0f8994c
updated and deleted endpoints
izzybusy5 May 1, 2023
1b26e32
refactored
izzybusy5 May 1, 2023
13c0fbb
adds query params
LRyder17 May 2, 2023
2d68184
testing
izzybusy5 May 2, 2023
f5d78e7
Merge branch 'main' of https://github.com/LRyder17/solar-system-api
LRyder17 May 2, 2023
56672fe
frankinstine merge
LRyder17 May 2, 2023
e8eb4e6
added pytests
LRyder17 May 3, 2023
24a2655
deleted files
izzybusy5 May 3, 2023
868a774
Merge branch 'main' of https://github.com/LRyder17/solar-system-api
izzybusy5 May 3, 2023
5adc317
adds test for empty database
LRyder17 May 3, 2023
52b7d0b
Merge branch 'main' of https://github.com/LRyder17/solar-system-api
LRyder17 May 3, 2023
a50347c
corrects test name
LRyder17 May 3, 2023
dc1e077
some updates
izzybusy5 May 3, 2023
24e76bd
Merge branch 'main' of https://github.com/LRyder17/solar-system-api
izzybusy5 May 3, 2023
f79cc7a
update return message
izzybusy5 May 3, 2023
d936ce5
adds pytests for update and delete
LRyder17 May 4, 2023
474ee69
adds generic validate function
LRyder17 May 4, 2023
ed82db7
final push
LRyder17 May 4, 2023
b80f5c5
added routes folder and refactors
LRyder17 May 9, 2023
5534ed1
fixes moon GET route
LRyder17 May 9, 2023
2134c2b
implements add_moon_to_planet function
LRyder17 May 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from dotenv import load_dotenv
import os

# postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development

db =SQLAlchemy()
migrate = Migrate()
load_dotenv()


def create_app(test_config=None):
app = Flask(__name__)

#DB Config
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

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.models.moon import Moon

from app.routes.planet_routes import planets_bp
from app.routes.moon_routes import moons_bp
app.register_blueprint(planets_bp)
app.register_blueprint(moons_bp)

# from app.models.<model> import <model>

return app
Empty file added app/models/__init__.py
Empty file.
29 changes: 29 additions & 0 deletions app/models/moon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from app import db
from flask import make_response

class Moon(db.Model):
moon_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String)
planets = db.relationship("Planet", back_populates="moon")

def moon_to_dict(self):
return {
"moon_id": self.moon_id,
"name": self.name
}
def __str__(self):
return f'An object of type {self.__class__.__name__} with id {self.moon_id}.'

@classmethod
def create_new_planet(cls, request_data):
if "name" not in request_data or request_data is None:
return make_response("Invalid Request. Missing required fields: name or request data", 400)
return cls(
name=request_data["name"].title()
)

def update(self, moon_to_dict):
for key, value in moon_to_dict.items():
if key == "name":
value = value.title()
setattr(self,key,value)
45 changes: 45 additions & 0 deletions app/models/planet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from app import db
from flask import make_response

class Planet(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String)
description = db.Column(db.String)
color = db.Column(db.String)
moon_id = db.Column(db.Integer, db.ForeignKey('moon.moon_id'))
moon = db.relationship("Moon", back_populates="planets")
# __tablename__ = "planets"


def update(self, planet_to_dict):
for key, value in planet_to_dict.items():
if key == "name":
value = value.title()
setattr(self,key,value)


def planet_to_dict(self):
return {
"id": self.id,
"name": self.name,
"description": self.description,
"color": self.color,
"moon_id": self.moon_id }

def __str__(self):
return f'An object of type {self.__class__.__name__} with id {self.id}.'

@classmethod
def create_new_planet(cls, request_data):
if "name" not in request_data or "description" not in request_data:
return make_response("Invalid Request. Missing required fields: name or description", 400)
return cls(
name=request_data["name"].title(),
description=request_data["description"],
color=request_data.get("color"),
moon_id=request_data.get("moon_id")
)




2 changes: 0 additions & 2 deletions app/routes.py

This file was deleted.

Empty file added app/routes/__init__.py
Empty file.
45 changes: 45 additions & 0 deletions app/routes/moon_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from app import db
from app.models.moon import Moon
from app.models.planet import Planet
from app.routes.planet_routes import validate_model
from flask import Blueprint, jsonify, abort, make_response, request

moons_bp = Blueprint("moons", __name__, url_prefix="/moons")


@moons_bp.route("", methods = ["GET"])
def read_all_moons():
moons_response = []
query_params = request.args.to_dict()

if query_params:
query_params = {k.lower(): v.title() for k, v in query_params.items()}
moons = Moon.query.filter_by(**query_params).all()
else:
moons = Moon.query.all()

moons_response = [moon.moon_to_dict() for moon in moons]
return jsonify(moons_response)

@moons_bp.route("", methods=["POST"])
def create_moon():
# Retrieve the request body
request_body = request.get_json()

# Validate the request body
if not request_body:
return make_response(jsonify({"error": "Request body must be provided"}), 400)
if "name" not in request_body:
return make_response(jsonify({"error": "Name field is required"}), 400)

# Create a new Moon instance
new_moon = Moon(
name=request_body["name"]
)

# Add the new Moon instance to the database session
db.session.add(new_moon)
db.session.commit()

# Return a JSON response indicating success
return make_response(jsonify({"message": f"New moon '{new_moon.name}' created"}), 201)
111 changes: 111 additions & 0 deletions app/routes/planet_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from app import db
from app.models.planet import Planet
from app.models.moon import Moon
from flask import Blueprint, jsonify, abort, make_response, request

planets_bp = Blueprint("planets", __name__, url_prefix="/planets")

@planets_bp.route("", methods = ["GET"])
def read_all_planets():
planets_response = []
query_params = request.args.to_dict()

if query_params:
query_params = {k.lower(): v.title() for k, v in query_params.items()}
planets = Planet.query.filter_by(**query_params).all()
else:
planets = Planet.query.all()

planets_response = [planet.planet_to_dict() for planet in planets]
return jsonify(planets_response)


@planets_bp.route("", methods = ["POST"])
def create_planets():
request_body = request.get_json()
try:
new_planet = Planet.create_new_planet(request_body)
db.session.add(new_planet)
db.session.commit()

message = f"Planet {new_planet.name} successfully created"
return make_response(jsonify(message), 201)

except KeyError as e:
abort(make_response(f"Invalid request. Missing required value: {e}"), 400)


@planets_bp.route("/<id>", methods=["GET"])
def read_one_planet(id):
planet = validate_model(Planet, id)
planet = Planet.query.get(id)
return jsonify(planet.planet_to_dict()), 200

@planets_bp.route("/<id>", methods=["PUT"])
def update_planet(id):
planet = validate_model(Planet, id)

planet.update(request.get_json())

db.session.commit()
message = f"Planet #{id} successfully updated"
return make_response(jsonify(message))

@planets_bp.route("/<id>", methods=["DELETE"])
def delete_planet(id):
planet= validate_model(Planet, id)

db.session.delete(planet)
db.session.commit()

message = f"Planet #{id} successfully deleted"
return make_response(jsonify(message), 200)
def validate_model(cls, id):
try:
id = int(id)
except:
message = f"{cls.__name__} {id} is invalid"
abort(make_response({"message": message}, 400))

model = cls.query.get(id)

if not model:
message = f"{cls.__name__} {id} not found"
abort(make_response({"message": message}, 404))

return model

@planets_bp.route("/<id>/moons", methods=["POST"])
def create_moon(id):
planet = validate_model(Planet, id)
request_body = request.get_json()
new_moon = Moon(
name=request_body["name"]
)

db.session.add(new_moon)
db.session.commit()
add_moon_to_planet(new_moon.moon_id, planet)

return make_response(jsonify(f"Moon {new_moon.name} successfully created"), 201)

def add_moon_to_planet(moon_id, planet):
planet.moon_id = moon_id
db.session.commit()
@planets_bp.route("/<id>/moons", methods=["GET"])
def read_moons(id):
planet = validate_model(Planet, id)
moons_response = []
for id in str(planet.id):
moons_response.append(
{
"moon_id": int(id),
}
)
return jsonify(moons_response)
# def handle_moon_from_planet():





Empty file added app/tests/__init__.py
Empty file.
49 changes: 49 additions & 0 deletions app/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest
from app import create_app
from app import db
from flask.signals import request_finished
from app.models.planet import Planet

# instance of our app that is listening for our test db
@pytest.fixture
def app():
app = create_app({"TESTING": True})
# helps with the accuracy of tests
# clears out temperary data
@request_finished.connect_via(app)
def expire_session(sender, response, **extra):
db.session.remove()

# our database is clean,its empty of all data
with app.app_context():
# generate a new clean, start to db
# an empty db that we can test on
db.create_all()

# tells fixture to return an instance of app context
# sending it to test when test is called
yield app

# once test is complete clears out any data that it created
# so we can work with clean database
with app.app_context():
db.drop_all()

@pytest.fixture
def client(app):
# app that references another fixture
# holds the reference to the test interface
return app.test_client()

@pytest.fixture
def two_saved_planets(app):
# Arrange
ocean_planet = Planet(name="Ocean Planet",
description="Smells fishy",
color="Silver")
minion_planet = Planet(name="Mark",
description="Miniony",
color="Yellow")
db.session.add_all([ocean_planet, minion_planet])
db.session.commit()

Loading