Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions projects/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,21 @@ def get_projects_for_list_view(self):
)

def get_user_projects_for_list_view(self):
return self.get_queryset().prefetch_related(
Prefetch(
"industry",
queryset=Industry.objects.only("name").all(),
),
Prefetch(
"leader",
queryset=CustomUser.objects.only("id").all(),
),
Prefetch("collaborator_set"),
).distinct()
return (
self.get_queryset()
.prefetch_related(
Prefetch(
"industry",
queryset=Industry.objects.only("name").all(),
),
Prefetch(
"leader",
queryset=CustomUser.objects.only("id").all(),
),
Prefetch("collaborator_set"),
)
.distinct()
)

def get_projects_for_detail_view(self):
return (
Expand All @@ -66,6 +70,9 @@ def check_if_owns_any_projects(self, user) -> bool:
# I don't think this should work but the function has no usages, so I'll let it be
return user.leader_projects.exists()

def get_projects_from_list_of_ids(self, ids):
return self.get_queryset().filter(id__in=ids)


class AchievementManager(Manager):
def get_achievements_for_list_view(self):
Expand Down
18 changes: 18 additions & 0 deletions projects/migrations/0010_project_views_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.3 on 2023-01-21 14:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("projects", "0009_remove_project_short_description"),
]

operations = [
migrations.AddField(
model_name="project",
name="views_count",
field=models.PositiveIntegerField(default=0),
),
]
6 changes: 6 additions & 0 deletions projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ class Project(models.Model):

objects = ProjectManager()

views_count = models.PositiveIntegerField(default=0)
Comment thread
yeezy-na-izi marked this conversation as resolved.

def increment_views_count(self):
self.views_count += 1
self.save()

def get_short_description(self) -> Optional[str]:
return self.description[:90] if self.description else None

Expand Down
19 changes: 18 additions & 1 deletion projects/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from industries.models import Industry
from projects.models import Project, Achievement, Collaborator
from projects.validators import validate_project
from users.models import LikesOnProject
from vacancy.serializers import ProjectVacancyListSerializer


Expand Down Expand Up @@ -65,6 +66,7 @@ class ProjectDetailSerializer(serializers.ModelSerializer):
vacancies = ProjectVacancyListSerializer(many=True, read_only=True)
short_description = serializers.SerializerMethodField()
industry_id = serializers.IntegerField(required=False)
likes_count = serializers.SerializerMethodField(method_name="count_likes")

def validate(self, data):
super().validate(data)
Expand All @@ -74,6 +76,9 @@ def validate(self, data):
def get_short_description(cls, project):
return project.get_short_description()

def count_likes(self, project):
return LikesOnProject.objects.filter(project=project, is_liked=True).count()

def update(self, instance, validated_data):
instance = super().update(instance, validated_data)
instance.save()
Expand All @@ -99,12 +104,20 @@ class Meta:
"vacancies",
"datetime_created",
"datetime_updated",
"views_count",
"likes_count",
]
read_only_fields = [
"leader",
"views_count",
"datetime_created",
"datetime_updated",
]
read_only_fields = ["leader"]


class ProjectListSerializer(serializers.ModelSerializer):
collaborators = serializers.SerializerMethodField(method_name="get_collaborators")
likes_count = serializers.SerializerMethodField(method_name="count_likes")
collaborator_count = serializers.SerializerMethodField(
method_name="get_collaborator_count"
)
Expand All @@ -120,6 +133,9 @@ def get_short_description(cls, project):
def get_collaborator_count(cls, obj):
return len(obj.collaborator_set.all())

def count_likes(self, obj):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_likes_count

return LikesOnProject.objects.filter(project=obj, is_liked=True).count()

def get_collaborators(self, obj):
max_collaborator_count = 4
return CollaboratorSerializer(
Expand All @@ -142,6 +158,7 @@ class Meta:
"collaborators",
"vacancies",
"datetime_created",
"likes_count",
]

read_only_fields = [
Expand Down
2 changes: 2 additions & 0 deletions projects/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
ProjectCollaborators,
ProjectCountView,
ProjectVacancyResponses,
SetLikeOnProject,
)

app_name = "projects"

urlpatterns = [
path("", ProjectList.as_view()),
path("<int:pk>/like/", SetLikeOnProject.as_view()),
path("<int:pk>/collaborators/", ProjectCollaborators.as_view()),
path("<int:pk>/", ProjectDetail.as_view()),
path("count/", ProjectCountView.as_view()),
Expand Down
30 changes: 30 additions & 0 deletions projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
AchievementDetailSerializer,
ProjectCollaboratorSerializer,
)
from users.models import LikesOnProject
from vacancy.models import VacancyResponse
from vacancy.serializers import VacancyResponseListSerializer

