diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 05ca50d23b..f0ee775a17 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -1,10 +1,12 @@ name: 'Lint Code' -on: - push: - branches: [master, main] - pull_request: - branches: [master, main] +on: workflow_dispatch + +# on: +# push: +# branches: [master, main] +# pull_request: +# branches: [master, main] jobs: # list of things to do lint_function_js: diff --git a/server/db.sqlite3 b/server/db.sqlite3 new file mode 100644 index 0000000000..b463531559 Binary files /dev/null and b/server/db.sqlite3 differ diff --git a/server/djangoapp/admin.py b/server/djangoapp/admin.py index b1039e16b8..f414dd91bd 100644 --- a/server/djangoapp/admin.py +++ b/server/djangoapp/admin.py @@ -1,13 +1,27 @@ 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 = 6 # CarModelAdmin class +class CarModelAdmin(admin.ModelAdmin): + list_display = ['name', 'dealer_id', 'c_type', 'year'] + search_fields = ['name'] + # CarMakeAdmin class with CarModelInline +class CarMakeAdmin(admin.ModelAdmin): + inlines = [CarModelInline] + list_display = ['name', 'description'] + search_fields = ['name'] # Register models here +admin.site.register(CarMake, CarMakeAdmin) +admin.site.register(CarModel) \ No newline at end of file diff --git a/server/djangoapp/models.py b/server/djangoapp/models.py index 27d96f4eff..7f7e753c58 100644 --- a/server/djangoapp/models.py +++ b/server/djangoapp/models.py @@ -9,6 +9,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(null=False, max_length=30) + description = models.CharField(null=False, max_length=30) + + def __str__(self): + return self.name + "" + self.description # Create a Car Model model `class CarModel(models.Model):`: @@ -19,9 +25,66 @@ # - 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): + TYPES = ( + ("SEDAN", "Sedan"), ("SUV", "SUV"), ("WAGON", "Wagon"), ("LIMOUSINE", "Limousine"), ("BATMOBILE", "Batmobile") + ) + + make = models.ForeignKey(CarMake, on_delete=models.CASCADE) + name = models.CharField(null=False, max_length=30) + c_type = models.CharField(max_length=30, choices=TYPES) + dealer_id = models.IntegerField() + year = models.DateField() + + def __str__(self): + return "Name: " + self.name + \ + " Make Name: "+ self.make.name + \ + " Type: " + self.c_type + \ + " Dealer ID: " + str(self.dealer_id)+ \ + " Year: " + str(self.year) + # 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,sentiment, id): + self.dealership=dealership + self.name=name + self.purchase=purchase + self.review=review + self.purchase_date=purchase_date + self.car_make=car_make + self.car_model=car_model + self.car_year=car_year + self.sentiment=sentiment #Watson NLU service + self.id=id + + def __str__(self): + return "Review: " + self.review +\ + " Sentiment: " + self.sentiment \ No newline at end of file diff --git a/server/djangoapp/restapis.py b/server/djangoapp/restapis.py index b4d13f596a..c5482d11ab 100644 --- a/server/djangoapp/restapis.py +++ b/server/djangoapp/restapis.py @@ -1,34 +1,117 @@ import requests import json # import related models here +from .models import CarDealer, DealerReview from requests.auth import HTTPBasicAuth + # 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): + #print(kwargs) + print("GET from {} ".format(url)) + json_data={} + try: + if "apikey" in kwargs: + response = requests.get(url, headers={'Content-Type':'application/json'}, params=kwargs, auth=HTTPBasicAuth("apikey", kwargs["apikey"])) + else: + response = requests.get(url, headers={'Content-Type':'application/json'}, params=kwargs) + status_code = response.status_code + print("With status {} ".format(status_code)) + json_data = json.loads(response.text) + #print(json_data) + except Exception as e: + print("Error " ,e) + + 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(url) + print(payload) + print(kwargs) + try: + response = requests.post(url, params=kwargs, json=payload) + except Exception as e: + print("Error" ,e) + print("Status Code ", {response.status_code}) + data = json.loads(response.text) + return 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) + #print(json_result) + if json_result: + # Get the row list in JSON as dealers + dealers = json_result["entries"] + # For each dealer object + for dealer_doc in dealers: + # Get its content in `doc` object + #dealer_doc = dealers["doc"] + # 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 +#Coding practice: create a get_dealer_by_id or get_dealers_by_state method in restapis.py. HINT, the only difference from the get_dealers_from_cf method is adding a dealer id or state URL parameter argument when calling the def get_request(url, **kwargs): method such as get_request(url, dealerId=dealerId). # 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, dealer_id): + results = [] + # Call get_request with a URL parameter + json_result = get_request(url, dealerId=dealer_id) + + if "entries" in json_result: + reviews = json_result["entries"] + # For each review object + for review in reviews: + review_obj = DealerReview( + dealership=review["dealership"], + name=review["name"], + purchase=review["purchase"], + review=review["review"], + purchase_date=review["purchase_date"], + car_make=review["car_make"], + car_model=review["car_model"], + car_year=review["car_year"], + sentiment=analyze_review_sentiments(review["review"]), + id=review['id'] + ) + results.append(review_obj) + #print(results[0]) + 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(dealerreview, **kwargs): + API_KEY="MxFCXuNdAY4i7RdB1PTx0LGspyMbNVmVOKxtpJ5XPxkz" + #API_KEY="0614ccd0-1e9f-4d49-923e-e7741f963747:Q3ZX2R1b3oBEb0XebEO99rpulJ31yoY7X5GfjoQykN4RpM9eThYrrs14If0aOHtG" + NLU_URL='https://api.us-south.natural-language-understanding.watson.cloud.ibm.com/instances/93a549ab-8f15-404e-a8ed-97f6fb8a35aa/v1/analyze?version=2020-08-01' + params = json.dumps({"text": dealerreview, "features": {"sentiment": {}}}) + response = requests.post(NLU_URL,data=params,headers={'Content-Type':'application/json'},auth=HTTPBasicAuth("apikey", API_KEY)) + + #print(response.json()) + try: + sentiment=response.json()['sentiment']['document']['label'] + return sentiment + except: + return "neutral" \ No newline at end of file diff --git a/server/djangoapp/templates/djangoapp/about.html b/server/djangoapp/templates/djangoapp/about.html new file mode 100644 index 0000000000..e56fe95374 --- /dev/null +++ b/server/djangoapp/templates/djangoapp/about.html @@ -0,0 +1,45 @@ + + + + + Dealership Review - About + + + + + + + + + + + + + +
+
+

