diff --git a/migrations/versions/149b30d389da_.py b/migrations/versions/149b30d389da_.py new file mode 100644 index 000000000..d1c181077 --- /dev/null +++ b/migrations/versions/149b30d389da_.py @@ -0,0 +1,52 @@ +"""empty message + +Revision ID: 149b30d389da +Revises: a5cffa318ac2 +Create Date: 2025-04-21 11:50:40.070591 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '149b30d389da' +down_revision = 'a5cffa318ac2' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('people', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=30), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + op.create_table('planets', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=30), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('name') + ) + op.create_table('favorites', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('favorite', sa.Enum('PLANETS', 'PEOPLE', name='favorites_enum'), nullable=False), + sa.Column('planet_id', sa.Integer(), nullable=True), + sa.Column('people_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['people_id'], ['people.id'], ), + sa.ForeignKeyConstraint(['planet_id'], ['planets.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('favorites') + op.drop_table('planets') + op.drop_table('people') + # ### end Alembic commands ### diff --git a/migrations/versions/f358b0659e94_.py b/migrations/versions/f358b0659e94_.py new file mode 100644 index 000000000..132db5c06 --- /dev/null +++ b/migrations/versions/f358b0659e94_.py @@ -0,0 +1,38 @@ +"""empty message + +Revision ID: f358b0659e94 +Revises: 149b30d389da +Create Date: 2025-04-22 13:09:12.734572 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'f358b0659e94' +down_revision = '149b30d389da' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('favorites', schema=None) as batch_op: + batch_op.alter_column('people_id', + existing_type=sa.INTEGER(), + nullable=True) + batch_op.drop_column('favorite') + + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('favorites', schema=None) as batch_op: + batch_op.add_column(sa.Column('favorite', postgresql.ENUM('PLANETS', 'PEOPLE', name='favorites_enum'), autoincrement=False, nullable=False)) + batch_op.alter_column('people_id', + existing_type=sa.INTEGER(), + nullable=False) + + # ### end Alembic commands ### diff --git a/src/admin.py b/src/admin.py index bb934027c..9c6a049dd 100644 --- a/src/admin.py +++ b/src/admin.py @@ -1,6 +1,6 @@ import os from flask_admin import Admin -from models import db, User +from models import db, User,People,Planets,Favorite from flask_admin.contrib.sqla import ModelView def setup_admin(app): @@ -11,6 +11,10 @@ def setup_admin(app): # Add your models here, for example this is how we add a the User model to the admin admin.add_view(ModelView(User, db.session)) + admin.add_view(ModelView(People, db.session)) + admin.add_view(ModelView(Planets, db.session)) + admin.add_view(ModelView(Favorite, db.session)) + # You can duplicate that line to add mew models # admin.add_view(ModelView(YourModelName, db.session)) \ No newline at end of file diff --git a/src/app.py b/src/app.py index 016c5bff9..002a66488 100644 --- a/src/app.py +++ b/src/app.py @@ -8,15 +8,17 @@ from flask_cors import CORS from utils import APIException, generate_sitemap from admin import setup_admin -from models import db, User -#from models import Person +from models import db, User, People, Favorite, Planets +from sqlalchemy import select +# from models import Person app = Flask(__name__) app.url_map.strict_slashes = False db_url = os.getenv("DATABASE_URL") if db_url is not None: - app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace("postgres://", "postgresql://") + app.config['SQLALCHEMY_DATABASE_URI'] = db_url.replace( + "postgres://", "postgresql://") else: app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db" app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False @@ -27,23 +29,149 @@ setup_admin(app) # Handle/serialize errors like a JSON object + + @app.errorhandler(APIException) def handle_invalid_usage(error): return jsonify(error.to_dict()), error.status_code # generate sitemap with all your endpoints + + @app.route('/') def sitemap(): return generate_sitemap(app) + @app.route('/user', methods=['GET']) def handle_hello(): - response_body = { - "msg": "Hello, this is your GET /user response " - } + users = User.query.all() + if len(users) == 0: + return jsonify({ + "msg": "No se encontraron los resultados" + }), 404 + else: + return jsonify([user.serialize() for user in users]), 200 + + +@app.route("/people", methods=["GET"]) +def get_people(): + characters = People.query.all() + if len(characters) == 0: + return jsonify({ + "msg": "No se encontraron los resultados" + }), 404 + else: + return jsonify([character.serialize_people() for character in characters]), 200 + + +@app.route('/people/', methods=["GET"]) +def get_character(id): + character = People.query.filter_by(id=id).first() + if character is None: + return jsonify({ + "msg": "No se encontraron los resultados" + }), 404 + else: + return jsonify({ + "character": character.serialize_people() + }), 200 + + +@app.route("/planets", methods=["GET"]) +def get_planets(): + planets = Planets.query.all() + if len(planets) == 0: + return jsonify({ + "msg": "No se encontraron los resultados" + }), 404 + else: + return jsonify([planet.serialize_planets() for planet in planets]), 200 + + +@app.route("/planets/", methods=["GET"]) +def get_planet(id): + planet = Planets.query.filter_by(id=id).first() + + if planet is None: + return jsonify({ + "msg": "No se encontro el recurso" + }), 404 + else: + return jsonify(planet.serialize_planets()), 200 + + +@app.route("/favorites/user/", methods=["GET"]) +def favorites_user(): + favorites = Favorite.query.filter_by(user_id=1).all() + return jsonify([favorite.serialize() for favorite in favorites]), 200 + + +@app.route("/favorite/planet/", methods=["POST"]) +def post_favorite_planet(planet_id): + planet = Planets.query.get(planet_id) + if planet is None: + return jsonify({ + "msg": "Not found" + }), 404 + favorite = Favorite.query.filter_by(user_id=1, planet_id=planet_id).first() + if favorite: + return jsonify({ + "msg": "Already exist" + }), 409 + new_favorite = Favorite(user_id=1, planet_id=planet_id, people_id=None) + db.session.add(new_favorite) + db.session.commit() + return jsonify(new_favorite.serialize()), 200 + + +@app.route("/favorite/people/", methods=["POST"]) +def post_favorite_people(people_id): + people = People.query.get(people_id) + if people is None: + return jsonify({ + "msg": "Not found" + }), 404 + favorite = Favorite.query.filter_by(user_id=1, people_id=people_id).first() + if favorite: + return jsonify({ + "msg": "Already exist" + }), 409 + + new_favorite = Favorite(user_id=1, people_id=people_id, planet_id=None) + db.session.add(new_favorite) + db.session.commit() + return jsonify(new_favorite.serialize()) + + +@app.route("/favorite/planet/", methods=["DELETE"]) +def delete_favorite_planet(id): + favorite = Favorite.query.filter_by(user_id=1, planet_id=id).first() + if favorite is None: + return jsonify({ + "msg": "Not found" + }), 404 + db.session.delete(favorite) + db.session.commit() + return jsonify({ + "msg": "Successfully deleted" + }), 200 + + +@app.route("/favorite/people/", methods=["DELETE"]) +def delete_favorite_people(id): + favorite = Favorite.query.filter_by(user_id=1, people_id=id).first() + if favorite is None: + return jsonify({ + "msg": "Not found" + }), 404 + db.session.delete(favorite) + db.session.commit() + return jsonify({ + "msg": "Successfully deleted" + }), 200 - return jsonify(response_body), 200 # this only runs if `$ python src/app.py` is executed if __name__ == '__main__': diff --git a/src/models.py b/src/models.py index 7f535f618..7b199d9af 100644 --- a/src/models.py +++ b/src/models.py @@ -1,15 +1,24 @@ from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import String, Boolean -from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy import String, Boolean, ForeignKey, Enum +from sqlalchemy.orm import Mapped, mapped_column, relationship +from enum import Enum as pyEnum db = SQLAlchemy() + +# class FavoritesEnum(pyEnum): +# PLANETS = "planets" +# PEOPLE = "people" + + class User(db.Model): id: Mapped[int] = mapped_column(primary_key=True) - email: Mapped[str] = mapped_column(String(120), unique=True, nullable=False) + email: Mapped[str] = mapped_column( + String(120), unique=True, nullable=False) password: Mapped[str] = mapped_column(nullable=False) is_active: Mapped[bool] = mapped_column(Boolean(), nullable=False) - + favorites = relationship( + 'Favorite', back_populates='user') def serialize(self): return { @@ -17,3 +26,57 @@ def serialize(self): "email": self.email, # do not serialize the password, its a security breach } + + +class People(db.Model): + __tablename__ = "people" + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(30), nullable=False, unique=True) + favorites = relationship( + "Favorite", back_populates="people") + + def serialize_people(self): + return { + "id": self.id, + "name": self.name + } + + +class Planets(db.Model): + __tablename__ = "planets" + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(30), nullable=False, unique=True) + favorites: Mapped[list['Favorite']] = relationship( + "Favorite", back_populates="planet") + + def serialize_planets(self): + return { + "id": self.id, + "name": self.name + } + + +class Favorite(db.Model): + __tablename__ = "favorites" + id: Mapped[int] = mapped_column(primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey('user.id'), nullable=False) + # favorite: Mapped[FavoritesEnum] = mapped_column( + # Enum(FavoritesEnum, name="favorites_enum")) + planet_id: Mapped[int] = mapped_column( + ForeignKey('planets.id'), nullable=True) + people_id: Mapped[int] = mapped_column(ForeignKey('people.id'),nullable=True) + user = relationship( + "User", back_populates="favorites") + planet = relationship( + "Planets", back_populates="favorites") + people = relationship( + "People", back_populates="favorites") + + def serialize(self): + return { + "id": self.id, + "user_id": self.user_id, + # "favorite": self.favorite.value, + "planet_id": self.planet_id, + "people_id": self.people_id + }