diff --git a/functions/get-dealership.js b/functions/get-dealership.js new file mode 100644 index 0000000000..a0a7df9e8d --- /dev/null +++ b/functions/get-dealership.js @@ -0,0 +1,52 @@ +const express = require('express'); +const app = express(); +const port = process.env.PORT || 3000; +const Cloudant = require('@cloudant/cloudant'); +// Initialize Cloudant connection with IAM authentication +async function dbCloudantConnect() { + try { + const cloudant = Cloudant({ + plugins: { iamauth: { iamApiKey: 'r6c5foCGS8FS4_7b-CGoKf_w6MMRMK9K5PzXjn4GdmSK' } }, // Replace with your IAM API key + url: 'https://8715b253-759d-4f15-b10e-c0d9e133d048-bluemix.cloudantnosqldb.appdomain.cloud', // Replace with your Cloudant URL + }); + const db = cloudant.use('dealerships'); + console.info('Connect success! Connected to DB'); + return db; + } catch (err) { + console.error('Connect failure: ' + err.message + ' for Cloudant DB'); + throw err; + } +} +let db; +(async () => { + db = await dbCloudantConnect(); +})(); +app.use(express.json()); +// Define a route to get all dealerships with optional state and ID filters +app.get('/dealerships/get', (req, res) => { + const { state, id } = req.query; + // Create a selector object based on query parameters + const selector = {}; + if (state) { + selector.state = state; + } + if (id) { + selector._id = id; + } + const queryOptions = { + selector, + limit: 10, // Limit the number of documents returned to 10 + }; + db.find(queryOptions, (err, body) => { + if (err) { + console.error('Error fetching dealerships:', err); + res.status(500).json({ error: 'An error occurred while fetching dealerships.' }); + } else { + const dealerships = body.docs; + res.json(dealerships); + } + }); +}); +app.listen(port, () => { + console.log(`Server is running on port ${port}`); +}); diff --git a/functions/reviews.py b/functions/reviews.py new file mode 100644 index 0000000000..376a2b885e --- /dev/null +++ b/functions/reviews.py @@ -0,0 +1,72 @@ +from cloudant.client import Cloudant +from cloudant.query import Query +from flask import Flask, jsonify, request +import atexit + +#Add your Cloudant service credentials here +cloudant_username = '8715b253-759d-4f15-b10e-c0d9e133d048-bluemix' +cloudant_api_key = 'r6c5foCGS8FS4_7b-CGoKf_w6MMRMK9K5PzXjn4GdmSK' +cloudant_url = 'https://8715b253-759d-4f15-b10e-c0d9e133d048-bluemix.cloudantnosqldb.appdomain.cloud' +client = Cloudant.iam(cloudant_username, cloudant_api_key, connect=True, url=cloudant_url) + +session = client.session() +print('Databases:', client.all_dbs()) + +db = client['reviews'] + +app = Flask(__name__) + +@app.route('/api/get_reviews', methods=['GET']) +def get_reviews(): + dealership_id = request.args.get('id') + + # Check if "id" parameter is missing + if dealership_id is None: + return jsonify({"error": "Missing 'id' parameter in the URL"}), 400 + + # Convert the "id" parameter to an integer (assuming "id" should be an integer) + try: + dealership_id = int(dealership_id) + except ValueError: + return jsonify({"error": "'id' parameter must be an integer"}), 400 + + # Define the query based on the 'dealership' ID + selector = { + 'dealership': dealership_id + } + + # Execute the query using the query method + result = db.get_query_result(selector) + + # Create a list to store the documents + data_list = [] + + # Iterate through the results and add documents to the list + for doc in result: + data_list.append(doc) + + # Return the data as JSON + return jsonify(data_list) + + +@app.route('/api/post_review', methods=['POST']) +def post_review(): + if not request.json: + abort(400, description='Invalid JSON data') + + # Extract review data from the request JSON + review_data = request.json + + # Validate that the required fields are present in the review data + required_fields = ['id', 'name', 'dealership', 'review', 'purchase', 'purchase_date', 'car_make', 'car_model', 'car_year'] + for field in required_fields: + if field not in review_data: + abort(400, description=f'Missing required field: {field}') + + # Save the review data as a new document in the Cloudant database + db.create_document(review_data) + + return jsonify({"message": "Review posted successfully"}), 201 + +if __name__ == '__main__': + app.run(debug=True) diff --git a/server/db.sqlite3 b/server/db.sqlite3 new file mode 100644 index 0000000000..28f72c9eb6 Binary files /dev/null and b/server/db.sqlite3 differ diff --git a/server/djangoapp/admin.py b/server/djangoapp/admin.py index b1039e16b8..0305dae32b 100644 --- a/server/djangoapp/admin.py +++ b/server/djangoapp/admin.py @@ -1,13 +1,22 @@ from django.contrib import admin # from .models import related models - - +from .models import CarMake,CarModel # Register your models here. # CarModelInline class +class CarModelInline(admin.StackedInline): + model = CarModel + extra = 5 # CarModelAdmin class - +class CarModelAdmin(admin.ModelAdmin): + list_display = ('name', "id", "type", "year", ) + list_filter = ['id'] + search_fields = ['name'] # CarMakeAdmin class with CarModelInline - +class CarMakeAdmin(admin.ModelAdmin): + inlines=[CarModelInline] + list_display=("name", "description") # Register models here +admin.site.register(CarModel,CarModelAdmin) +admin.site.register(CarMake,CarMakeAdmin) diff --git a/server/djangoapp/models.py b/server/djangoapp/models.py index 27d96f4eff..33d68d8441 100644 --- a/server/djangoapp/models.py +++ b/server/djangoapp/models.py @@ -3,12 +3,20 @@ # Create your models here. - +CHOICES = [ + ("SUv", "SUV"), + ("Wagon", "Wagon"), +] # Create a Car Make model `class CarMake(models.Model)`: # - Name # - 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(null=False, max_length=30, default='new car make') + description = models.CharField(null=False, max_length=500) + def __str__(self): + return "Car Make:" + self.name # Create a Car Model model `class CarModel(models.Model):`: @@ -19,9 +27,54 @@ # - Year (DateField) # - Any other fields you would like to include in car model # - __str__ method to print a car make object - +class CarModel(models.Model): + make = models.ForeignKey(CarMake, on_delete=models.CASCADE) + name = models.CharField(null=False, max_length=30, default='new car model') + id = models.IntegerField(default=1,primary_key=True) + type = models.CharField(null=False, max_length=100, choices=CHOICES) + year = models.DateField(default=now) + description = models.CharField(null=False, max_length=500) + def __str__(self): + return "Name:" + self.name + "Description:" + self.description # Create a plain Python class `CarDealer` to hold dealer data +class CarDealer: + def __init__(self, address, city, full_name, id, lat, long, short_name, st, zip): + # Dealer address + self.address = address + # Dealer city + self.city = city + # Dealer Full Name + self.full_name = full_name + # Dealer id + self.id = id + # Location lat + self.lat = lat + # Location long + self.long = long + # Dealer short name + self.short_name = short_name + # Dealer state + self.st = st + # Dealer zip + self.zip = zip + def __str__(self): + return "Dealer name: " + self.full_name # Create a plain Python class `DealerReview` to hold review data +class DealerReview: + def __init__(self, dealership, name, purchase, review, purchase_date, car_make, car_model, car_year, id): + self.dealership = dealership + self.name = name + self.purchase = purchase + self.review = review + self.purchase_date = "" + self.car_make = "" + self.car_model = "" + self.car_year = "" + self.sentiment = "" + self.id = "" + def __str__(self): + return "Dealer name: " + self.name + diff --git a/server/djangoapp/restapis.py b/server/djangoapp/restapis.py index b4d13f596a..188158b4fb 100644 --- a/server/djangoapp/restapis.py +++ b/server/djangoapp/restapis.py @@ -1,34 +1,148 @@ import requests import json # import related models here +from .models import CarDealer, DealerReview from requests.auth import HTTPBasicAuth - +from ibm_cloud_sdk_core.authenticators import IAMAuthenticator +from ibm_watson import NaturalLanguageUnderstandingV1 +from ibm_watson.natural_language_understanding_v1 import Features,SentimentOptions +import time # Create a `get_request` to make HTTP GET requests # e.g., response = requests.get(url, params=params, headers={'Content-Type': 'application/json'}, # auth=HTTPBasicAuth('apikey', api_key)) +def get_request(url, **kwargs): + + # If argument contain API KEY + api_key = kwargs.get("api_key") + print("GET from {} ".format(url)) + try: + if api_key: + params = dict() + params["text"] = kwargs["text"] + params["version"] = kwargs["version"] + params["features"] = kwargs["features"] + params["return_analyzed_text"] = kwargs["return_analyzed_text"] + response = requests.get(url, params=params, headers={'Content-Type': 'application/json'}, + auth=HTTPBasicAuth('apikey', api_key)) + else: + # Call get method of requests library with URL and parameters + response = requests.get(url, headers={'Content-Type': 'application/json'}, + params=kwargs) + except: + # If any error occurs + print("Network exception occurred") + status_code = response.status_code + print("With status {} ".format(status_code)) + json_data = json.loads(response.text) + return json_data # Create a `post_request` to make HTTP POST requests # e.g., response = requests.post(url, params=kwargs, json=payload) - +def post_request(url, payload, **kwargs): + print(kwargs) + print("POST to {} ".format(url)) + print(payload) + response = requests.post(url, params=kwargs, json=payload) + status_code = response.status_code + print("With status {} ".format(status_code)) + json_data = json.loads(response.text) + return json_data # Create a get_dealers_from_cf method to get dealers from a cloud function # def get_dealers_from_cf(url, **kwargs): # - Call get_request() with specified arguments # - Parse JSON results into a CarDealer object list +def get_dealers_from_cf(url, **kwargs): + results = [] + # Call get_request with a URL parameter + json_result = get_request(url) + if json_result: + # Get the row list in JSON as dealers + dealers = json_result + # For each dealer object + for dealer in dealers: + # Get its content in `doc` object + dealer_doc = dealer + # Create a CarDealer object with values in `doc` object + dealer_obj = CarDealer(address=dealer_doc["address"], city=dealer_doc["city"], full_name=dealer_doc["full_name"], + id=dealer_doc["id"], lat=dealer_doc["lat"], long=dealer_doc["long"], + short_name=dealer_doc["short_name"], + st=dealer_doc["st"], zip=dealer_doc["zip"]) + results.append(dealer_obj) + return results +def get_dealer_by_id_from_cf(url, id, **kwargs): + results = [] + json_result = get_request(url, id=id) + if json_result: + for dealer in json_result: + if (dealer["id"] == dealerId): + dealer_obj = CarDealer(address=dealer_doc["address"], city=dealer_doc["city"], full_name=dealer_doc["full_name"], + id=dealer_doc["id"], lat=dealer_doc["lat"], long=dealer_doc["long"], + short_name=dealer_doc["short_name"], + st=dealer_doc["st"], zip=dealer_doc["zip"]) + results.append(dealer_obj) + return results # Create a get_dealer_reviews_from_cf method to get reviews by dealer id from a cloud function -# def get_dealer_by_id_from_cf(url, dealerId): # - Call get_request() with specified arguments # - Parse JSON results into a DealerView object list +def get_dealer_reviews_from_cf(url, **kwargs): + results = [] + id = kwargs.get("id") + if id: + json_result = get_request(url, id=id) + else: + json_result = get_request(url) + # print(json_result) + if json_result: + print("line 105",json_result) + reviews = json_result + for dealer_review in reviews: + review_obj = DealerReview(dealership=dealer_review["dealership"], + name=dealer_review["name"], + purchase=dealer_review["purchase"], + review=dealer_review["review"], + purchase_date=dealer_review["purchase_date"], + car_year=dealer_review["car_year"], + car_make=dealer_review["car_make"], + car_model=dealer_review["car_model"], + id=dealer_review["id"] + ) + if "id" in dealer_review: + review_obj.id = dealer_review["id"] + if "purchase_date" in dealer_review: + review_obj.purchase_date = dealer_review["purchase_date"] + if "car_make" in dealer_review: + review_obj.car_make = dealer_review["car_make"] + if "car_model" in dealer_review: + review_obj.car_model = dealer_review["car_model"] + if "car_year" in dealer_review: + review_obj.car_year = dealer_review["car_year"] + + sentiment = analyze_review_sentiments(review_obj.review) + print(sentiment) + review_obj.sentiment = sentiment + results.append(review_obj) - + return results # Create an `analyze_review_sentiments` method to call Watson NLU and analyze text # def analyze_review_sentiments(text): # - Call get_request() with specified arguments # - Get the returned sentiment label such as Positive or Negative - +def analyze_review_sentiments(text): + url = "https://api.us-east.natural-language-understanding.watson.cloud.ibm.com/instances/82a35a10-4051-4212-abd4-a1cd904ec957" + api_key = "5ZftXhylD7h-NR5ACWviXM1TRl2WOmhzoe9nXDXBZioq" + authenticator = IAMAuthenticator(api_key) + natural_language_understanding = NaturalLanguageUnderstandingV1(version='2021-08-01',authenticator=authenticator) + natural_language_understanding.set_service_url(url) + response = natural_language_understanding.analyze( text=text+"hello hello hello",features=Features(sentiment=SentimentOptions(targets=[text+"hello hello hello"]))).get_result() + label=json.dumps(response, indent=2) + label = response['sentiment']['document']['label'] + + + return(label) diff --git a/server/djangoapp/templates/djangoapp/about.html b/server/djangoapp/templates/djangoapp/about.html new file mode 100644 index 0000000000..95606f5579 --- /dev/null +++ b/server/djangoapp/templates/djangoapp/about.html @@ -0,0 +1,49 @@ + + + + + Dealership Review + + + + + + + + + + + + +