Expand Down Expand Up @@ -79,6 +80,12 @@ class ProjectDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [HasInvolvementInProjectOrReadOnly]
serializer_class = ProjectDetailSerializer

def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.increment_views_count()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я думал, что мы можем сделать так, что один айпишник может увелчить кол-во просмотров на 1 раз в 5 минут. Т.е. добавить кеш ip-project, чтобы просмотры было накрутить проще, чем просто запустить скрипт который кидает запросы к апишке

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

чтобы *сложнее было наверно)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Это пока не будет делаться
Переписка с петей

serializer = self.get_serializer(instance)
return Response(serializer.data)

def put(self, request, pk, **kwargs):
# bootleg version of updating achievements via project
if request.data.get("achievements") is not None:
Expand All @@ -100,6 +107,29 @@ def put(self, request, pk, **kwargs):
return super(ProjectDetail, self).put(request, pk)


class SetLikeOnProject(APIView):
permission_classes = [IsAuthenticated]

def post(self, request, pk):
"""
Set like on project

---

Args:
request:
pk - project id

Returns:
Response

"""
project = Project.objects.get(pk=pk)
LikesOnProject.objects.toggle_like(request.user, project)

return Response(ProjectListSerializer(project).data)


class ProjectCountView(generics.GenericAPIView):
queryset = Project.objects.get_projects_for_count_view()
serializer_class = ProjectListSerializer
Expand Down
18 changes: 18 additions & 0 deletions users/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,21 @@ def get_achievements_for_detail_view(self):
.select_related("user")
.only("id", "title", "status", "user")
)


class LikesOnProjectManager(Manager):
def get_likes_for_list_view(self):
return (
self.get_queryset()
.select_related("user")
.only("id", "user__id", "project__id")
)

def get_or_create(self, user, project):
return super().get_or_create(user=user, project=project)

def toggle_like(self, user, project):
like, created = self.get_or_create(user=user, project=project)
if not created:
like.toggle_like()
return like
52 changes: 52 additions & 0 deletions users/migrations/0027_likesonproject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Generated by Django 4.1.3 on 2023-01-21 14:45

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("projects", "0010_project_views_count"),
("users", "0026_remove_member_preferred_industries_and_more"),
]

operations = [
migrations.CreateModel(
name="LikesOnProject",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("like", models.BooleanField(default=True)),
(
"project",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="likes",
to="projects.project",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="likes_on_projects",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"verbose_name": "Лайк на проект",
"verbose_name_plural": "Лайки на проекты",
"unique_together": {("user", "project")},
},
),
]
18 changes: 18 additions & 0 deletions users/migrations/0028_rename_like_likesonproject_is_liked.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.1.3 on 2023-01-21 18:29

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("users", "0027_likesonproject"),
]

operations = [
migrations.RenameField(
model_name="likesonproject",
old_name="like",
new_name="is_liked",
),
]
45 changes: 44 additions & 1 deletion users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
VERBOSE_ROLE_TYPES,
VERBOSE_USER_TYPES,
)
from users.managers import CustomUserManager, UserAchievementManager
from users.managers import (
CustomUserManager,
UserAchievementManager,
LikesOnProjectManager,
)
from users.validators import user_birthday_validator


Expand Down Expand Up @@ -151,6 +155,45 @@ class Meta:
abstract = True


class LikesOnProject(models.Model):
"""
LikesOnProject model

This model is used to store the user's likes on projects.

Attributes:
user: ForeignKey instance of user.
project: ForeignKey instance of project.
"""

is_liked = models.BooleanField(default=True)

user = models.ForeignKey(
CustomUser,
on_delete=models.CASCADE,
related_name="likes_on_projects",
)
project = models.ForeignKey(
"projects.Project",
on_delete=models.CASCADE,
related_name="likes",
)

objects = LikesOnProjectManager()

def toggle_like(self):
self.is_liked = not self.is_liked
self.save()

def __str__(self):
return f"LikesOnProject<{self.id}>"

class Meta:
verbose_name = "Лайк на проект"
verbose_name_plural = "Лайки на проекты"
unique_together = ("user", "project")


class Member(models.Model):
"""
Member model
Expand Down
2 changes: 2 additions & 0 deletions users/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
UserTypesView,
VerifyEmail,
LogoutView,
LikedProjectList,
)

app_name = "users"
Expand All @@ -24,6 +25,7 @@
), # this url actually returns mentors, experts and investors
path("users/", UserList.as_view()),
path("users/projects/", UserProjectsList.as_view()),
path("users/liked/", LikedProjectList.as_view()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

users/liked_projects будет понятнее

path("users/roles/", UserAdditionalRolesView.as_view()),
path("users/types/", UserTypesView.as_view()),
path("users/<int:pk>/", UserDetail.as_view()),
Expand Down
Loading