Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6988a8c
add wave 1, get all planets
misha-c Oct 24, 2022
1fd6bd8
updates planet attributes
misha-c Oct 24, 2022
a4e0a8e
pushing wave 2 completed
leilow Oct 24, 2022
6ec6e5d
created planet model
leilow Oct 28, 2022
e9030af
server is working
leilow Oct 28, 2022
c509cd6
commit before pulling
misha-c Oct 28, 2022
9b75377
Merge branch 'main' of https://github.com/leilow/solar-system-api
misha-c Oct 28, 2022
13b5382
wave 3 is done
leilow Oct 28, 2022
bb257e2
Merge branch 'main' of https://github.com/leilow/solar-system-api
misha-c Oct 28, 2022
0541226
commit before pulling
misha-c Oct 29, 2022
ae506cd
adds wave 4 -get 1, update, delete
misha-c Oct 31, 2022
0a56590
added is_dwarf attribute to model
leilow Nov 1, 2022
84ba83d
Added function for query parameters, search by name
leilow Nov 2, 2022
e94fb40
adds is_dwarf again
misha-c Nov 3, 2022
6abfc1b
Merge branch 'main' of https://github.com/leilow/solar-system-api
misha-c Nov 3, 2022
1a94408
reverting back to old version
leilow Nov 3, 2022
d564717
add .env file, updates create_app test config
misha-c Nov 3, 2022
fc56176
restoring to previous after merge conflict
misha-c Nov 3, 2022
7c78c99
Merge branch 'main' of https://github.com/leilow/solar-system-api
misha-c Nov 3, 2022
0637fa7
added name query
leilow Nov 3, 2022
640717f
set up test_cofig files and fixtures
leilow Nov 3, 2022
7c3f1fe
added to todo list for testing
leilow Nov 3, 2022
266a6de
two working test
leilow Nov 3, 2022
0c97dd5
passing all pytest
leilow Nov 4, 2022
9bce6cc
sorry, beautified the pytest
leilow Nov 4, 2022
83de446
cleaned up routes.py a bit
leilow Nov 4, 2022
4a398f9
commit before pulling
misha-c Nov 4, 2022
9069d67
merging -- all test pass-- thank you Lei <3
misha-c Nov 4, 2022
c8cd491
adds test_model file with tests and helper function to_dict
misha-c Nov 4, 2022
1eb2d9a
Created a classmethod and refactored test_routes.py and routes.py
leilow Nov 4, 2022
437893d
Cleaned up files. Took out old code and comments. Updated unncessary …
leilow Nov 4, 2022
375836c
edits to update_planet
misha-c Nov 4, 2022
7143671
deleted line in update planet-- works now
misha-c Nov 4, 2022
f59fe56
Procfil creation
leilow Nov 7, 2022
6d9f2ef
"hm"
leilow Nov 7, 2022
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.env
.vscode
.DS_Store

Expand Down
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: gunicorn 'app:create_app()'
26 changes: 25 additions & 1 deletion app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
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__)

return app
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/solar_system_development'

if not test_config:
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get("SQLALCHEMY_DATABASE_URI")
else:
app.config["TESTING"] = True
# app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
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 .routes import planet_bp
app.register_blueprint(planet_bp)

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

class Planet(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String)
color = db.Column(db.String)
livability = db.Column(db.Integer)
moons = db.Column(db.Integer)
is_dwarf = db.Column(db.Boolean)

def to_dict(self):
planet_as_dict = {}
planet_as_dict["id"] = self.id
planet_as_dict["name"] = self.name
planet_as_dict["color"] = self.color
planet_as_dict["livability"] = self.livability
planet_as_dict["moons"] = self.moons
planet_as_dict["is_dwarf"] = self.is_dwarf
return planet_as_dict

@classmethod
def from_dict(cls, planet_data):
new_planet = Planet(name=planet_data["name"],
color=planet_data["color"],
moons=planet_data["moons"],
livability=planet_data["livability"],
is_dwarf=planet_data["is_dwarf"])
return new_planet
Comment on lines +11 to +28
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good helper methods

69 changes: 68 additions & 1 deletion app/routes.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,69 @@
from flask import Blueprint
from app import db
from app.models.planet import Planet
from flask import Blueprint, jsonify, abort, make_response, request

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

def validate_model(cls, model_id):
try:
model_id = int(model_id)
except:
abort(make_response({"message":f"the planet {cls.__name__} {model_id} is invalid, please search by planet_id."}, 400))

planet = cls.query.get(model_id)

if not planet:
abort(make_response({"message":f"the planet {cls.__name__} {model_id} doesn't exist."}, 404))
return planet

Comment on lines +7 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good validation function

@planet_bp.route("/<model_id>", methods = ["GET"])
def get_one_planet(model_id):
planet = validate_model(Planet, model_id)
# planet = cls.query.get(model_id)
return planet.to_dict()

@planet_bp.route("", methods=["GET"])
def get_all_planets():
name_query = request.args.get("name")
if name_query:
planets = Planet.query.filter_by(name=name_query)
else:
planets = Planet.query.all()

planet_response = []
for planet in planets:
planet_response.append(planet.to_dict())
return jsonify(planet_response)

@planet_bp.route("", methods=["POST"])
def create_new_planet():
request_body = request.get_json()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing some validation on the request body would be appropriate.

new_planet = Planet.from_dict(request_body)