+ Welcome to Best Cars dealership, home to the best cars in North America. We sell domestic and imported cars at reasonable prices. +

+ diff --git a/server/djangoapp/templates/djangoapp/add_review.html b/server/djangoapp/templates/djangoapp/add_review.html index 768ddf508c..f68a29211d 100644 --- a/server/djangoapp/templates/djangoapp/add_review.html +++ b/server/djangoapp/templates/djangoapp/add_review.html @@ -12,5 +12,20 @@ +
+ + + + + + \ No newline at end of file diff --git a/server/djangoapp/templates/djangoapp/contact.html b/server/djangoapp/templates/djangoapp/contact.html new file mode 100644 index 0000000000..db966ac2b7 --- /dev/null +++ b/server/djangoapp/templates/djangoapp/contact.html @@ -0,0 +1,51 @@ + + + + + Dealership Review + + + + + + + + + + +
+
    +
  • Person #1: 123-456-7890
  • +
  • Person #2: 098-765-4321
  • +
  • Person #3: 102-938-4756
  • +
+ + \ No newline at end of file diff --git a/server/djangoapp/templates/djangoapp/dealer_details.html b/server/djangoapp/templates/djangoapp/dealer_details.html index 25bd9a223d..fab223a5b0 100644 --- a/server/djangoapp/templates/djangoapp/dealer_details.html +++ b/server/djangoapp/templates/djangoapp/dealer_details.html @@ -10,9 +10,56 @@ + - +
+ {% for review in review_list %} +
+ {% if review.sentiment == "positive" %} + + {% elif review.sentiment == "negative" %} + + {% else %} + + {% endif %} +
+
{{review.car_model}}, {{review.car_make}}
+
{{review.purchase_date}}
+