About Us

+
+

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

+
+
+ + \ No newline at end of file diff --git a/server/djangoapp/templates/djangoapp/add_review.html b/server/djangoapp/templates/djangoapp/add_review.html index 768ddf508c..5b9d4fc398 100644 --- a/server/djangoapp/templates/djangoapp/add_review.html +++ b/server/djangoapp/templates/djangoapp/add_review.html @@ -1,16 +1,81 @@ - - - {% load static %} - - - - - - - - + + + Dealership Review - About + + + + + + + + + + + + + +
+
+
+ {% csrf_token %} + +

Review for {{dealer_name}}


+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ +
+
+
\ 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..ad6ca6b088 --- /dev/null +++ b/server/djangoapp/templates/djangoapp/contact.html @@ -0,0 +1,49 @@ + + + + + Dealership Review - About + + + + + + + + + + + + + +
+
+

Contact Details

+
+

+ Address: New Jersy, USA +

+

+ Contact Number: +1 521 56424 +

+
+
+ + + diff --git a/server/djangoapp/templates/djangoapp/dealer_details.html b/server/djangoapp/templates/djangoapp/dealer_details.html index 25bd9a223d..f771cea718 100644 --- a/server/djangoapp/templates/djangoapp/dealer_details.html +++ b/server/djangoapp/templates/djangoapp/dealer_details.html @@ -1,18 +1,119 @@ - - - Dealership Review - {% load static %} - - + + + Dealership Review - About + + + + + + + + + + + + + - +
+
+ {% if reviews %} +
+ {% for review in reviews %} +
- - - - - +
+
+ +
+ +
+
{{review.name}}
+

{{review.review}}

+
+ +
+
+ {% endfor %} +
+
+
Write Your Own Review!
+
+
+

Want to review this dealership?

+

Click the button below!

+
+ + +
+
+ {% else %} +
+
+
No Reviews
+
+
+ + +

There are no reviews for this dealership. Be the first to write one!