db.session.add(new_planet)
db.session.commit()

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

@planet_bp.route("/<model_id>", methods = ["PUT"])
def update_planet(model_id):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting this is untested.

planet = validate_model(Planet, model_id)
request_body = request.get_json()

planet.name = request_body["name"],
planet.color = request_body["color"],
planet.moons = request_body["moons"],
planet.livability = request_body["livability"],
planet.is_dwarf = request_body["is_dwarf"]
Comment on lines +53 to +57
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing some validation on the request body here would also be appropriate.


db.session.commit()

return make_response(f"Planet #{model_id} successfully updated.")

@planet_bp.route("/<model_id>", methods = ["DELETE"])
def delete_planet(model_id):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noting this is untested.

planet = validate_model(Planet, model_id)
db.session.delete(planet)
db.session.commit()

return make_response(f"Planet #{model_id} successfully deleted.")
Empty file added app/tests/__init__.py
Empty file.
33 changes: 33 additions & 0 deletions app/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest
from app import create_app
from app import db
from app.models.planet import Planet
from flask.signals import request_finished

@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()

@pytest.fixture
def saved_test_planets(app):

test_planet1 = Planet(color= "pink", is_dwarf= True, livability= 3, moons= 99, name= "Pretend Planet X")
test_planet2 = Planet(color= "purple", is_dwarf= False, livability= 7.4, moons= 1, name= "Planet Pretend Z")

db.session.add_all([test_planet1, test_planet2])
db.session.commit()
80 changes: 80 additions & 0 deletions app/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from app.models.planet import Planet

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love these tests ❤️

def test_to_dict_no_missing_data():
# Arrange
test_data = Planet(
id = 1,
color = "pink",
is_dwarf = True,
livability = 3,
moons = 99,
name = "Pretend Planet X")
# Act
result = test_data.to_dict()

# Assert
assert len(result) == 6
assert result["id"] == 1
assert result["color"] == "pink"
assert result["livability"] == 3
assert result["moons"] == 99
assert result["name"] == "Pretend Planet X"

def test_to_dict_missing_id():
# Arrange
test_data = Planet(
color = "pink",
is_dwarf = True,
livability = 3,
moons = 99,
name = "Pretend Planet X")
# Act
result = test_data.to_dict()

# Assert
assert len(result) == 6
assert result["id"] is None
assert result["color"] == "pink"
assert result["livability"] == 3
assert result["moons"] == 99
assert result["name"] == "Pretend Planet X"



def test_to_dict_missing_name():
# Arrange
test_data = Planet(id = 1,
color = "pink",
is_dwarf = True,
livability = 3,
moons = 99)

# Act
result = test_data.to_dict()

# Assert
assert len(result) == 6
assert result["id"] == 1
assert result["color"] == "pink"
assert result["livability"] == 3
assert result["moons"] == 99
assert result["name"] is None

def test_to_dict_missing_moons():
# Arrange
test_data = Planet(id = 1,
color = "pink",
is_dwarf = True,
livability = 3,
name = "Pretend Planet X")

# Act
result = test_data.to_dict()

# Assert
assert len(result) == 6
assert result["id"] == 1
assert result["color"] == "pink"
assert result["livability"] == 3
assert result["moons"] is None
assert result["name"] == "Pretend Planet X"
58 changes: 58 additions & 0 deletions app/tests/test_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from app.models.planet import Planet

def test_get_all_planets_in_database(client, saved_test_planets):
# Act
response = client.get('/planets')
response_body = Planet.query.all()

# Assert
assert response.status_code == 200

def test_missing_planet_id(client):
# Act
response = client.get('planets/3')
response_body = response.get_json()

# Assert
assert response.status_code == 404

def test_get_planets_for_empty_database(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_by_id(client, saved_test_planets):
# Act
response = client.get('/planets/1')
response_body = response.get_json()

# Assert
assert response.status_code == 200
assert response_body == {'id': 1, 'color': "pink", 'is_dwarf': True, 'livability': 3, 'moons': 99, 'name': "Pretend Planet X"}

def test_create_one_new_planet(client):
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also testing a create action with an invalid request body would be appropriate.

# Act
EXPECTED_PLANET = {
"color": "green",
"is_dwarf": False,
"livability": 6,
"moons": 43,
"name": "New Pretend Planet Y",
}

response = client.post("/planets", json=EXPECTED_PLANET)
response_body = response.get_data(as_text=True)
actual_planet = Planet.query.get(1)

# Assert
assert response.status_code == 201
assert response_body == f"Planet {EXPECTED_PLANET['name']} successfully created"
assert actual_planet.color == EXPECTED_PLANET["color"]
assert actual_planet.is_dwarf == EXPECTED_PLANET["is_dwarf"]
assert actual_planet.livability == EXPECTED_PLANET["livability"]
assert actual_planet.moons == EXPECTED_PLANET["moons"]
assert actual_planet.name == EXPECTED_PLANET["name"]
5 changes: 3 additions & 2 deletions coworking_agreement.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ Talk through each section with your partner. Add notes on what you discussed and
*Other co-working agreements that were not captured in the above sections.*

## Signatures
______________ _______________
Date: _________
Leimomi Bong
Mishella Joy
Novemeber 2022
1 change: 1 addition & 0 deletions migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
45 changes: 45 additions & 0 deletions migrations/alembic.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# A generic, single database configuration.

[alembic]
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false


# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Loading