• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gcivil-nyu-org / team2-wed-spring25 / 859

23 Apr 2025 01:52PM UTC coverage: 87.903% (-3.0%) from 90.945%
859

Pull #360

travis-pro

web-flow
Merge 1161be77f into 804524f90
Pull Request #360: Develop yd debug

45 of 134 new or added lines in 7 files covered. (33.58%)

29 existing lines in 2 files now uncovered.

3001 of 3414 relevant lines covered (87.9%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.91
/backend/nightwalkers/forum/views.py
1
from django.core.paginator import Paginator
1✔
2
from django.shortcuts import get_object_or_404
1✔
3
from django.http import JsonResponse
1✔
4
from django.views.decorators.csrf import csrf_exempt
1✔
5
from django.contrib.auth import get_user_model
1✔
6

7
from map.models import SavedRoute
1✔
8
from .models import Post, Comment, Like, CommentLike, ReportPost, ReportComment
1✔
9
from accounts.models import Follow
1✔
10
from django.db.models import Count, OuterRef, Subquery, IntegerField, Exists
1✔
11
import json
1✔
12
from notifications import notification_service
1✔
13

14
User = get_user_model()
1✔
15

16

17
# Helper function to parse JSON request body
18
def parse_json_request(request):
1✔
19
    try:
1✔
20
        return json.loads(request.body)
1✔
21
    except json.JSONDecodeError:
1✔
22
        return None
1✔
23

24

25
@csrf_exempt
1✔
26
def create_post(request):
1✔
27
    if request.method == "POST":
1✔
28
        data = parse_json_request(request)
1✔
29
        if not data:
1✔
30
            return JsonResponse({"error": "Invalid JSON data"}, status=400)
×
31

32
        user_id = data.get("user_id")
1✔
33
        content = data.get("content")
1✔
34
        is_edit = data.get(
1✔
35
            "is_edit", False
36
        )  # Optional field to indicate if it's an edit
37
        post_id = data.get("post_id")  # Optional field for editing an existing post
1✔
38
        image_urls = data.get("image_urls", [])
1✔
39
        if is_edit and post_id:
1✔
40
            try:
1✔
41
                post = Post.objects.get(id=post_id)
1✔
42
                post.content = content
1✔
43
                post.image_urls = image_urls
1✔
44
                post.save()
1✔
45
                return JsonResponse(
1✔
46
                    {
47
                        "id": post.id,
48
                        "title": post.title,
49
                        "content": post.content,
50
                        "image_urls": post.image_urls,
51
                        "date_created": post.date_created,
52
                        "user": {
53
                            "id": post.user.id,
54
                            "username": post.user.username,
55
                            "email": post.user.email,
56
                            "first_name": post.user.first_name,
57
                            "last_name": post.user.last_name,
58
                            "avatar_url": post.user.get_avatar_url(),
59
                            "karma": post.user.karma,
60
                        },
61
                        "status": 200,
62
                    },
63
                    status=200,
64
                )
65
            except Post.DoesNotExist:
×
66
                print("Post not found for editing")
×
67
                return JsonResponse({"error": "Post not found"}, status=404)
×
68

69
        if not user_id or (not content and not image_urls):
1✔
70
            return JsonResponse(
1✔
71
                {"error": "user_id and content are required"}, status=400
72
            )
73

74
        try:
1✔
75
            # Fetch the user by ID
76
            user = User.objects.get(id=user_id)
1✔
77
        except User.DoesNotExist:
×
78
            return JsonResponse({"error": "User not found"}, status=404)
×
79

80
        # Create the post
81
        post = Post.objects.create(
1✔
82
            user=user, title="", content=content, image_urls=image_urls
83
        )
84

85
        # increase user profile karma by 10
86
        user.karma += 10
1✔
87
        user.save()
1✔
88

89
        # Include user details in the response
90
        return JsonResponse(
1✔
91
            {
92
                "id": post.id,
93
                "title": post.title,
94
                "content": post.content,
95
                "image_urls": post.image_urls,
96
                "date_created": post.date_created,
97
                "user": {
98
                    "id": user.id,
99
                    "username": user.username,
100
                    "email": user.email,
101
                    "first_name": user.first_name,
102
                    "last_name": user.last_name,
103
                    "avatar_url": user.get_avatar_url(),
104
                    "karma": user.karma,
105
                },
106
                "status": 201,
107
            },
108
            status=201,
109
        )
110

111
    return JsonResponse({"error": "Method not allowed"}, status=405)
×
112

113

114
@csrf_exempt
1✔
115
def create_repost(request):
1✔
116
    if request.method == "POST":
1✔
117
        data = parse_json_request(request)
1✔
118
        if not data:
1✔
119
            return JsonResponse(
×
120
                {"error": "Invalid JSON data", "status": 400}, status=400
121
            )
122

123
        user_id = data.get("user_id")
1✔
124
        original_post_id = data.get("original_post_id")
1✔
125
        if not user_id or not original_post_id:
1✔
126
            return JsonResponse(
1✔
127
                {"error": "user_id and original_post_id are required", "status": 400},
128
                status=400,
129
            )
130

131
        try:
1✔
132
            # Fetch the user by ID
133
            user = User.objects.get(id=user_id)
1✔
134
        except User.DoesNotExist:
1✔
135
            return JsonResponse({"error": "User not found", "status": 404}, status=404)
1✔
136

137
        try:
1✔
138
            # Fetch the original post by ID
139
            original_post = Post.objects.get(id=original_post_id)
1✔
140
        except Post.DoesNotExist:
1✔
141
            return JsonResponse(
1✔
142
                {"error": "Original post not found", "status": 404}, status=404
143
            )
144

145
        # Check if the user already reposted the same post
146
        if Post.objects.filter(user=user, original_post=original_post).exists():
1✔
147
            return JsonResponse(
1✔
148
                {"error": "You have already reposted this post", "status": 400},
149
                status=400,
150
            )
151

152
        # Create the repost
153
        repost = Post.objects.create(
1✔
154
            user=user,  # The user creating the repost
155
            is_repost=True,
156
            original_post=original_post,
157
            reposted_by=user,
158
            title="",  # Optional: You can add a title if needed
159
            content=original_post.content,  # Copy the original post's content
160
            image_urls=original_post.image_urls,  # Copy the original post's image URLs
161
        )
162

163
        # increase karma by 5
164
        user.karma += 5
1✔
165
        user.save()
1✔
166

167
        # Include repost details in the response
168
        return JsonResponse(
1✔
169
            {
170
                "id": repost.id,
171
                "is_repost": repost.is_repost,
172
                "original_post": {
173
                    "id": original_post.id,
174
                    "content": original_post.content,
175
                    "user": {
176
                        "id": original_post.user.id,
177
                        "username": original_post.user.username,
178
                        "email": original_post.user.email,
179
                        "first_name": original_post.user.first_name,
180
                        "last_name": original_post.user.last_name,
181
                    },
182
                },
183
                "reposted_by": {
184
                    "id": user.id,
185
                    "username": user.username,
186
                    "email": user.email,
187
                    "first_name": user.first_name,
188
                    "last_name": user.last_name,
189
                },
190
                "date_created": repost.date_created,
191
                "status": 201,
192
            },
193
            status=201,
194
        )
195

196
    return JsonResponse({"error": "Method not allowed", "status": 405}, status=405)
1✔
197

198

199
def get_user_data(request):
1✔
200
    """
201
    get total user followers count,
202
    by find total rows in Follow table
203
    where following_user = cureent user
204
    get user karma, its in User table
205
    get user saved routes counts, by
206
    finding total rows with user= current user in SavedRoute table
207
    get user total posts count,
208
    by finding total rows with user= current user in Post table
209
    """
210
    if request.method == "GET":
1✔
211
        user_id = request.GET.get("user_id")
1✔
212
        if not user_id:
1✔
213
            return JsonResponse({"error": "user_id is required"}, status=400)
×
214

215
        try:
1✔
216
            user = User.objects.get(id=user_id)
1✔
217
        except User.DoesNotExist:
1✔
218
            return JsonResponse({"error": "User not found"}, status=404)
1✔
219
        total_followers = Follow.objects.filter(following_user=user).count()
1✔
220
        print("user: ", user)
1✔
221
        user_karma = user.karma
1✔
222
        total_posts = Post.objects.filter(user=user).count()
1✔
223
        # get Total Saved Routers count by current user using SavedRoute model
224
        total_saved_routes = SavedRoute.objects.filter(user=user).count()
1✔
225
        return JsonResponse(
1✔
226
            {
227
                "total_followers": total_followers,
228
                "user_karma": user_karma,
229
                "total_posts": total_posts,
230
                "total_saved_routes": total_saved_routes,
231
                "status": 200,
232
            },
233
            status=200,
234
        )
235

236
    return JsonResponse({"error": "Method not allowed"}, status=405)
1✔
237

238

239
# Get all posts
240
def get_posts(request):
1✔
241
    if request.method == "GET":
1✔
242
        user_id = request.GET.get("user_id")  # Get the user ID from query parameters
1✔
243
        offset = int(request.GET.get("offset", 0))  # Get the offset (default: 0)
1✔
244
        limit = int(request.GET.get("limit", 5))  # Get the limit (default: 5)
1✔
245
        settings_type = request.GET.get(
1✔
246
            "settings_type", ""
247
        )  # Get the settings type (default: "")
248
        # Fetch all likes by the current user in a single query
249
        user_likes = Like.objects.filter(user_id=user_id).values_list(
1✔
250
            "post_id", "like_type"
251
        )
252

253
        # Convert user_likes into a dictionary for quick lookup
254
        user_likes_dict = {post_id: like_type for post_id, like_type in user_likes}
1✔
255

256
        # Fetch all follow relationships for the current user
257
        current_user_following = Follow.objects.filter(
1✔
258
            main_user_id=user_id
259
        ).values_list("following_user_id", flat=True)
260

261
        # get all the posts that the user has reported
262
        reported_posts = ReportPost.objects.filter(
1✔
263
            reporting_user_id=user_id
264
        ).values_list("post_id", flat=True)
265
        # create set for quick lookup
266
        reported_posts = set(reported_posts)
1✔
267

268
        # Convert current_user_following into a set for quick lookup
269
        current_user_following_set = set(current_user_following)
1✔
270
        user = get_object_or_404(User, id=user_id)
1✔
271
        print("settings_type:", settings_type)
1✔
272
        # Annotate posts with distinct likes_count and comments_count
273
        if settings_type == "":
1✔
274
            posts = Post.objects.annotate(
1✔
275
                likes_count=Count("likes", distinct=True),  # Distinct count for likes
276
                comments_count=Count(
277
                    "comments", distinct=True
278
                ),  # Distinct count for comments
279
            ).order_by("-date_created")
280
        elif settings_type == "posts":
1✔
281
            posts = (
1✔
282
                Post.objects.filter(user=user, is_repost=False)
283
                .annotate(
284
                    likes_count=Count(
285
                        "likes", distinct=True
286
                    ),  # Distinct count for likes
287
                    comments_count=Count(
288
                        "comments", distinct=True
289
                    ),  # Distinct count for comments
290
                )
291
                .order_by("-date_created")
292
            )
293
        elif settings_type == "reactions":
1✔
294
            # show all the post user has liked
295
            posts = (
1✔
296
                Post.objects.filter(likes__user=user)
297
                .annotate(
298
                    likes_count=Count(
299
                        "likes", distinct=True
300
                    ),  # Distinct count for likes
301
                    comments_count=Count(
302
                        "comments", distinct=True
303
                    ),  # Distinct count for comments
304
                )
305
                .order_by("-date_created")
306
            )
307
        elif settings_type == "reports":
1✔
308
            # show all the post user has reported
309
            posts = (
1✔
310
                Post.objects.filter(reports__reporting_user=user)
311
                .annotate(
312
                    likes_count=Count(
313
                        "likes", distinct=True
314
                    ),  # Distinct count for likes
315
                    comments_count=Count(
316
                        "comments", distinct=True
317
                    ),  # Distinct count for comments
318
                )
319
                .order_by("-date_created")
320
            )
321
        elif settings_type == "comments":
1✔
322
            # find all the posts_id where user has commented \
323
            # then filter the posts and annotate and order by \
324
            # date and paginate amd return
325
            posts = (
1✔
326
                Post.objects.filter(comments__user=user)
327
                .annotate(
328
                    likes_count=Count(
329
                        "likes", distinct=True
330
                    ),  # Distinct count for likes
331
                    comments_count=Count(
332
                        "comments", distinct=True
333
                    ),  # Distinct count for comments
334
                )
335
                .order_by("-date_created")
336
            )
337
        elif settings_type == "flagged_posts":
×
338
            # show all the posts made by user that have been reported
339
            # so find all the posts made by user that have been reported,
340
            # then filter the posts and annotate and order by date
341

342
            posts = (
×
343
                Post.objects.filter(user=user)
344
                .annotate(
345
                    likes_count=Count("likes", distinct=True),
346
                    comments_count=Count("comments", distinct=True),
347
                    is_reported=Exists(ReportPost.objects.filter(post=OuterRef("pk"))),
348
                )
349
                .filter(is_reported=True)
350
                .order_by("-date_created")
351
            )
352

353
        # Prepare the response data
354
        posts_data = []
1✔
355
        all_posts_so_far = set()
1✔
356
        post_count = 0
1✔
357

358
        for post in posts[offset:]:  # Apply offset to skip already fetched posts
1✔
359
            if post_count >= limit:  # Stop after fetching the required number of posts
1✔
360
                break
1✔
361

362
            if post.is_repost:
1✔
363
                # If the post is a repost, fetch the original post details
364
                original_post = post.original_post
×
365

366
                # If the post is a repost,
367
                # only show it to the current user
368
                # if the current user is not following the original author
369
                if (
×
370
                    original_post.user.id in current_user_following_set
371
                    or original_post.id in all_posts_so_far
372
                ):
373
                    continue
×
374
                all_posts_so_far.add(original_post.id)
×
375

376
                # Calculate likes and comments for the original post
377
                original_likes_count = Like.objects.filter(post=original_post).count()
×
378
                original_comments_count = Comment.objects.filter(
×
379
                    post=original_post,
380
                ).count()
381

382
                post_data = {
×
383
                    "id": post.id,
384
                    "is_reported": original_post.id in reported_posts,
385
                    "original_post_id": original_post.id,
386
                    "title": original_post.title,
387
                    "content": original_post.content,
388
                    "image_urls": original_post.image_urls,
389
                    "date_created": original_post.date_created,
390
                    "user_id": original_post.user.id,
391
                    "user_fullname": original_post.user.get_full_name(),
392
                    "user_avatar": original_post.user.get_avatar_url(),
393
                    "user_karma": original_post.user.get_karma(),
394
                    "comments_count": original_comments_count,
395
                    "likes_count": original_likes_count,
396
                    "user_has_liked": original_post.id
397
                    in user_likes_dict,  # Check if the user has liked the post
398
                    "like_type": user_likes_dict.get(
399
                        original_post.id
400
                    ),  # Get the like_type if the user has liked the post
401
                    "is_following_author": original_post.user.id
402
                    in current_user_following_set,
403
                    # Check if the current user is following the post author
404
                    "is_repost": post.is_repost,
405
                    "reposted_by": {
406
                        "id": post.reposted_by.id,
407
                        "username": post.reposted_by.username,
408
                        "email": post.reposted_by.email,
409
                        "first_name": post.reposted_by.first_name,
410
                        "last_name": post.reposted_by.last_name,
411
                        "avatar_url": post.reposted_by.get_avatar_url(),
412
                    },
413
                }
414
                posts_data.append(post_data)
×
415
                post_count += 1
×
416
                continue
×
417

418
            if post.id in all_posts_so_far:
1✔
419
                continue
×
420

421
            post_data = {
1✔
422
                "id": post.id,
423
                "is_reported": post.id in reported_posts,
424
                "title": post.title,
425
                "content": post.content,
426
                "image_urls": post.image_urls,
427
                "date_created": post.date_created,
428
                "user_id": post.user.id,
429
                "user_fullname": post.user.get_full_name(),
430
                "user_avatar": post.user.get_avatar_url(),
431
                "comments_count": post.comments_count,
432
                "user_karma": post.user.get_karma(),
433
                "likes_count": post.likes_count,
434
                "user_has_liked": post.id
435
                in user_likes_dict,  # Check if the user has liked the post
436
                "like_type": user_likes_dict.get(
437
                    post.id
438
                ),  # Get the like_type if the user has liked the post
439
                "is_following_author": post.user.id in current_user_following_set,
440
                # Check if the current user is following the post author
441
            }
442
            posts_data.append(post_data)
1✔
443
            post_count += 1
1✔
444

445
        return JsonResponse(
1✔
446
            {
447
                "posts": posts_data,
448
                "has_more": len(posts) > (offset + limit),
449
                # Indicate if there are more posts to fetch
450
            },
451
            safe=False,
452
            status=200,
453
        )
454

455
    return JsonResponse({"error": "Method not allowed"}, status=405)
1✔
456

457

458
# Get a single post by ID
459
def get_post(request, post_id):
1✔
460
    if request.method == "GET":
1✔
461
        try:
1✔
462
            print(f"All posts in DB: {Post.objects.all().values_list('id', flat=True)}")
1✔
463
            post = get_object_or_404(Post, id=post_id)
1✔
464
        except Post.DoesNotExist:
1✔
465
            return JsonResponse({"error": "Post not found"}, status=404)
×
466
        post_data = {
1✔
467
            "id": post.id,
468
            "title": post.title,
469
            "content": post.content,
470
            "image_urls": post.image_urls,
471
            "date_created": post.date_created,
472
            "user": post.user.get_full_name(),
473
            "comments": [
474
                {
475
                    "id": comment.id,
476
                    "content": comment.content,
477
                    "date_created": comment.date_created,
478
                    "user": comment.user.get_full_name(),
479
                }
480
                for comment in post.comments.all()
481
            ],
482
            "likes_count": post.likes.count(),
483
        }
484
        return JsonResponse(post_data, status=200)
1✔
485

486
    return JsonResponse({"error": "Method not allowed"}, status=405)
1✔
487

488

489
# Create a comment on a post
490
@csrf_exempt
1✔
491
def comments(request, post_id):
1✔
492
    if request.method == "POST":
1✔
493
        data = parse_json_request(request)
1✔
494
        if not data:
1✔
495
            return JsonResponse({"error": "Invalid JSON data"}, status=400)
×
496

497
        user_id = data.get("user_id")
1✔
498
        content = data.get("content")
1✔
499
        parent_comment_id = data.get(
1✔
500
            "parent_comment_id"
501
        )  # Optional field for nested comments
502
        is_edit = data.get(
1✔
503
            "is_edit", False
504
        )  # Optional field to indicate if it's an edit
505

506
        if is_edit:
1✔
507
            # parent comment id is the comment id of the comment that is being edited
508
            if not parent_comment_id:
1✔
509
                return JsonResponse(
×
510
                    {"error": "comment_id is required for editing"}, status=400
511
                )
512
            try:
1✔
513
                print("Editing comment with ID:", parent_comment_id)
1✔
514
                print("content:", content)
1✔
515
                comment = Comment.objects.get(id=parent_comment_id, user_id=user_id)
1✔
516
                comment.content = content
1✔
517
                comment.save()
1✔
518
                return JsonResponse(
1✔
519
                    {
520
                        "id": comment.id,
521
                        "content": comment.content,
522
                        "date_created": comment.date_created,
523
                        "user": comment.user.get_full_name(),
524
                        "parent_comment_id": (
525
                            comment.parent_comment.id
526
                            if comment.parent_comment
527
                            else None
528
                        ),
529
                        "status": 200,
530
                    },
531
                    status=200,
532
                )
533
            except Comment.DoesNotExist:
1✔
534
                return JsonResponse({"error": "Comment not found"}, status=404)
1✔
535

536
        if not user_id or not content:
1✔
537
            return JsonResponse(
1✔
538
                {"error": "user_id and content are required"}, status=400
539
            )
540

541
        post = get_object_or_404(Post, id=post_id)
1✔
542

543
        try:
1✔
544
            # Fetch the user by ID
545
            user = User.objects.get(id=user_id)
1✔
546
        except User.DoesNotExist:
×
547
            print("User not found")
×
548
            return JsonResponse({"error": "User not found"}, status=404)
×
549

550
        # Check if this is a nested comment (reply to another comment)
551
        parent_comment = None
1✔
552
        if parent_comment_id:
1✔
553
            try:
1✔
554
                parent_comment = Comment.objects.get(id=parent_comment_id, post=post)
1✔
555
            except Comment.DoesNotExist:
×
556
                print("Parent comment not found")
×
557
                return JsonResponse({"error": "Parent comment not found"}, status=404)
×
558

559
        # increase karma by 2 for the owner of the post
560
        post_user = post.user
1✔
561
        post_user.karma += 2
1✔
562
        post_user.save()
1✔
563

564
        # Create the comment
565
        comment = Comment.objects.create(
1✔
566
            user=user,
567
            post=post,
568
            content=content,
569
            parent_comment=parent_comment,  # Set to None if not a nested comment
570
        )
571
        return JsonResponse(
1✔
572
            {
573
                "id": comment.id,
574
                "content": comment.content,
575
                "date_created": comment.date_created,
576
                "user": comment.user.get_full_name(),
577
                "parent_comment_id": (
578
                    comment.parent_comment.id if comment.parent_comment else None
579
                ),
580
                "status": 201,
581
            },
582
            status=201,
583
        )
584
    elif request.method == "GET":
1✔
585
        try:
1✔
586
            # Get query parameters
587
            user_id = request.GET.get("user_id")  # Required: Current user ID
1✔
588
            parent_comment_id = request.GET.get(
1✔
589
                "parent_comment_id", 0
590
            )  # Optional: Parent comment ID
591
            page = int(
1✔
592
                request.GET.get("page", 1)
593
            )  # Pagination: Current page (default: 1)
594
            limit = int(
1✔
595
                request.GET.get("limit", 5)
596
            )  # Pagination: Comments per page (default: 5)
597

598
            if not user_id:
1✔
599
                return JsonResponse({"error": "user_id is required"}, status=400)
×
600

601
            # Fetch the parent comment if parent_comment_id is provided
602
            parent_comment = None
1✔
603
            if parent_comment_id != 0:
1✔
604
                try:
1✔
605
                    parent_comment = Comment.objects.get(
1✔
606
                        id=parent_comment_id, post_id=post_id
607
                    )
608
                except Comment.DoesNotExist:
×
609
                    pass
610

611
            # Fetch all likes by the current user for comments in this post
612
            user_likes = CommentLike.objects.filter(
1✔
613
                user_id=user_id,
614
                comment__post_id=post_id,  # Filter likes for comments in this post
615
            ).values("comment_id", "like_type")
616

617
            # Convert user_likes into a dictionary for quick lookup
618
            user_likes_dict = {
1✔
619
                like["comment_id"]: like["like_type"] for like in user_likes
620
            }
621

622
            # Subquery to count replies for each comment
623
            replies_subquery = (
1✔
624
                Comment.objects.filter(parent_comment_id=OuterRef("id"))
625
                .values("parent_comment_id")
626
                .annotate(count=Count("id"))
627
                .values("count")
628
            )
629

630
            # Annotate comments with the total number of likes and replies
631
            comments_query = (
1✔
632
                Comment.objects.filter(
633
                    post_id=post_id, parent_comment=parent_comment
634
                )  # Filter by post and parent comment
635
                .select_related("user")
636
                .annotate(
637
                    likes_count=Count(
638
                        "comment_likes", distinct=True
639
                    ),  # Count total likes for each comment
640
                    replies_count=Subquery(
641
                        replies_subquery, output_field=IntegerField()
642
                    ),  # Count total replies for each comment
643
                )
644
                .order_by("-date_created")  # Order by most recent
645
            )
646

647
            # Paginate the comments
648
            paginator = Paginator(comments_query, limit)
1✔
649
            page_comments = paginator.get_page(page)
1✔
650

651
            # Serialize the comments along with user details and like information
652
            comments_data = [
1✔
653
                {
654
                    "id": comment.id,
655
                    "post_id": comment.post_id,
656
                    "content": comment.content,
657
                    "date_created": comment.date_created,
658
                    "user": {
659
                        "id": comment.user.id,
660
                        "avatar_url": comment.user.avatar_url,
661
                        "email": comment.user.email,  # Only include if necessary
662
                        "first_name": comment.user.first_name,
663
                        "last_name": comment.user.last_name,
664
                        "user_karma": comment.user.karma,
665
                    },
666
                    "likes_count": comment.likes_count,  # Total likes on the comment
667
                    "replies_count": comment.replies_count
668
                    or 0,  # Total replies to the comment
669
                    # Check if the current user has liked the comment
670
                    "user_has_liked": comment.id in user_likes_dict,
671
                    "like_type": user_likes_dict.get(
672
                        comment.id
673
                    ),  # Get the like_type if the user has liked the comment
674
                }
675
                for comment in page_comments
676
            ]
677

678
            # Return the paginated comments and pagination metadata
679
            return JsonResponse(
1✔
680
                {
681
                    "comments": comments_data,
682
                    # Indicates if there are more comments to load
683
                    "has_more": page_comments.has_next(),
684
                    "status": 200,
685
                },
686
                safe=False,
687
                status=200,
688
            )
689

690
        except Exception as e:
×
691
            print("Error fetching comments:", str(e))  # Logs for debugging
×
692
            return JsonResponse(
×
693
                {"error": "Internal server error", "status": 500}, status=500
694
            )
695
    return JsonResponse({"error": "Method not allowed", "status": 405}, status=405)
1✔
696

697

698
# Like a post
699
@csrf_exempt
1✔
700
def like_post(request, post_id):
1✔
701
    if request.method == "POST":
1✔
702
        data = parse_json_request(request)
1✔
703
        if not data:
1✔
704
            return JsonResponse({"error": "Invalid JSON data"}, status=400)
×
705
        user_id = data.get("user_id")
1✔
706
        is_liked = data.get("is_liked")
1✔
707
        like_type = data.get("like_type")
1✔
708
        user = User.objects.get(id=user_id)
1✔
709
        post = get_object_or_404(Post, id=post_id)
1✔
710
        # Check if the user has already liked the post
711
        # if like_type in ["Like", "Clap", "Support", "Heart", "Bulb", "Laugh"]
712
        # and Like.objects.filter(user=user, post=post, like_type=like_type).exists()::
713
        if Like.objects.filter(user=user, post=post).exists():
1✔
714
            if not is_liked:
1✔
715
                # remove
716
                Like.objects.filter(user=user, post=post).delete()
1✔
717
                # reducer karma by 1 for the owner of the post
718
                post_user = post.user
1✔
719
                post_user.karma -= 1
1✔
720
                post_user.save()
1✔
721
            else:
722
                # update
723
                Like.objects.filter(user=user, post=post).update(like_type=like_type)
1✔
724
        else:
725
            # Create a new like
726
            Like.objects.create(user=user, post=post, like_type=like_type)
1✔
727
            # increase karma by 1 for the owner of the post
728
            post_user = post.user
1✔
729
            post_user.karma += 1
1✔
730
            post_user.save()
1✔
731
        return JsonResponse(
1✔
732
            {
733
                "message": "Post liked successfully",
734
                "likes_count": post.likes.count(),
735
                "status": 201,
736
            },
737
            status=201,
738
        )
739

740
    return JsonResponse({"error": "Method not allowed"}, status=405)
1✔
741

742

743
# Unlike a post
744
@csrf_exempt
1✔
745
def unlike_post(request, post_id):
1✔
746
    if request.method == "POST":
×
747
        user = request.user
×
748
        post = get_object_or_404(Post, id=post_id)
×
749

750
        like = Like.objects.filter(user=user, post=post).first()
×
751
        if not like:
×
752
            return JsonResponse({"error": "You have not liked this post"}, status=400)
×
753

754
        like.delete()
×
755
        return JsonResponse(
×
756
            {"message": "Post unliked successfully", "likes_count": post.likes.count()},
757
            status=200,
758
        )
759

760
    return JsonResponse({"error": "Method not allowed"}, status=405)
×
761

762

763
@csrf_exempt
1✔
764
def follow_unfollow_user(request, user_id):
1✔
765
    """
766
    View to handle follow/unfollow actions..
767
    - POST: Follow a user.
768
    - DELETE: Unfollow a user.
769
    """
770

771
    try:
1✔
772
        data = parse_json_request(request)
1✔
773
        follow = data.get("follow")
1✔
774
        main_user_id = data.get("user_id")
1✔
775
        post_user_id = user_id
1✔
776
        # Get the main user and post user
777
        main_user = User.objects.get(id=main_user_id)
1✔
778
        post_user = User.objects.get(id=post_user_id)
1✔
779
        if follow:
1✔
780
            notification_service.send_to_user(
1✔
781
                user_id=post_user.id,
782
                title="New Follower",
783
                body=f"{main_user.first_name} has followed you!",
784
                data={
785
                    "url": "/messages/123",
786
                    "type": "follow",
787
                    "title": "New Follower",
788
                    "body": f"{main_user.first_name} has followed you!",
789
                },
790
            )
791

792
        if follow:
1✔
793
            # Check if the main user is already following the post user
UNCOV
794
            if main_user.following.filter(id=post_user_id).exists():
×
UNCOV
795
                return JsonResponse(
×
796
                    {"error": "You are already following this user"}, status=400
797
                )
798

799
            # Create a new follow relationship
UNCOV
800
            Follow.objects.create(main_user=main_user, following_user=post_user)
×
801
            # increase karma by 3 if someone follows you
UNCOV
802
            post_user.karma += 3
×
UNCOV
803
            post_user.save()
×
UNCOV
804
            return JsonResponse({"message": "User followed successfully"}, status=201)
×
805

806
        else:
807
            # Check if the main user is following the post user
808
            follow_relationship = Follow.objects.filter(
1✔
809
                main_user=main_user, following_user=post_user
810
            )
811
            if not follow_relationship.exists():
1✔
812
                return JsonResponse(
1✔
813
                    {"error": "You are not following this user"}, status=400
814
                )
815

816
            # Delete the follow relationship
817
            follow_relationship.delete()
1✔
818
            # reduce karma by 3 if someone unfollows you
819
            post_user.karma -= 3
1✔
820
            post_user.save()
1✔
821
            return JsonResponse({"message": "User unfollowed successfully"}, status=200)
1✔
822

823
    except User.DoesNotExist:
1✔
824
        return JsonResponse({"error": "User not found"}, status=404)
1✔
825
    except Exception as e:
1✔
826
        return JsonResponse({"error": str(e)}, status=500)
1✔
827

828

829
@csrf_exempt
1✔
830
def like_comment(request, comment_id):
1✔
831
    if request.method == "POST":
1✔
832
        data = parse_json_request(request)
1✔
833
        if not data:
1✔
834
            return JsonResponse(
×
835
                {"error": "Invalid JSON data", "status": 400}, status=400
836
            )
837

838
        user_id = data.get("user_id")
1✔
839
        is_liked = data.get("is_liked", False)  # Default to False if not provided
1✔
840
        like_type = data.get("like_type", "like")  # Default to "like" if not provided
1✔
841

842
        # Check if the user exists
843
        user = get_object_or_404(User, id=user_id)
1✔
844

845
        # Check if the comment exists
846
        comment = get_object_or_404(Comment, id=comment_id)
1✔
847

848
        # Check if the user has already liked the comment
849
        comment_like = CommentLike.objects.filter(user=user, comment=comment).first()
1✔
850

851
        if is_liked:
1✔
852
            # If the user wants to like the comment
853
            if comment_like:
1✔
854
                # Update the like type if the user has already liked the comment
855
                comment_like.like_type = like_type
1✔
856
                comment_like.save()
1✔
857
                message = "Comment like updated successfully"
1✔
858
            else:
859
                # Create a new like
860
                CommentLike.objects.create(
1✔
861
                    user=user, comment=comment, like_type=like_type
862
                )
863
                message = "Comment liked successfully"
1✔
864
        else:
865
            # If the user wants to unlike the comment
866
            if comment_like:
1✔
867
                comment_like.delete()
1✔
868
                message = "Comment unliked successfully"
1✔
869
            else:
870
                return JsonResponse(
1✔
871
                    {"error": "You have not liked this comment", "status": 400},
872
                    status=400,
873
                )
874
        # Return the updated likes count and success message
875
        return JsonResponse(
1✔
876
            {
877
                "message": message,
878
                "likes_count": comment.comment_likes.count(),  # Use the related_name
879
                "status": 201,
880
            },
881
            status=201,
882
        )
883

884
    return JsonResponse({"error": "Method not allowed", "status": 405}, status=405)
1✔
885

886

887
@csrf_exempt
1✔
888
def report_post(request, post_id):
1✔
889
    if request.method == "POST":
1✔
890
        data = parse_json_request(request)
1✔
891
        if not data:
1✔
892
            return JsonResponse({"error": "Invalid JSON data"}, status=400)
×
893

894
        reporting_user_id = data.get("reporting_user_id")
1✔
895
        post_owner_id = data.get("post_owner_id")
1✔
896
        repost_user_id = data.get("repost_user_id")
1✔
897

898
        # Check if the reporting user exists
899
        try:
1✔
900
            reporting_user = User.objects.get(id=reporting_user_id)
1✔
901
        except User.DoesNotExist:
1✔
902
            return JsonResponse({"error": "Reporting user not found"}, status=404)
1✔
903

904
        # Check if the post owner exists
905
        try:
1✔
906
            post_owner = User.objects.get(id=post_owner_id)
1✔
907
        except User.DoesNotExist:
1✔
908
            return JsonResponse({"error": "Post owner not found"}, status=404)
1✔
909

910
        # Check if the repost user exists (if provided)
911
        repost_user = None
1✔
912
        if repost_user_id:
1✔
913
            try:
1✔
914
                repost_user = User.objects.get(id=repost_user_id)
1✔
915
            except User.DoesNotExist:
1✔
916
                return JsonResponse({"error": "Repost user not found"}, status=404)
1✔
917

918
        # Check if the post exists
919
        try:
1✔
920
            post = Post.objects.get(id=post_id)
1✔
921
        except Post.DoesNotExist:
1✔
922
            return JsonResponse({"error": "Post not found"}, status=404)
1✔
923

924
        # Check if the user has already reported this post
925
        existing_report = ReportPost.objects.filter(
1✔
926
            post=post,
927
            reporting_user=reporting_user,
928
            post_owner=post_owner,
929
        ).exists()
930

931
        if existing_report:
1✔
932
            return JsonResponse(
1✔
933
                {"error": "You have already reported this post"}, status=400
934
            )
935

936
        # Create a new report
937
        ReportPost.objects.create(
1✔
938
            post=post,
939
            reporting_user=reporting_user,
940
            post_owner=post_owner,
941
            is_repost=bool(repost_user),  # Set is_repost based on repost_user
942
        )
943

944
        # Deduct karma from the post owner
945
        post_owner.karma -= 10
1✔
946
        post_owner.save()
1✔
947

948
        notification_service.send_to_user(
1✔
949
            user_id=post_owner.id,
950
            title="Post Reported",
951
            body="People are reporting your posts",
952
            data={
953
                "url": "/messages/123",
954
                "type": "report",
955
                "title": "Post Reported",
956
                "body": "People are reporting your post",
957
            },
958
        )
959

960
        # Deduct karma from the repost user (if applicable)
UNCOV
961
        if repost_user:
×
UNCOV
962
            repost_user.karma -= 10
×
UNCOV
963
            repost_user.save()
×
964

UNCOV
965
        return JsonResponse({"message": "Post reported successfully"}, status=201)
×
966

967
    return JsonResponse({"error": "Method not allowed"}, status=405)
1✔
968

969

970
@csrf_exempt
1✔
971
def report_comment(request):
1✔
972
    if request.method != "POST":
1✔
973
        return JsonResponse(
1✔
974
            {"error": "Only POST requests are allowed."},
975
            status=405,
976
        )
977

978
    try:
1✔
979
        # Parse the JSON data from the request body
980
        data = parse_json_request(request)
1✔
981
        if not data:
1✔
982
            return JsonResponse(
1✔
983
                {"error": "Invalid JSON data."},
984
                status=400,
985
            )
986
        comment_id = data.get("comment_id")
1✔
987
        reporting_user_id = data.get("user_id")
1✔
988
        reason = data.get("reason")  # Optional reason field
1✔
989
    except json.JSONDecodeError:
×
990
        return JsonResponse(
×
991
            {"error": "Invalid JSON data."},
992
            status=400,
993
        )
994

995
    if not comment_id or not reporting_user_id:
1✔
996
        return JsonResponse(
1✔
997
            {"error": "Both comment_id and user_id are required."},
998
            status=400,
999
        )
1000

1001
    # Fetch the comment and reporting user
1002
    comment = get_object_or_404(Comment, id=comment_id)
1✔
1003
    reporting_user = get_object_or_404(get_user_model(), id=reporting_user_id)
1✔
1004

1005
    # Check if the user has already reported this comment
1006
    if ReportComment.objects.filter(
1✔
1007
        comment=comment, reporting_user=reporting_user
1008
    ).exists():
1009
        return JsonResponse(
1✔
1010
            {"error": "User Already Reported This Comment."},
1011
            status=400,
1012
        )
1013

1014
    # Create the report with the reason (if provided)
1015
    ReportComment.objects.create(
1✔
1016
        comment=comment,
1017
        reporting_user=reporting_user,
1018
        reason=reason,  # Optional reason field
1019
    )
1020

1021
    # Decrease the karma of the user who created the comment by 5
1022
    comment_user = comment.user
1✔
1023
    comment_user.karma -= 5
1✔
1024
    comment_user.save()
1✔
1025

1026
    return JsonResponse(
1✔
1027
        {"message": "Comment reported successfully. Karma decreased by 5."},
1028
        status=201,
1029
    )
1030

1031

1032
@csrf_exempt
1✔
1033
def delete_comment(request, post_id, comment_id):
1✔
1034
    if request.method != "DELETE":
1✔
1035
        return JsonResponse(
1✔
1036
            {"error": "Only DELETE requests are allowed."},
1037
            status=405,
1038
        )
1039

1040
    try:
1✔
1041
        # Fetch the post and comment
1042
        post = get_object_or_404(Post, id=post_id)
1✔
1043
        comment = get_object_or_404(Comment, id=comment_id, post=post)
1✔
1044

1045
        # Count the number of comments before deletion
1046
        comments_before = Comment.objects.filter(post=post).count()
1✔
1047

1048
        # Delete the comment (this will cascade to delete all replies)
1049
        comment.delete()
1✔
1050

1051
        # Count the number of comments after deletion
1052
        comments_after = Comment.objects.filter(post=post).count()
1✔
1053

1054
        # Calculate the number of comments deleted
1055
        total_deleted = comments_before - comments_after
1✔
1056

1057
        return JsonResponse(
1✔
1058
            {
1059
                "message": "Comment deleted successfully.",
1060
                "total_deleted": total_deleted,
1061
                "status": 200,
1062
            },
1063
            status=200,
1064
        )
1065
    except Comment.DoesNotExist:
×
1066
        return JsonResponse(
×
1067
            {"error": "Comment not found."},
1068
            status=404,
1069
        )
1070
    except Exception as e:
×
1071
        return JsonResponse(
×
1072
            {"error": str(e)},
1073
            status=500,
1074
        )
1075

1076

1077
@csrf_exempt
1✔
1078
def delete_post(request, post_id):
1✔
1079
    if request.method != "DELETE":
1✔
1080
        return JsonResponse(
1✔
1081
            {"error": "Only DELETE requests are allowed."},
1082
            status=405,
1083
        )
1084

1085
    # first get the post, store it
1086
    # then delete all the posts where original_post = post
1087
    # then delete the post itself
1088
    try:
1✔
1089
        post = get_object_or_404(Post, id=post_id)
1✔
1090
        # Delete all reposts of this post
1091
        Post.objects.filter(original_post=post).delete()
1✔
1092
        # Delete the original post
1093
        post.delete()
1✔
1094

1095
        return JsonResponse(
1✔
1096
            {
1097
                "message": "Post and its reposts deleted successfully.",
1098
                "status": 200,
1099
            },
1100
            status=200,
1101
        )
1102
    except Post.DoesNotExist:
×
1103
        return JsonResponse(
×
1104
            {"error": "Post not found."},
1105
            status=404,
1106
        )
1107
    except Exception as e:
×
1108
        return JsonResponse(
×
1109
            {"error": str(e)},
1110
            status=500,
1111
        )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc