Skip to content

Commit

Permalink
Feature added to recommend Users from profile page
Browse files Browse the repository at this point in the history
  • Loading branch information
tsu-ki committed Nov 18, 2024
1 parent d7d0c31 commit 0c5c7e6
Show file tree
Hide file tree
Showing 6 changed files with 269 additions and 1 deletion.
9 changes: 9 additions & 0 deletions website/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ def short_description(self, obj):
admin.site.unregister(User)


class RecommendationAdmin(admin.ModelAdmin):
list_display = ("recommender", "recommended_user", "created_at")
search_fields = ("recommender__username", "recommended_user__username")


class UserAdmin(ImportExportModelAdmin):
resource_class = UserResource
list_display = (
Expand Down Expand Up @@ -245,6 +250,7 @@ class UserProfileAdmin(admin.ModelAdmin):
"flagged_count",
"subscribed_domains_count",
"subscribed_users_count",
"recommendation_count",
"x_username",
"linkedin_url",
"github_url",
Expand Down Expand Up @@ -273,6 +279,9 @@ def subscribed_domains_count(self, obj):
def subscribed_users_count(self, obj):
return obj.subscribed_users.count()

def recommendation_count(self, obj):
return obj.recommendations.count()


class IssueScreenshotAdmin(admin.ModelAdmin):
model = IssueScreenshot
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("website", "0153_delete_contributorstats"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name="Recommendation",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"recommended_user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="received_recommendations",
to=settings.AUTH_USER_MODEL,
),
),
(
"recommender",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="given_recommendations",
to=settings.AUTH_USER_MODEL,
),
),
],
),
migrations.AddConstraint(
model_name="recommendation",
constraint=models.UniqueConstraint(
fields=("recommender", "recommended_user"), name="unique_recommendation"
),
),
]
22 changes: 22 additions & 0 deletions website/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,28 @@ def __str__(self):
return f"ActivityLog by {self.user.username} at {self.recorded_at}"


class Recommendation(models.Model):
recommender = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="given_recommendations"
)
recommended_user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="received_recommendations"
)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
constraints = [
models.UniqueConstraint(
fields=["recommender", "recommended_user"], name="unique_recommendation"
)
]

def __str__(self):
return (
f"Recommendation from {self.recommender.username} to {self.recommended_user.username}"
)


class DailyStatusReport(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateField()
Expand Down
1 change: 1 addition & 0 deletions website/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class Meta:
"issue_flaged",
"total_score",
"activities",
"recommendations",
)


Expand Down
159 changes: 159 additions & 0 deletions website/templates/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,49 @@
color: #ff5722;
font-weight: bold;
}

/* Added styles for Recommendations */
.recommend-user {
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.red-btn {
background-color: #cc0000;
color: #fff;
border: none;
margin : 10px;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}
.red-btn:hover {
background-color: #990000;
}
.error {
color: #cc0000;
font-weight: bold;
margin-bottom: 15px;
}

#user_list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #ddd;
padding: 10px;
border-radius: 5px;
}

.user-item {
padding: 5px 0;
border-bottom: 1px solid #eee;
}

