Skip to content

Commit

Permalink
Merge pull request #30 from Matsadura/readme
Browse files Browse the repository at this point in the history
ADD Readme
  • Loading branch information
Matsadura authored Sep 16, 2024
2 parents 8a313d1 + 61b4c43 commit 5d0a151
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 22 deletions.
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

0 comments on commit 5d0a151

Please sign in to comment.