+
+ + +
+ {% endif %} +
+
+ \ No newline at end of file diff --git a/server/djangoapp/templates/djangoapp/index.html b/server/djangoapp/templates/djangoapp/index.html index 1a9ee6e39a..203f9d4726 100644 --- a/server/djangoapp/templates/djangoapp/index.html +++ b/server/djangoapp/templates/djangoapp/index.html @@ -1,25 +1,85 @@ - - - Dealership Review - - - - - - - - - - - - - This is the index page of your Django app! - - + + + Dealership Review - About + + + + + + + + + + + + + - +
+
+ + + + + + + + + + + + + {% 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..461d9fe829 100644 --- a/server/djangoapp/templates/djangoapp/registration.html +++ b/server/djangoapp/templates/djangoapp/registration.html @@ -1,11 +1,75 @@ - - - {% load static %} - - - + + + Dealership Review - About + + + + + + +
+
+

Sign Up

+
+
+ {% csrf_token %} +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/server/djangoapp/urls.py b/server/djangoapp/urls.py index 37b1c89d01..5580cdcd1a 100644 --- a/server/djangoapp/urls.py +++ b/server/djangoapp/urls.py @@ -10,19 +10,24 @@ # name the URL # 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="register", view=views.registration_request, name="register"), # path for login + path(route="login", view=views.login_request, name="login"), # path for logout + path(route='logout', view=views.logout_request, name="logout"), path(route='', view=views.get_dealerships, name='index'), # path for dealer reviews view + path('dealer//', view=views.get_dealer_details, name='dealer_details'), # path for add a review view - + path(route='addreview//', 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..1d6f297bc9 100644 --- a/server/djangoapp/views.py +++ b/server/djangoapp/views.py @@ -3,7 +3,9 @@ from django.contrib.auth.models import User from django.shortcuts import get_object_or_404, render, redirect # from .models import related models +from .models import CarDealer, DealerReview, CarModel, CarMake # from .restapis import related methods +from .restapis import get_dealers_from_cf,get_dealer_reviews_from_cf,post_request from django.contrib.auth import login, logout, authenticate from django.contrib import messages from datetime import datetime @@ -18,37 +20,132 @@ # 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 = {} + if request.method == "POST": + username = request.POST['username'] + password = request.POST['password'] + + user = authenticate(username=username,password=password) + + if user is not None: + login(request, user) + return redirect('djangoapp:index') # Create a `logout_request` view to handle sign out request -# def logout_request(request): -# ... +def logout_request(request): + logout(request) + return redirect('djangoapp:index') # Create a `registration_request` view to handle sign up request -# def registration_request(request): -# ... +def registration_request(request): + context = {} + if request.method == 'GET': + return render(request, 'djangoapp/registration.html', context) + + if request.method == "POST": + username = request.POST['username'] + password = request.POST['password'] + first_name = request.POST['firstname'] + last_name = request.POST['lastname'] + user_exist = False + try: + User.objects.get(username=username) + user_exist = True + except: + logger.debug("{} is new user".format(username)) + + if not user_exist: + user = User.objects.create_user(username=username, first_name=first_name, last_name=last_name, + password=password) + login(request, user) + return redirect("djangoapp:index") # Update the `get_dealerships` view to render the index page with a list of dealerships def get_dealerships(request): - context = {} if request.method == "GET": + context={} + url = "https://08663624.us-south.apigw.appdomain.cloud/api/dealership" + # Get dealers from the URL + dealerships = get_dealers_from_cf(url) + # Concat all dealer's short name + 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, dealer_id): + context={} + url = "https://08663624.us-south.apigw.appdomain.cloud/api/review" + #print(dealer_id) + # Get dealers from the URL + dealer_details = get_dealer_reviews_from_cf(url,dealer_id) + context["dealer_id"]=dealer_id + context["reviews"]=dealer_details + 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, dealer_id): + context = {} + # If it is a GET request, just render the add_review page + if request.method == 'GET': + url = "https://08663624.us-south.apigw.appdomain.cloud/api/dealership" + # Get dealers from the URL + context = { + "dealer_id": dealer_id, + "dealer_name": get_dealers_from_cf(url)[dealer_id-1].full_name, + "cars": CarModel.objects.all() + } + #print(context) + return render(request, 'djangoapp/add_review.html', context) + elif request.method == 'POST': + if (request.user.is_authenticated): + review = dict() + review["id"]=0#placeholder + review["name"]=request.POST["name"] + review["dealership"]=dealer_id + review["review"]=request.POST["content"] + if ("purchasecheck" in request.POST): + review["purchase"]=True + else: + review["purchase"]=False + print(request.POST["car"]) + if review["purchase"] == True: + car_parts=request.POST["car"].split("|") + review["purchase_date"]=request.POST["purchase_date"] + review["car_make"]=car_parts[0] + review["car_model"]=car_parts[1] + review["car_year"]=car_parts[2] + + else: + review["purchase_date"]=None + review["car_make"]=None + review["car_model"]=None + review["car_year"]=None + json_result = post_request("https://08663624.us-south.apigw.appdomain.cloud/api/review", review, dealerId=dealer_id) + print(json_result) + if "error" in json_result: + context["message"] = "ERROR: Review was not submitted." + else: + context["message"] = "Review was submited" + return redirect("djangoapp:dealer_details", dealer_id=dealer_id) \ No newline at end of file diff --git a/server/manage.py b/server/manage.py old mode 100755 new mode 100644