Skip to content

Commit

Permalink
[FIX/ADD] Fix IMORTANT BROKEN docker errors, ADD Base Database Schema…
Browse files Browse the repository at this point in the history
… | Metrics Routes
  • Loading branch information
Matsadura committed Sep 10, 2024
1 parent 07b6e83 commit b873409
Show file tree
Hide file tree
Showing 14 changed files with 308 additions and 10 deletions.
12 changes: 9 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ services:
ports:
- "5000:5000"
volumes:
- ./server:/movie_name/server
- .:/movie_name
environment:
MOVIE_DB_HOST: mysql_db
MOVIE_DB_USER: CINEMA_USER
MOVIE_DB_PASSWORD: CINEMA_PASSWORD
MOVIE_DB_NAME: CINEMA_DB
PYTHONPATH: /movie_name
depends_on:
- mysql

Expand Down Expand Up @@ -36,9 +42,9 @@ services:
MYSQL_USER: CINEMA_USER
MYSQL_PASSWORD: CINEMA_PASSWORD
ports:
- "3307:3306"
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql

volumes:
mysql_data:
mysql_data:
4 changes: 0 additions & 4 deletions server/api/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
MOVIE_API_HOST=0.0.0.0
MOVIE_API_PORT=5000
MOVIE_DB_HOST=mysql_db
MOVIE_DB_USER=CINEMA_USER
MOVIE_DB_PASSWORD=CINEMA_PASSWORD
MOVIE_DB_NAME=CINEMA_DB
17 changes: 16 additions & 1 deletion server/api/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#!/usr/bin/env python3
""" Starts the Flask web app """
import os
from flask import Flask
from dotenv import load_dotenv
from flask import Flask, jsonify
from models import storage
from api.views import app_views


load_dotenv()
app = Flask(__name__)
app.url_map.strict_slashes = False
app.register_blueprint(app_views)
HOST = "0.0.0.0"
PORT = 5000

Expand All @@ -18,6 +21,18 @@ def volume():
return "Testing volumes: -Zidane Square headed-"


@app.teardown_appcontext
def teardown_db(exception):
""" Closes the storage session """
storage.close()


@app.errorhandler(404)
def page_not_found(e):
""" Handles the 404 error """
return jsonify({"error": "Not found"}), 404


if __name__ == "__main__":
if os.getenv("MOVIE_API_HOST"):
HOST = os.getenv("MOVIE_API_HOST")
Expand Down
8 changes: 8 additions & 0 deletions server/api/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/python3
""" Contains the flask blueprint """
from flask import Blueprint

app_views = Blueprint('app_views', __name__, url_prefix="/api")

from api.views.users import * # nopep8
from api.views.index import * # nopep8
23 changes: 23 additions & 0 deletions server/api/views/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/python3
"""Metrics Routes"""
from api.views import app_views


@app_views.route('/status')
def status():
"""Return the API status all wrapped in a json object"""
return {"status": "OK"}, 200


@app_views.route('stats')
def stats():
"""Return the count of all classes"""
from models.user import User
from models.mood import Mood
from models.recommendation import Recommendation
from models import storage

return {"Users": storage.count(User),
"Moods": storage.count(Mood),
"Recommendations": storage.count(Recommendation)
}
16 changes: 14 additions & 2 deletions server/flask.dockerfile
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
FROM python:3.8-slim

RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y pkg-config
RUN apt-get install -y default-libmysqlclient-dev
RUN apt-get install -y build-essential

WORKDIR /movie_name

COPY . /movie_name

RUN pip3 install flask==2.1.0 werkzeug==2.1.1 flask-cors==4.0.1 sqlalchemy==1.4.22 python-dotenv
RUN pip3 install flask==2.1.0 werkzeug==2.1.1 flask-cors==4.0.1 sqlalchemy==1.4.22 mysqlclient==2.2.4 python-dotenv

