From a6f7933fe0b0371ca3eecb2b5fdbcb54d1c91052 Mon Sep 17 00:00:00 2001 From: DonnieBLT <128622481+DonnieBLT@users.noreply.github.com> Date: Sat, 10 Aug 2024 17:12:01 -0400 Subject: [PATCH] Update user profile form fields and add new fields for LinkedIn URL, GitHub URL, website URL, discounted hourly rate, and X username. Add users endpoint to URL patterns. Refactor admin page for tags to display additional fields and prepopulate slug field. Add migration files for tag and user profile changes. Remove unused CSS class in sidenav.html and leaderboard_global.html. --- blt/urls.py | 2 + website/admin.py | 7 +- website/forms.py | 5 ++ .../migrations/0127_tag_created_tag_slug.py | 25 +++++++ ...profile_discounted_hourly_rate_and_more.py | 37 ++++++++++ website/models.py | 13 ++++ website/templates/includes/sidenav.html | 9 +++ website/templates/leaderboard_global.html | 16 ----- website/templates/profile.html | 33 +++++++-- website/templates/profile_edit.html | 52 +++++++++++++- website/templates/users.html | 69 +++++++++++++++++++ website/views.py | 30 ++++++++ 12 files changed, 272 insertions(+), 26 deletions(-) create mode 100644 website/migrations/0127_tag_created_tag_slug.py create mode 100644 website/migrations/0128_userprofile_discounted_hourly_rate_and_more.py create mode 100644 website/templates/users.html diff --git a/blt/urls.py b/blt/urls.py index a4e77f81b..7ccf6c215 100644 --- a/blt/urls.py +++ b/blt/urls.py @@ -505,6 +505,8 @@ path("robots.txt", website.views.robots_txt), path("ads.txt", website.views.ads_txt), re_path(r"^contributors/$", contributors_view, name="contributors"), + # users + path("users/", website.views.users_view, name="users"), path("company/", include("company.urls")), path("sponsor/", website.views.sponsor_view, name="sponsor"), path("companies/", DomainListView.as_view(), name="domain_lists"), diff --git a/website/admin.py b/website/admin.py index cd737bd4b..e0fd0cca0 100644 --- a/website/admin.py +++ b/website/admin.py @@ -319,6 +319,11 @@ class ProjectAdmin(admin.ModelAdmin): search_fields = ["name", "description", "slug"] +class TagAdmin(admin.ModelAdmin): + list_display = ("name", "slug", "created") + prepopulated_fields = {"slug": ("name",)} + + # Register all models with their respective admin classes admin.site.register(Project, ProjectAdmin) admin.site.register(ContributorStats) @@ -346,4 +351,4 @@ class ProjectAdmin(admin.ModelAdmin): admin.site.register(IP, IPAdmin) admin.site.register(Transaction) admin.site.register(Monitor, MonitorAdmin) -admin.site.register(Tag) +admin.site.register(Tag, TagAdmin) diff --git a/website/forms.py b/website/forms.py index 11292e51c..0c2d11523 100644 --- a/website/forms.py +++ b/website/forms.py @@ -89,6 +89,11 @@ class Meta: "tags", "subscribed_domains", "subscribed_users", + "linkedin_url", + "x_username", + "website_url", + "discounted_hourly_rate", + "github_url", ] widgets = { "tags": forms.CheckboxSelectMultiple(), diff --git a/website/migrations/0127_tag_created_tag_slug.py b/website/migrations/0127_tag_created_tag_slug.py new file mode 100644 index 000000000..0d67dd340 --- /dev/null +++ b/website/migrations/0127_tag_created_tag_slug.py @@ -0,0 +1,25 @@ +# Generated by Django 5.0.7 on 2024-08-10 19:41 + +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("website", "0126_alter_userprofile_subscribed_domains_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="tag", + name="created", + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name="tag", + name="slug", + field=models.SlugField(default="", unique=True), + preserve_default=False, + ), + ] diff --git a/website/migrations/0128_userprofile_discounted_hourly_rate_and_more.py b/website/migrations/0128_userprofile_discounted_hourly_rate_and_more.py new file mode 100644 index 000000000..aad3d4cc2 --- /dev/null +++ b/website/migrations/0128_userprofile_discounted_hourly_rate_and_more.py @@ -0,0 +1,37 @@ +# Generated by Django 5.0.7 on 2024-08-10 20:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("website", "0127_tag_created_tag_slug"), + ] + + operations = [ + migrations.AddField( + model_name="userprofile", + name="discounted_hourly_rate", + field=models.DecimalField(decimal_places=2, default=0, max_digits=10), + ), + migrations.AddField( + model_name="userprofile", + name="github_url", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="userprofile", + name="linkedin_url", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="userprofile", + name="website_url", + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name="userprofile", + name="x_username", + field=models.CharField(blank=True, max_length=50, null=True), + ), + ] diff --git a/website/models.py b/website/models.py index 290522796..1dc35e92d 100644 --- a/website/models.py +++ b/website/models.py @@ -18,6 +18,7 @@ from django.db.models import Count from django.db.models.signals import post_delete, post_save from django.dispatch import receiver +from django.template.defaultfilters import slugify from django.utils import timezone from google.api_core.exceptions import NotFound from google.cloud import storage @@ -46,6 +47,13 @@ class Subscription(models.Model): class Tag(models.Model): name = models.CharField(max_length=255) + slug = models.SlugField(unique=True) + created = models.DateTimeField(auto_now_add=True) + + def save(self, *args, **kwargs): + # make the slug using slugify + self.slug = slugify(self.name) + super(Tag, self).save(*args, **kwargs) def __str__(self): return self.name @@ -517,6 +525,11 @@ class UserProfile(models.Model): eth_address = models.CharField(max_length=100, blank=True, null=True) created = models.DateTimeField(auto_now_add=True) tags = models.ManyToManyField(Tag, blank=True) + x_username = models.CharField(max_length=50, blank=True, null=True) + linkedin_url = models.URLField(blank=True, null=True) + github_url = models.URLField(blank=True, null=True) + website_url = models.URLField(blank=True, null=True) + discounted_hourly_rate = models.DecimalField(max_digits=10, decimal_places=2, default=0) def avatar(self, size=36): if self.user_avatar: diff --git a/website/templates/includes/sidenav.html b/website/templates/includes/sidenav.html index 7673d0e21..a047d1d9f 100644 --- a/website/templates/includes/sidenav.html +++ b/website/templates/includes/sidenav.html @@ -69,6 +69,15 @@
  • +
    + +
    + Leaderboard +
    +
  • +
  • +
    diff --git a/website/templates/leaderboard_global.html b/website/templates/leaderboard_global.html index 9accd79ba..67aed35f2 100644 --- a/website/templates/leaderboard_global.html +++ b/website/templates/leaderboard_global.html @@ -110,22 +110,6 @@

    Global Leaderboard

    - {% if user_related_tags %} -
    - -
    - {% endif %}
    diff --git a/website/templates/profile.html b/website/templates/profile.html index 1d2dee98a..0d61cfb04 100644 --- a/website/templates/profile.html +++ b/website/templates/profile.html @@ -14,7 +14,6 @@ text-decoration: inherit; } - .list-group .label-default { margin-top: 3px; } @@ -82,10 +81,6 @@ width: 200px; } - - - - .scroll { max-height: 1100px; overflow-y: auto; @@ -259,6 +254,21 @@ #bch:checked~.s1 { margin-left: -60%; } + + /* Added styles for description and discounted hourly rate */ + .profile-description, + .profile-rate { + max-width: 100%; + word-wrap: break-word; + margin-top: 1rem; + font-size: 14px; + line-height: 1.5; + } + + .profile-rate { + color: #ff5722; + font-weight: bold; + } {% endblock style %} {% block content %} @@ -317,6 +327,16 @@ {% endif %}
    {% include "./includes/crypto_donation.html" %}
    + {% if user.userprofile.description %} +
    Bio / Description
    +
    {{ user.userprofile.description }}
    +
    + {% endif %} + {% if user.userprofile.discounted_hourly_rate %} +
    Discounted Hourly Rate
    +
    {{ user.userprofile.discounted_hourly_rate }}
    +
    + {% endif %}
    Recent Activity @@ -479,7 +499,8 @@
    {% for tag in user_related_tags %} - {% endfor %} diff --git a/website/templates/profile_edit.html b/website/templates/profile_edit.html index ebb99578c..031a6f4c6 100644 --- a/website/templates/profile_edit.html +++ b/website/templates/profile_edit.html @@ -35,6 +35,52 @@

    Edit Profile

    rows="4" class="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">{{ form.description.value }}
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    @@ -63,7 +109,7 @@

    Edit Profile

    @@ -72,7 +118,7 @@

    Edit Profile

    diff --git a/website/templates/users.html b/website/templates/users.html new file mode 100644 index 000000000..34b40d813 --- /dev/null +++ b/website/templates/users.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} +{% block style %} + + + + +{% endblock style %} +{% block content %} + {% include "includes/sidenav.html" %} +
    +
    +
    +

    Meet our Community

    +
    +
    + {% for tag_info in user_related_tags %} + + {% endfor %} +
    +
    +
    + {% for user in users %} +
    + user image +
    + {{ user.user.username }} +
    +
    {{ user.location }}
    +

    + {{ user.short_description }} +

    + + More Info +
    + {% endfor %} +
    +
    +{% endblock content %} diff --git a/website/views.py b/website/views.py index a43a6eb5a..4ca8f53c9 100644 --- a/website/views.py +++ b/website/views.py @@ -1459,6 +1459,10 @@ def get_context_data(self, **kwargs): str(prof.user.email) for prof in user.userprofile.follower.all() ] context["bookmarks"] = user.userprofile.issue_saved.all() + # tags + context["user_related_tags"] = ( + UserProfile.objects.filter(user=self.object).first().tags.all() + ) context["issues_hidden"] = "checked" if user.userprofile.issues_hidden else "!checked" return context @@ -3894,6 +3898,32 @@ def handler500(request, exception=None): return render(request, "500.html", {}, status=500) +def users_view(request, *args, **kwargs): + context = {} + + # Get all tags related to all user profiles + context["user_related_tags"] = Tag.objects.filter(userprofile__isnull=False).distinct() + + # Get all tags in the system + context["tags"] = Tag.objects.all() + + # Check if a specific tag is being requested + tag_name = request.GET.get("tag") + if tag_name: + # Check if the requested tag exists in user_related_tags + if context["user_related_tags"].filter(name=tag_name).exists(): + context["tag"] = tag_name + context["users"] = UserProfile.objects.filter(tags__name=tag_name) + else: + context["users"] = UserProfile.objects.none() # No users if the tag isn't found + else: + # Default filter: Show users with the tag "BLT Contributor" + context["tag"] = "BLT Contributors" + context["users"] = UserProfile.objects.filter(tags__name="BLT Contributors") + + return render(request, "users.html", context=context) + + def contributors_view(request, *args, **kwargs): contributors_file_path = os.path.join(settings.BASE_DIR, "contributors.json")