{{review.review}}

+
+
+ {% endfor %} +
\ No newline at end of file diff --git a/server/djangoapp/templates/djangoapp/index.html b/server/djangoapp/templates/djangoapp/index.html index 1a9ee6e39a..152921bae9 100644 --- a/server/djangoapp/templates/djangoapp/index.html +++ b/server/djangoapp/templates/djangoapp/index.html @@ -12,14 +12,67 @@ - - - This is the index page of your Django app! - + - + + + + + + + + + + ... + + + + {% for dealer in dealership_list %} + + + + + + + + + {% endfor %} + +
IDDealer NameCityAddressZipState
{{dealer.id}}{{dealer.full_name}}{{dealer.city}}{{dealer.address}}{{dealer.zip}}{{dealer.st}}
- + diff --git a/server/djangoapp/templates/djangoapp/registration.html b/server/djangoapp/templates/djangoapp/registration.html index ae11ea4b71..00622fa5b6 100644 --- a/server/djangoapp/templates/djangoapp/registration.html +++ b/server/djangoapp/templates/djangoapp/registration.html @@ -6,6 +6,28 @@ - +
+
+ {% csrf_token %} +

Sign Up

+
+
+ + + + + + + + + {% if message %} +
+ {{ message }} +
+ {% endif %} + +
+
+
\ No newline at end of file diff --git a/server/djangoapp/urls.py b/server/djangoapp/urls.py index 37b1c89d01..7f8206239e 100644 --- a/server/djangoapp/urls.py +++ b/server/djangoapp/urls.py @@ -8,21 +8,27 @@ # route is a string contains a URL pattern # view refers to the view function # name the URL + path(route='', view=views.get_dealerships, name='index'), # path for about view + path(route='about', view=views.about, name='about'), # path for contact us view + path(route='contact', view=views.contact, name='contact'), # path for registration + path(route='registration', view=views.registration_request, name='registration'), # path for login + path(route='login', view=views.login_request, name='login'), # path for logout - - path(route='', view=views.get_dealerships, name='index'), + path(route='logout', view=views.logout_request, name='logout'), # path for dealer reviews view + path(route='dealer//', view=views.get_dealer_details, name='dealer_details'), # path for add a review view + path(route='dealer//review', view=views.add_review, name='add_review') ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/server/djangoapp/views.py b/server/djangoapp/views.py index 61cc664da0..54a649b777 100644 --- a/server/djangoapp/views.py +++ b/server/djangoapp/views.py @@ -6,6 +6,8 @@ # from .restapis import related methods from django.contrib.auth import login, logout, authenticate from django.contrib import messages +from .restapis import get_request, post_request, get_dealers_from_cf, get_dealer_by_id_from_cf, get_dealer_reviews_from_cf +from .models import CarMake, CarModel from datetime import datetime import logging import json @@ -18,37 +20,144 @@ # Create an `about` view to render a static about page -# def about(request): -# ... +def about(request): + context = {} + if request.method == "GET": + return render(request, 'djangoapp/about.html', context) # Create a `contact` view to return a static contact page -#def contact(request): +def contact(request): + context = {} + if request.method == "GET": + return render(request, 'djangoapp/contact.html', context) + # Create a `login_request` view to handle sign in request -# def login_request(request): -# ... +def login_request(request): + context = {} + # Handles POST request + if request.method == "POST": + # Get username and password from request.POST dictionary + username = request.POST['username'] + password = request.POST['psw'] + # Try to check if provide credential can be authenticated + user = authenticate(username=username, password=password) + if user is not None: + # If user is valid, call login method to login current user + login(request, user) + return redirect('djangoapp:index') + else: + # If not, return to login page again + return render(request, 'djangoapp/login.html', context) + else: + return render(request, 'djangoapp/login.html', context) # Create a `logout_request` view to handle sign out request -# def logout_request(request): -# ... +def logout_request(request): + print("Log out the user `{}`".format(request.user.username)) + # Logout user in the request + logout(request) + # Redirect user back to course list view + return redirect('djangoapp:index') # Create a `registration_request` view to handle sign up request -# def registration_request(request): -# ... +def registration_request(request): + context = {} + # If it is a GET request, just render the registration page + if request.method == 'GET': + return render(request, 'djangoapp/registration.html', context) + # If it is a POST request + elif request.method == 'POST': + # Get user information from request.POST + username = request.POST['username'] + password = request.POST['psw'] + first_name = request.POST['firstname'] + last_name = request.POST['lastname'] + user_exist = False + try: + # Check if user already exists + User.objects.get(username=username) + user_exist = True + except: + # If not, simply log this is a new user + logger.debug("{} is new user".format(username)) + # If it is a new user + if not user_exist: + # Create user in auth_user table + user = User.objects.create_user(username=username, first_name=first_name, last_name=last_name, + password=password) + # Login the user and redirect to course list page + login(request, user) + return redirect("djangoapp:index") + else: + return render(request, 'djangoapp/registration.html', context) # Update the `get_dealerships` view to render the index page with a list of dealerships def get_dealerships(request): context = {} if request.method == "GET": + url = "https://mwu0614-3000.theiadocker-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai/dealerships/get" + # Get dealers from the URL + dealerships = get_dealers_from_cf(url) + context["dealership_list"] = dealerships + # Return a list of dealer short name return render(request, 'djangoapp/index.html', context) - # Create a `get_dealer_details` view to render the reviews of a dealer -# def get_dealer_details(request, dealer_id): -# ... +def get_dealer_details(request, id): + context = {} + if request.method == "GET": + dealer_url = "https://mwu0614-3000.theiadocker-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai/dealerships/get" + dealer = get_dealer_by_id_from_cf(dealer_url, id=id) + context["dealer"] = dealer + review_url = "https://mwu0614-5000.theiadocker-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai/api/get_reviews?id=" + str(id) + reviews = get_dealer_reviews_from_cf(review_url) + context["review_list"] = reviews + return render(request, 'djangoapp/dealer_details.html', context) + # Create a `add_review` view to submit a review -# def add_review(request, dealer_id): -# ... +def add_review(request, id): + context = {} + dealer_url = "https://mwu0614-3000.theiadocker-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai/dealerships/get" + dealer = get_dealers_from_cf(dealer_url) + context["dealer"] = dealer + if request.method == 'GET': + # Get cars for the dealer + cars = CarModel.objects.filter(dealer_id=id) + print(cars) + context["cars"] = cars + return render(request, 'djangoapp/add_review.html', context) + elif request.method == 'POST': + if request.user.is_authenticated: + username = request.user.username + print(request.POST) + payload = dict() + car_id = request.POST["car"] + car = CarModel.objects.get(pk=car_id) + payload["time"] = datetime.utcnow().isoformat() + payload["name"] = username + payload["dealership"] = dealer_id + payload["id"] = dealer_id + payload["review"] = request.POST["content"] + payload["purchase"] = False + if "purchasecheck" in request.POST: + if request.POST["purchasecheck"] == 'on': + payload["purchase"] = True + payload["purchase_date"] = request.POST["purchasedate"] + payload["car_make"] = car.make.name + payload["car_model"] = car.name + payload["car_year"] = int(car.year.strftime("%Y")) + + new_payload = {} + new_payload["review"] = payload + review_post_url = "https://mwu0614-5000.theiadocker-1-labs-prod-theiak8s-4-tor01.proxy.cognitiveclass.ai/api/get_reviews?id=" + str(dealer_id) + post_request(review_post_url, new_payload, id=id) + return redirect("djangoapp:dealer_details", id=id) + + + + +