RUN apt-get clean && rm -rf /var/lib/apt/lists/*

EXPOSE 5000

CMD ["python3", "-m", "server.api.app"]
COPY wait-for-mysql.sh /usr/local/bin/wait-for-mysql.sh
RUN chmod +x /usr/local/bin/wait-for-mysql.sh

WORKDIR /movie_name/server

CMD ["wait-for-mysql.sh", "python3", "-m", "api.app"]
8 changes: 8 additions & 0 deletions server/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/python3
"""Initialize the models package."""

from models.engine.db_storage import DBStorage


storage = DBStorage()
storage.reload()
75 changes: 75 additions & 0 deletions server/models/base_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""
Contains class BaseModel
"""

from datetime import datetime
import models
from sqlalchemy import Column, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
import uuid

time = "%Y-%m-%dT%H:%M:%S.%f"
Base = declarative_base()


class BaseModel:
"""The BaseModel class from which future classes will be derived"""
id = Column(String(60), primary_key=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow)

def __init__(self, *args, **kwargs):
"""Initialization of the base model"""
if kwargs:
for key, value in kwargs.items():
if key != "__class__":
setattr(self, key, value)
if kwargs.get("created_at", None) and type(self.created_at) is str:
self.created_at = datetime.strptime(kwargs["created_at"], time)
else:
self.created_at = datetime.utcnow()
if kwargs.get("updated_at", None) and type(self.updated_at) is str:
self.updated_at = datetime.strptime(kwargs["updated_at"], time)
else:
self.updated_at = datetime.utcnow()
if kwargs.get("id", None) is None:
self.id = str(uuid.uuid4())
else:
self.id = str(uuid.uuid4())
self.created_at = datetime.utcnow()
self.updated_at = self.created_at

def __str__(self):
"""String representation of the BaseModel class"""
return "[{:s}] ({:s}) {}".format(self.__class__.__name__, self.id,
self.__dict__)

def __repr__(self):
"""String representation of the BaseModel class"""
return "[{:s}] ({:s}) {}".format(self.__class__.__name__, self.id,
self.__dict__)

def save(self):
"""Updates the attribute 'updated_at' with the current datetime"""
self.updated_at = datetime.utcnow()
models.storage.new(self)
models.storage.save()

def to_dict(self):
"""Returns a dictionary containing all keys/values of the instance"""
new_dict = self.__dict__.copy()
if "created_at" in new_dict:
new_dict["created_at"] = new_dict["created_at"].strftime(time)
if "updated_at" in new_dict:
new_dict["updated_at"] = new_dict["updated_at"].strftime(time)
new_dict["__class__"] = self.__class__.__name__
if "_sa_instance_state" in new_dict:
del new_dict["_sa_instance_state"]
if "password" in new_dict:
del new_dict["password"]
return new_dict

def delete(self):
"""Delete the current instance from the storage"""
models.storage.delete(self)
Empty file.
83 changes: 83 additions & 0 deletions server/models/engine/db_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""
Contains the class DBStorage
"""

from models.base_model import BaseModel, Base
from models.mood import Mood
from models.recommendation import Recommendation
from models.user import User
from os import getenv
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker

classes = {"Mood": Mood, "Recommendation": Recommendation, "User": User}


class DBStorage:
"""Interaacts with the MySQL database"""
__engine = None
__session = None

def __init__(self):
"""Instantiate a DBStorage object"""
MOVIE_DB_USER = getenv('MOVIE_DB_USER')
MOVIE_DB_PASSWORD = getenv('MOVIE_DB_PASSWORD')
MOVIE_DB_HOST = getenv('MOVIE_DB_HOST')
MOVIE_DB_NAME = getenv('MOVIE_DB_NAME')
self.__engine = create_engine('mysql+mysqldb://{}:{}@{}/{}'.
format(MOVIE_DB_USER,
MOVIE_DB_PASSWORD,
MOVIE_DB_HOST,
MOVIE_DB_NAME))

def all(self, cls=None):
"""query on the current database session"""
new_dict = {}
for clss in classes:
if cls is None or cls is classes[clss] or cls is clss:
objs = self.__session.query(classes[clss]).all()
for obj in objs:
key = obj.__class__.__name__ + '.' + obj.id
new_dict[key] = obj
return (new_dict)

def new(self, obj):
"""Add the object to the current database session"""
self.__session.add(obj)

def save(self):
"""Commit all changes of the current database session"""
self.__session.commit()

def delete(self, obj=None):
"""Delete from the current database session obj if not None"""
if obj is not None:
self.__session.delete(obj)

def reload(self):
"""Reloads data from the database"""
Base.metadata.create_all(self.__engine)
sess_factory = sessionmaker(bind=self.__engine, expire_on_commit=False)
Session = scoped_session(sess_factory)
self.__session = Session

def close(self):
"""Call remove() method on the private session attribute"""
self.__session.remove()

def get(self, cls, id):
"""Returns the object based on the class and its ID,
or None if not found"""
objs = self.all(cls).values()
for obj in objs:
if obj.id == id:
return obj
return None

def count(self, cls=None):
"""
Returns the number of objects in storage matching the given class.
If no class is passed, returns the count of all objects in storage.
"""
return len(self.all(cls).keys())
18 changes: 18 additions & 0 deletions server/models/mood.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""Holds class Mood"""
from models.base_model import BaseModel, Base
from sqlalchemy import Column, String
from sqlalchemy.orm import relationship


class Mood(BaseModel, Base):
"""Representation of a user """
__tablename__ = 'moods'
mood = Column(String(24), nullable=False)
recommendations = relationship("Recommendation",
back_populates="mood",
cascade="all, delete-orphan")

def __init__(self, *args, **kwargs):
"""Initializes user"""
super().__init__(*args, **kwargs)
26 changes: 26 additions & 0 deletions server/models/recommendation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
"""Holds class Recommendation"""
from models.base_model import BaseModel, Base
from sqlalchemy import Column, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship


class Recommendation(BaseModel, Base):
"""Representation of a vault """
__tablename__ = 'Recommendation'
user_id = Column(String(128),
ForeignKey('users.id', ondelete='CASCADE'),
nullable=False)
mood_id = Column(String(128),
ForeignKey('moods.id', ondelete='CASCADE'),
nullable=False)
weather = Column(String(128), nullable=False)
recommendaton = Column(String(128), nullable=True)
seen = Column(Boolean, nullable=False)
like = Column(Boolean, nullable=False)
mood = relationship("Mood", back_populates="recommendations")
user = relationship("User", back_populates="recommendations",)

def __init__(self, *args, **kwargs):
"""initializes vault"""
super().__init__(*args, **kwargs)
25 changes: 25 additions & 0 deletions server/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""Holds class User"""
from models.base_model import BaseModel, Base
from sqlalchemy import Column, String
from sqlalchemy.orm import relationship
import hashlib


class User(BaseModel, Base):
"""Representation of a user """
__tablename__ = 'users'
email = Column(String(128), nullable=False)
password = Column(String(128), nullable=False)
first_name = Column(String(128), nullable=False)
last_name = Column(String(128), nullable=False)
recent_mood = Column(String(24), nullable=False)
recommendations = relationship("Recommendation",
back_populates="user",
cascade="all, delete-orphan")

def __init__(self, *args, **kwargs):
"""Initializes user"""
kwargs["password"] = hashlib.sha256(
kwargs["password"].encode()).hexdigest()
super().__init__(*args, **kwargs)
3 changes: 3 additions & 0 deletions wait-for-mysql.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
sleep 10
exec "$@"

0 comments on commit b873409

Please sign in to comment.