Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readme #30

Merged
merged 4 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,68 @@
# movie_name (To be changed later)
A movie suggesteur based on the current weather and the season.
# Clinema


![image](https://github.com/user-attachments/assets/3d9f59ae-7301-4103-8a8c-f8dec5719b61)

## Overview

Clinema is a cinema management application that allows users to manage movie listings, bookings, and user accounts. The application is built using a microservices architecture, with separate services for the API, client, and database. The API is developed using Flask, while the client is built with Vite and Tailwind CSS for a modern user interface. The application uses MySQL as the database to store all relevant data.

## Features
- User authentication and authorization
- Movie listing and details
- Booking management
- Responsive design for mobile and desktop

## Getting Started
To get started with Clinema, clone the repository and follow the instructions in the respective service directories for setup and deployment.

## Tech Stack
Clinema utilizes a modern tech stack to ensure efficient development and a seamless user experience. The key technologies include:

- **client**: A fast build tool and development server for modern web projects, used for the client-side application built with Vite.
- **server**: A Python framework for building web applications, specifically the API developed using Flask.
- **database**: A relational database management system used to store application data, specifically MySQL.



## Technologies Used
- ReactJS
- Vite
- Tailwind CSS
- Flask
- MySQL

## Installation
To install and run Clinema, follow these steps:

1. **Clone the repository**:
```bash
git clone https://github.com/Matsadura/movie_name.git
cd clinema
```

2. **Set up the environment**:
- Ensure you have Docker and Docker Compose installed on your machine.

3. **Build and run the services**:
```bash
docker-compose up --build
```

4. **Access the application**:
- The API will be available at `http://localhost:5000`
- The client can be accessed at `http://localhost:5173`

5. **Database setup**:
- The MySQL database will be automatically set up by Docker. You can connect to it using the credentials defined in the `docker-compose.yml` file.



## Authors

- [Ali JBARI](https://github.com/ila36IX)
- [Badr ANNABI](https://github.com/Badr-Annabi)
- [Karim ASSIHOUT](https://github.com/ashtkarim)
- [Oumaima NAANAA](https://github.com/naanaa59)
- [Radouane ABOUNOUAS](https://github.com/RadouaneAbn)
- [Zidane ZAOUI](https://github.com/matsadura)
8 changes: 4 additions & 4 deletions client/src/components/MovieCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default function MovieCard({userId, movie_id, title, poster, year, rate,

const handleSave = useCallback(async () => {
setSave(prevState => !prevState);
await toggleSave(userId, movie_id, 'save');
await toggleSave(userId, movie_id, true); // HADI KHAS TKUN DYMANIC 3LA HSSAB CHNU KANT 9BL F DATABASE
}, [userId, movie_id]);

const handleOrder = useCallback(async () => {
Expand All @@ -33,7 +33,7 @@ export default function MovieCard({userId, movie_id, title, poster, year, rate,
const dataToSend = {
user_id: userId,
movie_id: movie_id,
like: liked
like: true // HADI KHAS TKUN DYMANIC 3LA HSSAB CHNU KANT 9BL F DATABASE
};

if (token) {
Expand All @@ -55,8 +55,8 @@ export default function MovieCard({userId, movie_id, title, poster, year, rate,
const token = localStorage.getItem('_token');
const dataToSend = {
user_id: userId,
movie_id: movie_id,
save: save
movie_id: movie_id, // HADI KHAS TKUN DYMANIC 3LA HSSAB CHNU KANT 9BL F DATABASE
save: true
};

if (token) {
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Signin.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default function LogIn({ slideRun }) {
console.log(res);
if (res.status === 200) {
localStorage.setItem("_token", res.data.token);
localStorage.setItem("_user_id", res.data.user_id); // TMP
window.location.href = "/";
} else {
setErrCred(true);
Expand Down
38 changes: 35 additions & 3 deletions client/src/scenes/AllMoviesPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,39 @@ const AllMoviesPage = () => {
);

if (response.data.results && response.data.results.length > 0) {
return response.data.results[0];
const movieData = response.data.results[0];

// ADD MOVIE TO DATABASE
const token = localStorage.getItem('_token');
if (token) {
const dataToSend = {
tmdb_id: movieData.id,
name: movieData.title,
description: movieData.overview,
poster: `https://image.tmdb.org/t/p/w500/${movieData.poster_path}`,
adult: movieData.adult,
popularity: movieData.popularity,
year: new Date(movieData.release_date).getFullYear(),
rating: movieData.vote_average
};

try {
const addMovieResponse = await axios.post(
`${process.env.REACT_APP_API_URL}/movies`,
dataToSend,
{
headers: {
Authorization: `Bearer ${token}`,
}
}
);
console.log('Movie added to database:', addMovieResponse.data);
} catch (dbErr) {
console.error('Error adding movie to database:', dbErr);
}
}

return movieData;
} else {
return null;
}
Expand Down Expand Up @@ -78,8 +110,8 @@ const AllMoviesPage = () => {

return (
<MovieCard
key={movieDetails.id}
userId = { user.id }
key={movieDetails.id}
userId = {localStorage.getItem('_user_id')} // TMP
movie_id={movieDetails.id}
title={movieDetails.title}
// adult={movieDetails.adult}
Expand Down
17 changes: 15 additions & 2 deletions server/api/views/movies.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ def movies():

@app_views.route('/movies', methods=['POST'])
def post_movies():
"""
POST
- Header: Authorization Bearer Token (required)
Input:
- name: String (required)
- tmdb_id: Integer (required)
- description: String
- Poster: String
- adult: Boolean
- popularity: Float
- year: Integer
- rating: Float
"""
try:
verify_jwt_in_request()
current_user = get_jwt_identity()
Expand All @@ -57,10 +70,10 @@ def post_movies():

existing_movie = storage.get_specific(Movie, 'name', name)
if existing_movie:
return jsonify({'error': 'Movie name already exists'}), 400
return jsonify({'error': 'Movie name already exists'}), 409

valid_attributes = ['name', 'description', 'poster', 'adult',
'year', 'rating', 'popularity']
'year', 'rating', 'popularity', 'tmdb_id']
movie_parsed = {}
for k, v in movie_data.items():
if k in valid_attributes:
Expand Down
34 changes: 33 additions & 1 deletion server/api/views/user_movies.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
@app_views.get('/<user_id>/user_movies')
@jwt_required()
def user_movies(user_id):
"""
GET
- Header: Authorization Bearer Token (required)
"""
try:
current_user = get_jwt_identity()
except Exception as e:
Expand All @@ -76,6 +80,10 @@ def user_movies(user_id):
@app_views.get('/<user_id>/save')
@jwt_required()
def save_movies(user_id):
"""
GET
- Header: Authorization Bearer Token (required)
"""
try:
current_user = get_jwt_identity()
except Exception as e:
Expand Down Expand Up @@ -127,6 +135,16 @@ def liked_movies(user_id):
@app_views.post('/<user_id>/liked')
@jwt_required()
def save_or_liked(user_id):
"""
POST
- Header: Authorization Bearer Token (required)
Input:
- movie_id: Integer (required)
- user_id: String (required)
- save: Boolean
- like: Boolean
Only one of save or like is required
"""
try:
current_user = get_jwt_identity()
except Exception as e:
Expand All @@ -153,7 +171,9 @@ def save_or_liked(user_id):
if existing_user_movie:
return jsonify({'error': 'User Movie relation already exists'}), 409

if data['save'] is None and data['like'] is None:
save = data.get('save')
like = data.get('like')
if save is None and like is None:
return jsonify({'error': 'Missing required save or like boolean'}), 400

existing_user = storage.get(User, data['user_id'])
Expand Down Expand Up @@ -195,6 +215,10 @@ def user_movie_id(user_id, user_movie_id):
return jsonify({'error': 'Unauthorized to DEL OR PUT this data'}), 403

if request.method == 'DELETE':
"""
DELETE
- Header: Authorization Bearer Token (required)
"""
um = storage.get_specific(User_Movie, 'id', user_movie_id)
if not um:
return jsonify({'error': 'User_movie is not found'}), 404
Expand All @@ -203,6 +227,14 @@ def user_movie_id(user_id, user_movie_id):
return jsonify({})

if request.method == 'PUT':
"""
PUT
- Header: Authorization Bearer Token (required)
Input:
- save: Boolean
- like: Boolean
Only one of these two is required
"""
try:
data = request.get_json()
except Exception as e:
Expand Down
16 changes: 14 additions & 2 deletions server/api/views/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@
@app_views.route('/users/profile', methods=['GET'])
@jwt_required()
def get_user_profile():
"""Get user profile"""
"""Get user profile

GET
- Header: Authorization Bearer Token (required)
"""
try:
current_user = get_jwt_identity()
except Exception as e:
Expand All @@ -55,7 +59,15 @@ def get_user_profile():
@app_views.route('/users/<user_id>/profile', methods=['DELETE', 'PUT'])
@jwt_required()
def handle_user_id(user_id):
"""Handle user_id related requests"""
"""
Handle user_id related requests

POST
- Header: Authorization Bearer Token (required)
Input:
first_name: String (required)
last_name: String (required)
"""
try:
current_user = get_jwt_identity()
except Exception as e:
Expand Down
9 changes: 5 additions & 4 deletions server/models/movie.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
class Movie(BaseModel, Base):
"""Representation of a user """
__tablename__ = 'movies'
tmdb_id = Column(Integer, nullable=False)
name = Column(String(128), nullable=False)
description = Column(String(512), nullable=True)
description = Column(String(2024), nullable=True)
poster = Column(String(512), nullable=True)
adult = Column(Boolean, nullable=True)
popularity = Column(Float, nullable=True)
year = Column(Integer, nullable=True)
rating = Column(Float, nullable=True)
user_movies = relationship("User_Movie",
back_populates="movie",
cascade="all, delete-orphan")
# user_movies = relationship("User_Movie",
# back_populates="movie",
# cascade="all, delete-orphan")

def __init__(self, *args, **kwargs):
"""Initializes user"""
Expand Down
9 changes: 5 additions & 4 deletions server/models/user_movie.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ class User_Movie(BaseModel, Base):
user_id = Column(String(128),
ForeignKey('users.id', ondelete='CASCADE'),
nullable=False)
movie_id = Column(String(128),
ForeignKey('movies.id', ondelete='CASCADE'),
nullable=False)
# movie_id = Column(String(128),
# ForeignKey('movies.id', ondelete='CASCADE'),
# nullable=False)
movie_id = Column(String(128), nullable=False)
save = Column(Boolean, nullable=True)
like = Column(Boolean, nullable=True)
movie = relationship("Movie", back_populates="user_movies")
# movie = relationship("Movie", back_populates="user_movies")
user = relationship("User", back_populates="user_movies",)

def __init__(self, *args, **kwargs):
Expand Down