diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..78e5623149 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,47 @@ +name: 'Lint Code' +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] +jobs: + lint_python: + name: Lint Python Files + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 + - name: Print working directory + run: pwd + - name: Run Linter + run: | + pwd + # This command finds all Python files recursively and runs flake8 on them + find . -name "*.py" -exec flake8 {} + + echo "Linted all the python files successfully" + lint_js: + name: Lint JavaScript Files + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + - name: Install Node.js + uses: actions/setup-node@v3 + with: + node-version: 14 + - name: Install JSHint + run: npm install jshint --global + - name: Run Linter + run: | + # This command finds all JavaScript files recursively and runs JSHint on them + find ./server/database -name "*.js" -exec jshint {} + + echo "Linted all the js files successfully" diff --git a/server/database/app.js b/server/database/app.js index 00f52b2008..0ec213a3d7 100644 --- a/server/database/app.js +++ b/server/database/app.js @@ -1,104 +1,80 @@ const express = require('express'); const mongoose = require('mongoose'); -const fs = require('fs'); -const cors = require('cors') -const app = express() -const port = 3030; +const bodyParser = require('body-parser'); +const Review = require('./models/review'); // Ajusta la ruta según tu estructura +const Dealer = require('./models/dealership'); // Ajusta la ruta según tu estructura -app.use(cors()) -app.use(require('body-parser').urlencoded({ extended: false })); +const app = express(); +app.use(bodyParser.json()); -const reviews_data = JSON.parse(fs.readFileSync("reviews.json", 'utf8')); -const dealerships_data = JSON.parse(fs.readFileSync("dealerships.json", 'utf8')); +// Endpoints -mongoose.connect("mongodb://mongo_db:27017/",{'dbName':'dealershipsDB'}); - - -const Reviews = require('./review'); - -const Dealerships = require('./dealership'); - -try { - Reviews.deleteMany({}).then(()=>{ - Reviews.insertMany(reviews_data['reviews']); - }); - Dealerships.deleteMany({}).then(()=>{ - Dealerships.insertMany(dealerships_data['dealerships']); - }); - -} catch (error) { - res.status(500).json({ error: 'Error fetching documents' }); -} - - -// Express route to home -app.get('/', async (req, res) => { - res.send("Welcome to the Mongoose API") -}); - -// Express route to fetch all reviews +// Fetch all reviews app.get('/fetchReviews', async (req, res) => { - try { - const documents = await Reviews.find(); - res.json(documents); - } catch (error) { - res.status(500).json({ error: 'Error fetching documents' }); - } + try { + const reviews = await Review.find(); + res.json(reviews); + } catch (error) { + res.status(500).send(error); + } }); -// Express route to fetch reviews by a particular dealer +// Fetch reviews by dealer ID app.get('/fetchReviews/dealer/:id', async (req, res) => { - try { - const documents = await Reviews.find({dealership: req.params.id}); - res.json(documents); - } catch (error) { - res.status(500).json({ error: 'Error fetching documents' }); - } + try { + const reviews = await Review.find({ dealerId: req.params.id }); + res.json(reviews); + } catch (error) { + res.status(500).send(error); + } }); -// Express route to fetch all dealerships +// Fetch all dealers app.get('/fetchDealers', async (req, res) => { -//Write your code here + try { + const dealers = await Dealer.find(); + res.json(dealers); + } catch (error) { + res.status(500).send(error); + } }); -// Express route to fetch Dealers by a particular state +// Fetch dealers by state app.get('/fetchDealers/:state', async (req, res) => { -//Write your code here + try { + const dealers = await Dealer.find({ state: req.params.state }); + res.json(dealers); + } catch (error) { + res.status(500).send(error); + } }); -// Express route to fetch dealer by a particular id +// Fetch dealer by ID app.get('/fetchDealer/:id', async (req, res) => { -//Write your code here + try { + const dealer = await Dealer.findById(req.params.id); + if (!dealer) { + return res.status(404).send('Dealer not found'); + } + res.json(dealer); + } catch (error) { + res.status(500).send(error); + } }); -//Express route to insert review -app.post('/insert_review', express.raw({ type: '*/*' }), async (req, res) => { - data = JSON.parse(req.body); - const documents = await Reviews.find().sort( { id: -1 } ) - let new_id = documents[0]['id']+1 - - const review = new Reviews({ - "id": new_id, - "name": data['name'], - "dealership": data['dealership'], - "review": data['review'], - "purchase": data['purchase'], - "purchase_date": data['purchase_date'], - "car_make": data['car_make'], - "car_model": data['car_model'], - "car_year": data['car_year'], - }); - - try { - const savedReview = await review.save(); - res.json(savedReview); - } catch (error) { - console.log(error); - res.status(500).json({ error: 'Error inserting review' }); - } +// Insert a review +app.post('/insert_review', async (req, res) => { + const review = new Review(req.body); + try { + await review.save(); + res.status(201).send(review); + } catch (error) { + res.status(400).send(error); + } }); -// Start the Express server -app.listen(port, () => { - console.log(`Server is running on http://localhost:${port}`); +// Start the server +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); }); diff --git a/server/djangoapp/admin.py b/server/djangoapp/admin.py index 433657fc64..60e22ce4aa 100644 --- a/server/djangoapp/admin.py +++ b/server/djangoapp/admin.py @@ -1,5 +1,10 @@ -# from django.contrib import admin -# from .models import related models + +from django.contrib import admin +from .models import CarMake, CarModel + +# Registrando modelos con su respectivo administrador +admin.site.register(CarMake) +admin.site.register(CarModel) # Register your models here. diff --git a/server/djangoapp/models.py b/server/djangoapp/models.py index eb101a68c8..97a7bff63d 100644 --- a/server/djangoapp/models.py +++ b/server/djangoapp/models.py @@ -1,8 +1,8 @@ # Uncomment the following imports before adding the Model code -# from django.db import models -# from django.utils.timezone import now -# from django.core.validators import MaxValueValidator, MinValueValidator +from django.db import models +from django.utils.timezone import now +from django.core.validators import MaxValueValidator, MinValueValidator # Create your models here. @@ -12,7 +12,12 @@ # - Description # - Any other fields you would like to include in car make model # - __str__ method to print a car make object - +class CarMake(models.Model): + name = models.CharField(max_length=100) + description = models.TextField() + # Other fields as needed + def __str__(self): + return self.name # Return the name as the string representation # Create a Car Model model `class CarModel(models.Model):`: # - Many-To-One relationship to Car Make model (One Car Make has many @@ -23,3 +28,22 @@ # - Year (IntegerField) with min value 2015 and max value 2023 # - Any other fields you would like to include in car model # - __str__ method to print a car make object +class CarModel(models.Model): + car_make = models.ForeignKey(CarMake, on_delete=models.CASCADE) # Many-to-One relationship + name = models.CharField(max_length=100) + CAR_TYPES = [ + ('SEDAN', 'Sedan'), + ('SUV', 'SUV'), + ('WAGON', 'Wagon'), + # Add more choices as required + ] + type = models.CharField(max_length=10, choices=CAR_TYPES, default='SUV') + year = models.IntegerField(default=2023, + validators=[ + MaxValueValidator(2023), + MinValueValidator(2015) + ]) + # Other fields as needed + + def __str__(self): + return self.name # Return the name as the string representation \ No newline at end of file diff --git a/server/djangoapp/populate.py b/server/djangoapp/populate.py index 1927e09e18..09cc361f5e 100644 --- a/server/djangoapp/populate.py +++ b/server/djangoapp/populate.py @@ -1,2 +1,38 @@ +from .models import CarMake, CarModel + def initiate(): - print("Populate not implemented. Add data manually") + car_make_data = [ + {"name":"NISSAN", "description":"Great cars. Japanese technology"}, + {"name":"Mercedes", "description":"Great cars. German technology"}, + {"name":"Audi", "description":"Great cars. German technology"}, + {"name":"Kia", "description":"Great cars. Korean technology"}, + {"name":"Toyota", "description":"Great cars. Japanese technology"}, + ] + + car_make_instances = [] + for data in car_make_data: + car_make_instances.append(CarMake.objects.create(name=data['name'], description=data['description'])) + + + # Create CarModel instances with the corresponding CarMake instances + car_model_data = [ + {"name":"Pathfinder", "type":"SUV", "year": 2023, "car_make":car_make_instances[0]}, + {"name":"Qashqai", "type":"SUV", "year": 2023, "car_make":car_make_instances[0]}, + {"name":"XTRAIL", "type":"SUV", "year": 2023, "car_make":car_make_instances[0]}, + {"name":"A-Class", "type":"SUV", "year": 2023, "car_make":car_make_instances[1]}, + {"name":"C-Class", "type":"SUV", "year": 2023, "car_make":car_make_instances[1]}, + {"name":"E-Class", "type":"SUV", "year": 2023, "car_make":car_make_instances[1]}, + {"name":"A4", "type":"SUV", "year": 2023, "car_make":car_make_instances[2]}, + {"name":"A5", "type":"SUV", "year": 2023, "car_make":car_make_instances[2]}, + {"name":"A6", "type":"SUV", "year": 2023, "car_make":car_make_instances[2]}, + {"name":"Sorrento", "type":"SUV", "year": 2023, "car_make":car_make_instances[3]}, + {"name":"Carnival", "type":"SUV", "year": 2023, "car_make":car_make_instances[3]}, + {"name":"Cerato", "type":"Sedan", "year": 2023, "car_make":car_make_instances[3]}, + {"name":"Corolla", "type":"Sedan", "year": 2023, "car_make":car_make_instances[4]}, + {"name":"Camry", "type":"Sedan", "year": 2023, "car_make":car_make_instances[4]}, + {"name":"Kluger", "type":"SUV", "year": 2023, "car_make":car_make_instances[4]}, + # Add more CarModel instances as needed + ] + + for data in car_model_data: + CarModel.objects.create(name=data['name'], car_make=data['car_make'], type=data['type'], year=data['year']) \ No newline at end of file diff --git a/server/djangoapp/urls.py b/server/djangoapp/urls.py index 0edc274f90..987655c415 100644 --- a/server/djangoapp/urls.py +++ b/server/djangoapp/urls.py @@ -1,15 +1,21 @@ # Uncomment the imports before you add the code -# from django.urls import path +from django.urls import path from django.conf.urls.static import static from django.conf import settings -# from . import views +from . import views + app_name = 'djangoapp' urlpatterns = [ # # path for registration + + path('register/', views.registration, name='register'), # path for login - # path(route='login', view=views.login_user, name='login'), + path(route='login', view=views.login_user, name='login'), + path('logout/', views.logout_request, name='logout'), # Cambia 'logout' por name='logout' + + path(route='get_cars', view=views.get_cars, name ='getcars'), # path for dealer reviews view diff --git a/server/djangoapp/views.py b/server/djangoapp/views.py index b16409f419..bd584b91b1 100644 --- a/server/djangoapp/views.py +++ b/server/djangoapp/views.py @@ -1,19 +1,19 @@ # Uncomment the required imports before adding the code -# from django.shortcuts import render -# from django.http import HttpResponseRedirect, HttpResponse -# from django.contrib.auth.models import User -# from django.shortcuts import get_object_or_404, render, redirect -# from django.contrib.auth import logout -# from django.contrib import messages -# from datetime import datetime - +from django.shortcuts import render +from django.http import HttpResponseRedirect, HttpResponse +from django.contrib.auth.models import User +from django.shortcuts import get_object_or_404, render, redirect +from django.contrib.auth import logout +from django.contrib import messages +from datetime import datetime +from .models import CarMake, CarModel from django.http import JsonResponse from django.contrib.auth import login, authenticate import logging import json from django.views.decorators.csrf import csrf_exempt -# from .populate import initiate +from .populate import initiate # Get an instance of a logger @@ -39,13 +39,31 @@ def login_user(request): return JsonResponse(data) # Create a `logout_request` view to handle sign out request -# def logout_request(request): -# ... +def logout_request(request): + username = request.user.username if request.user.is_authenticated else "" + logout(request) + data = {"userName": username} + return JsonResponse(data) # Create a `registration` view to handle sign up request -# @csrf_exempt -# def registration(request): -# ... +@csrf_exempt +def registration(request): + if request.method == 'POST': + data = json.loads(request.body) + username = data['userName'] + password = data['password'] + first_name = data['firstName'] + last_name = data['lastName'] + email = data['email'] + + # Verificación y creación del usuario + if User.objects.filter(username=username).exists(): + return JsonResponse({"error": "Already Registered"}, status=400) + + user = User.objects.create_user(username=username, first_name=first_name, last_name=last_name, password=password, email=email) + login(request, user) + + return JsonResponse({"userName": username, "status": "Authenticated"}) # # Update the `get_dealerships` view to render the index page with # a list of dealerships @@ -63,3 +81,13 @@ def login_user(request): # Create a `add_review` view to submit a review # def add_review(request): # ... +def get_cars(request): + count = CarMake.objects.filter().count() + print(count) + if(count == 0): + initiate() + car_models = CarModel.objects.select_related('car_make') + cars = [] + for car_model in car_models: + cars.append({"CarModel": car_model.name, "CarMake": car_model.car_make.name}) + return JsonResponse({"CarModels":cars}) \ No newline at end of file diff --git a/server/djangoproj/settings.py b/server/djangoproj/settings.py index e0b1092a5c..12790d942f 100644 --- a/server/djangoproj/settings.py +++ b/server/djangoproj/settings.py @@ -27,9 +27,8 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True - -ALLOWED_HOSTS = [] -CSRF_TRUSTED_ORIGINS = [] +ALLOWED_HOSTS = ['localhost','https://federicomon2-8000.theiadockernext-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai'] +CSRF_TRUSTED_ORIGINS = ['https://federicomon2-8000.theiadockernext-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai'] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [], @@ -61,7 +60,9 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [os.path.join(BASE_DIR, 'frontend/static'), + os.path.join(BASE_DIR,'frontend/build'), + os.path.join(BASE_DIR, 'frontend/build/static'),], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -134,5 +135,9 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' -STATICFILES_DIRS = [] +STATICFILES_DIRS = [ + os.path.join(BASE_DIR, 'frontend/static'), + os.path.join(BASE_DIR, 'frontend/build'), + os.path.join(BASE_DIR, 'frontend/build/static'), +] diff --git a/server/djangoproj/urls.py b/server/djangoproj/urls.py index 6808da9141..e31531f15b 100644 --- a/server/djangoproj/urls.py +++ b/server/djangoproj/urls.py @@ -23,4 +23,8 @@ path('admin/', admin.site.urls), path('djangoapp/', include('djangoapp.urls')), path('', TemplateView.as_view(template_name="Home.html")), + path('about/', TemplateView.as_view(template_name="About.html")), + path('contact/', TemplateView.as_view(template_name="contact.html")), + path('login/', TemplateView.as_view(template_name="index.html")), + path('register/', TemplateView.as_view(template_name="index.html")) ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/server/frontend/package-lock.json b/server/frontend/package-lock.json index 0797425307..b9244446c9 100644 --- a/server/frontend/package-lock.json +++ b/server/frontend/package-lock.json @@ -16,6 +16,9 @@ "react-router-dom": "^6.19.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" + }, + "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -646,9 +649,17 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -1891,6 +1902,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", diff --git a/server/frontend/src/App.js b/server/frontend/src/App.js index aceac6974d..77c8e9cfa6 100644 --- a/server/frontend/src/App.js +++ b/server/frontend/src/App.js @@ -1,11 +1,14 @@ -import LoginPanel from "./components/Login/Login" +import LoginPanel from "./components/Login/Login"; +import Register from "./components/Register/Register"; // Asegúrate de que la ruta sea correcta import { Routes, Route } from "react-router-dom"; function App() { return ( } /> + } /> ); } + export default App; diff --git a/server/frontend/src/components/Register/Register.jsx b/server/frontend/src/components/Register/Register.jsx new file mode 100644 index 0000000000..9f1a290327 --- /dev/null +++ b/server/frontend/src/components/Register/Register.jsx @@ -0,0 +1,98 @@ +import React, { useState } from "react"; +import "./Register.css"; +import user_icon from "../assets/person.png" +import email_icon from "../assets/email.png" +import password_icon from "../assets/password.png" +import close_icon from "../assets/close.png" + +const Register = () => { + + const [userName, setUserName] = useState(""); + const [password, setPassword] = useState(""); + const [email, setEmail] = useState(""); + const [firstName, setFirstName] = useState(""); + const [lastName, setlastName] = useState(""); + + + const gohome = ()=> { + window.location.href = window.location.origin; + } + + const register = async (e) => { + e.preventDefault(); + + let register_url = window.location.origin+"/djangoapp/register"; + + const res = await fetch(register_url, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + "userName": userName, + "password": password, + "firstName":firstName, + "lastName":lastName, + "email":email + }), + }); + + const json = await res.json(); + if (json.status) { + sessionStorage.setItem('username', json.userName); + window.location.href = window.location.origin; + } + else if (json.error === "Already Registered") { + alert("The user with same username is already registered"); + window.location.href = window.location.origin; + } +}; + + return( +
+
+ SignUp +
+ {gohome()}} style={{justifyContent: "space-between", alignItems:"flex-end"}}> + X + +
+
+
+ +
+
+
+ Username + setUserName(e.target.value)}/> +
+
+ First Name + setFirstName(e.target.value)}/> +
+ +
+ Last Name + setlastName(e.target.value)}/> +
+ +
+ Email + setEmail(e.target.value)}/> +
+ +
+ password + setPassword(e.target.value)}/> +
+ +
+
+ +
+
+
+ ) +} + +export default Register; \ No newline at end of file diff --git a/server/frontend/static/About.html b/server/frontend/static/About.html index 484efd960f..c23b259bc8 100644 --- a/server/frontend/static/About.html +++ b/server/frontend/static/About.html @@ -1,6 +1,8 @@ - + + +