.user-item:last-child {
border-bottom: none;
}
</style>
{% endblock style %}
{% block content %}
Expand Down Expand Up @@ -494,6 +537,9 @@
<button class="mr-3" onclick="openFollowingModal();">
<div class="bg-[#f4f4f4] rounded-2xl p-3 m-2">👤 {% trans "Following" %} {{ following|length }}</div>
</button>
<button class="mr-3" onclick="openRecommendationModal();">
<div class="bg-[#f4f4f4] rounded-2xl p-3 m-2">📑 {% trans "Recommends" %} {{ recommendations|length }}</div>
</button>
<button class="mr-3" onclick="openBookmarkModal();">
<div class="bg-[#f4f4f4] rounded-2xl p-3 m-2">📑 {% trans "Bookmarks" %} {{ bookmarks|length }}</div>
</button>
Expand All @@ -515,6 +561,31 @@
<hr>
</div>
<div id="chart" class="max-sm:w-full w-3/4"></div>
<div>
<div class="text-2xl font-bold my-4 pl-2">Spot a Talent? Recommend Now!</div>
<form method="post"
action="{% url 'profile' slug=user.username %}"
class="recommend-user">
{% csrf_token %}
<label for="search_user" class="pr-2">Search for a user:</label>
<input type="text"
id="search_user"
onkeyup="filterUsers()"
placeholder="Start typing a name...">
<div id="user_list">
{% for user in all_users %}
<div class="user-item">
<input type="radio"
name="recommended_user"
value="{{ user.id }}"
id="user_{{ user.id }}">
<label for="user_{{ user.id }}">{{ user.username }}</label>
</div>
{% endfor %}
</div>
<button class="red-btn" type="submit">Recommend</button>
</form>
</div>
</div>
</div>
<div class="max-sm:w-[100vw] w-[22vw]">
Expand Down Expand Up @@ -671,6 +742,65 @@ <h3>Following:</h3>
</div>
</div>
</div>
<div id="recommendations"
class="hidden relative z-10"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true">
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
aria-hidden="true"></div>
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
<div class="flex min-h-full items-center justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 max-sm:w-full lg:w-[30vw]">
<div class="bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start lg:w-full">
<div class="mt-3 sm:ml-4 sm:mt-0 sm:text-left lg:w-full">
<div class="mt-2">
<h3>Recommended Users:</h3>
<br>
<p class="text-xl">
<table class="table-auto w-full border-spacing-52">
<tbody>
{% for recommendation in recommendations %}
<tr class="shadow-lg m-3 w-full rounded">
{% if recommendation.recommended_user.socialaccount_set.all.0.get_avatar_url %}
<td>
<img src="{{ recommendation.recommended_user.socialaccount_set.all.0.get_avatar_url }}"
height="50px"
width="50px"
alt="{{ recommendation.recommended_user.username }}">
</td>
{% else %}
<td>
<img src="{% gravatar_url recommendation.recommended_user.email 50 %}"
height="50px"
width="50px"
alt="{{ recommendation.recommended_user.username }}">
</td>
{% endif %}
<td>
{% if recommendation.recommended_user.username %}
&nbsp; <a href="{% url 'profile' slug=recommendation.recommended_user.username %}">{{ recommendation.recommended_user.username }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
<button type="button"
class="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-xl font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
id="close-Recommendations">Close</button>
</div>
</div>
</div>
</div>
</div>
<div id="Bookmark"
class="hidden relative z-10"
aria-labelledby="modal-title"
Expand Down Expand Up @@ -735,6 +865,11 @@ <h3>Bookmarks:</h3>
closeFollowing.addEventListener("click",()=>{
openFollowing.style.display="none";
});
var closeRecommendations = document.getElementById("close-Recommendations");
var openRecommendations = document.getElementById("recommendations");
openRecommendations.addEventListener("click",()=>{
openRecommendations.style.display="none";
});
var closeBookmark = document.getElementById("close-Bookmark");
var openBookmark = document.getElementById("Bookmark");
closeBookmark.addEventListener("click",()=>{
Expand All @@ -749,11 +884,35 @@ <h3>Bookmarks:</h3>
function openFollowingModal(){
openFollowing.style.cssText = "display:block !important;"
}


function openRecommendationModal(){
openRecommendations.style.cssText = "display:block !important;"
}



function openBookmarkModal(){
openBookmark.style.cssText = "display:block !important;"
}

function filterUsers() {
var input, filter, userList, userItems, label, i, txtValue;
input = document.getElementById('search_user');
filter = input.value.toLowerCase();
userList = document.getElementById('user_list');
userItems = userList.getElementsByClassName('user-item');

for (i = 0; i < userItems.length; i++) {
label = userItems[i].getElementsByTagName('label')[0];
txtValue = label.textContent || label.innerText;
if (txtValue.toLowerCase().indexOf(filter) > -1) {
userItems[i].style.display = "";
} else {
userItems[i].style.display = "none";
}
}
}
</script>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script>
Expand Down
28 changes: 27 additions & 1 deletion website/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
Monitor,
Payment,
Points,
Recommendation,
Tag,
User,
UserProfile,
Expand Down Expand Up @@ -264,10 +265,30 @@ def get_context_data(self, **kwargs):
UserProfile.objects.filter(user=self.object).first().tags.all()
)
context["issues_hidden"] = "checked" if user.userprofile.issues_hidden else "!checked"

recommendations = Recommendation.objects.filter(recommender=user)
context["recommendations"] = recommendations
context["all_users"] = User.objects.exclude(id=self.request.user.id)

return context

@method_decorator(login_required)
def post(self, request, *args, **kwargs):
if "recommended_user" in request.POST:
recommended_user_id = request.POST.get("recommended_user")
try:
recommended_user = User.objects.get(id=recommended_user_id)
if Recommendation.objects.filter(
recommender=request.user, recommended_user=recommended_user
).exists():
messages.error(request, "You have already recommended this user.")
else:
Recommendation.objects.create(
recommender=request.user, recommended_user=recommended_user
)
messages.success(request, "Recommendation added successfully!")
except User.DoesNotExist:
messages.error(request, "User not found.")
form = UserProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
if request.FILES.get("user_avatar") and form.is_valid():
form.save()
Expand All @@ -277,7 +298,7 @@ def post(self, request, *args, **kwargs):
user_issues.update(is_hidden=hide)
request.user.userprofile.issues_hidden = hide
request.user.userprofile.save()
return redirect(reverse("profile", kwargs={"slug": kwargs.get("slug")}))
return redirect(self.request.path_info)


class UserProfileDetailsView(DetailView):
Expand Down Expand Up @@ -350,6 +371,11 @@ def get_context_data(self, **kwargs):
str(prof.user.email) for prof in user.userprofile.follower.all()
]
context["bookmarks"] = user.userprofile.issue_saved.all()

# Recommendations
recommendations = Recommendation.objects.filter(recommender=user)
context["recommendations"] = recommendations

return context

@method_decorator(login_required)
Expand Down

0 comments on commit 0c5c7e6

Please sign in to comment.