Skip to content

Commit

Permalink
#57 #118 #110 Adding and removing bookmarks, displaying reviews on us…
Browse files Browse the repository at this point in the history
…er screen and login bug fix (#117)

Co-authored-by: aakashshankar <[email protected]>
  • Loading branch information
shub-garg and ahhcash authored Nov 6, 2024
1 parent c2c8099 commit 9c65a64
Show file tree
Hide file tree
Showing 13 changed files with 719 additions and 231 deletions.
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[flake8]
max-line-length = 140
ignore = E501, W503
ignore = E501, W503, E203
49 changes: 49 additions & 0 deletions db-prep/create_bookmarks_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env python3

import sys
import boto3


def create_bookmarks_table(dynamodb, table_name):
table = dynamodb.create_table(
TableName=table_name,
KeySchema=[{"AttributeName": "BookmarkId", "KeyType": "HASH"}],
AttributeDefinitions=[
{"AttributeName": "BookmarkId", "AttributeType": "S"},
{"AttributeName": "UserId", "AttributeType": "S"},
{"AttributeName": "ServiceId", "AttributeType": "S"},
],
GlobalSecondaryIndexes=[
{
"IndexName": "UserBookmarksIndex",
"KeySchema": [
{"AttributeName": "UserId", "KeyType": "HASH"},
{"AttributeName": "ServiceId", "KeyType": "RANGE"},
],
"Projection": {"ProjectionType": "ALL"},
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
}
],
ProvisionedThroughput={"ReadCapacityUnits": 5, "WriteCapacityUnits": 5},
)

# Wait for the table to be created
table.meta.client.get_waiter("table_exists").wait(TableName=table_name)
print("Bookmarks table has been created successfully.")
return table


def main():
if len(sys.argv) != 2:
print("Usage: python3 create_bookmarks_table.py <dynamoDB table name>")
exit(1)
table_name = sys.argv[1]
dynamodb = boto3.resource("dynamodb", region_name="us-east-1")
create_bookmarks_table(dynamodb, table_name)


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions src/accounts/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ def clean(self):
if user is None:
raise forms.ValidationError("Invalid username/email or password.")
else:
if user.user_type != "user":
raise forms.ValidationError("This page is for users only.")
self.confirm_login_allowed(user)
self.user_cache = user
else:
Expand Down
174 changes: 108 additions & 66 deletions src/accounts/templates/profile_base.html
Original file line number Diff line number Diff line change
@@ -1,76 +1,118 @@
{% load widget_tweaks %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Seeker Profile</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
<script>
function toggleEdit() {
var viewSection = document.getElementById('view-profile');
var editSection = document.getElementById('edit-profile');
viewSection.classList.toggle('hidden');
editSection.classList.toggle('hidden');
}
</script>
</head>
<body class="bg-gray-100">
<div class="max-w-4xl mx-auto mt-10">
<h1 class="text-3xl font-semibold text-center text-gray-800 mb-8">Service Seeker Profile</h1>

<!-- Profile View Section -->
<div id="view-profile" class="bg-white shadow-md rounded-lg p-6">
<h2 class="text-xl font-semibold text-gray-700">Username: {{ profile.username }}</h2>
<h2 class="text-xl font-semibold text-gray-700">Email: {{ profile.email }}</h2>
<h2 class="text-xl font-semibold text-gray-700">First Name: {{ profile.first_name }}</h2>
<h2 class="text-xl font-semibold text-gray-700">Last Name: {{ profile.last_name }}</h2>
<ul>
{% for service in profile.bookmarked_services.all %}
<li>{{ service.title }} by {{ service.provider.business_name }}</li>
{% endfor %}
</ul>
{% extends 'base.html' %}
{% block title %}Service Seeker Profile{% endblock %}
{% block content %}
<div class="container mx-auto px-6 py-12 pb-32 mb-32 max-w-7xl" style="min-width: 1000px;">
<div class="mb-10 text-center mt-16">
<h1 class="text-4xl font-bold text-gray-800 mb-3">Profile</h1>
<p class="text-gray-600 text-lg">Manage your account information and activities</p>
</div>

<button onclick="toggleEdit()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 mt-4 rounded focus:outline-none">
Edit Profile
</button>
<!-- Profile View Section -->
<div id="view-profile" class="bg-white rounded-lg shadow-md p-6">
<div class="flex items-center mb-6">
<div class="w-24 h-24 rounded-full bg-blue-500 text-white flex items-center justify-center text-4xl font-bold mr-6 uppercase">
{{ profile.username|first }}
</div>
<div>
<h2 class="text-xl font-semibold text-blue-700 mb-2">Profile Information</h2>
<p class="text-gray-600"><strong>Username:</strong> {{ profile.username }}</p>
<p class="text-gray-600"><strong>Email:</strong> {{ profile.email }}</p>
<p class="text-gray-600"><strong>First Name:</strong> {{ profile.first_name }}</p>
<p class="text-gray-600"><strong>Last Name:</strong> {{ profile.last_name }}</p>
</div>
</div>

<!-- Profile Edit Form Section (Initially Hidden) -->
<div id="edit-profile" class="bg-white shadow-md rounded-lg p-6 hidden">
<form method="POST">
{% csrf_token %}

<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">Username</label>
{{ form.username|add_class:"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" }}
<div class="mb-8">
<h2 class="text-xl font-semibold text-blue-700 mb-4">Bookmarked Services</h2>
{% if bookmarks %}
<div class="grid gap-4">
{% for service in bookmarks %}
<div class="bg-gray-50 rounded-lg p-4">
<p class="font-semibold text-gray-800">{{ service.Name }}</p>
<p class="text-gray-600">{{ service.Address }}</p>
<p class="text-gray-600">{{ service.Category }}</p>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-gray-500">No bookmarks yet.</p>
{% endif %}
</div>

<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">Email</label>
{{ form.email|add_class:"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" }}
<div class="mb-8">
<h2 class="text-xl font-semibold text-blue-700 mb-4">My Reviews</h2>
{% if reviews %}
<div class="grid gap-4">
{% for review in reviews %}
<div class="bg-gray-50 rounded-lg p-4">
<p class="font-semibold text-gray-800">{{ review.ServiceName }}</p>
<p class="text-gray-600">Rating: {{ review.RatingStars }} stars</p>
<p class="text-gray-600">{{ review.RatingMessage }}</p>
<p class="text-sm text-gray-500">{{ review.Timestamp|date:"M d, Y" }}</p>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-gray-500">No reviews yet.</p>
{% endif %}
</div>

<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">First Name</label>
{{ form.first_name|add_class:"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" }}
</div>
<button onclick="toggleEdit()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition-colors duration-300">
Edit Profile
</button>
</div>

<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2">Last Name</label>
{{ form.last_name|add_class:"shadow appearance-none border rounded w-full py-2 px-3 text-gray-700" }}
</div>
<!-- Profile Edit Form Section -->
<div id="edit-profile" class="bg-white rounded-lg shadow-md p-6 hidden">
<h2 class="text-xl font-semibold text-blue-700 mb-4">Edit Profile</h2>
<form method="POST" class="space-y-4">
{% csrf_token %}
<div>
<label class="block text-gray-700 font-medium mb-2" for="username">Username</label>
<input type="text" id="username" name="username" value="{{ profile.username }}"
class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>

<div class="flex items-center justify-between">
<button type="submit" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded focus:outline-none">
Save Changes
</button>
<button type="button" onclick="toggleEdit()" class="text-gray-500 hover:text-gray-700">
Cancel
</button>
</div>
</form>
</div>
<!-- Repeat for other form fields -->
<div>
<label class="block text-gray-700 font-medium mb-2" for="email">Email</label>
<input type="email" id="email" name="email" value="{{ profile.email }}"
class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>

<div>
<label class="block text-gray-700 font-medium mb-2" for="first_name">First Name</label>
<input type="text" id="first_name" name="first_name" value="{{ profile.first_name }}"
class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>

<div>
<label class="block text-gray-700 font-medium mb-2" for="last_name">Last Name</label>
<input type="text" id="last_name" name="last_name" value="{{ profile.last_name }}"
class="w-full p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>

<div class="flex justify-between mt-6">
<button type="submit" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded transition-colors duration-300">
Save Changes
</button>
<button type="button" onclick="toggleEdit()" class="bg-gray-500 hover:bg-gray-700 text-white font-bold py-2 px-4 rounded transition-colors duration-300">
Cancel
</button>
</div>
</form>
</div>
</body>
</html>
</div>
<div class="h-32"></div>
{% endblock %}

{% block scripts %}
<script>
function toggleEdit() {
var viewSection = document.getElementById('view-profile');
var editSection = document.getElementById('edit-profile');
viewSection.classList.toggle('hidden');
editSection.classList.toggle('hidden');
}
</script>
{% endblock %}
46 changes: 46 additions & 0 deletions src/accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.decorators import login_required
from dateutil.parser import parse as parse_date


from accounts.models import CustomUser
from home.repositories import HomeRepository

from .forms import (
ServiceSeekerForm,
Expand Down Expand Up @@ -50,6 +53,41 @@ def profile_view(request):
elif user.user_type == "user":
service_seeker = get_object_or_404(CustomUser, email=user.email)

# Fetch user's bookmarks
repo = HomeRepository()
bookmarks = repo.get_user_bookmarks(str(user.id))

# Process the bookmarks
processed_bookmarks = [
{
"Id": item.get("Id"),
"Name": item.get("Name", "No Name"),
"Category": item.get("Category", "N/A"),
"Distance": "N/A", # Calculate if needed
"Address": item.get("Address", "N/A"),
}
for item in bookmarks
]

# Fetch user's reviews
reviews = repo.fetch_reviews_by_user(str(user.id))

# Get unique service IDs from the reviews
service_ids = set([review["ServiceId"] for review in reviews])

# Fetch service details
service_map = repo.get_services_by_ids(service_ids)

# Add service names and parse timestamps in reviews
for review in reviews:
service = service_map.get(review["ServiceId"], {})
review["ServiceName"] = service.get("Name", "Unknown Service")
# Parse the timestamp
try:
review["Timestamp"] = parse_date(review["Timestamp"])
except Exception:
pass # Keep as string if parsing fails

# If it's a POST request, we're updating the profile
if request.method == "POST":
form = ServiceSeekerForm(request.POST, instance=service_seeker)
Expand All @@ -65,6 +103,8 @@ def profile_view(request):
{
"profile": service_seeker,
"form": form, # Pass the form to the template
"bookmarks": processed_bookmarks,
"reviews": reviews, # Pass the reviews to the template
},
)

Expand Down Expand Up @@ -148,6 +188,12 @@ class UserLoginView(CustomLoginView):
form_class = UserLoginForm
template_name = "user_login.html"

def get_success_url(self):
if self.request.user.user_type == "user":
return reverse_lazy("home") # Redirect to user's home page
else:
return reverse_lazy("user_login")


# Service provider login view
class ServiceProviderLoginView(CustomLoginView):
Expand Down
Loading

0 comments on commit 9c65a64

Please sign in to comment.