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

gcivil-nyu-org / fall24-monday-team4 / 437

16 Dec 2024 08:19PM UTC coverage: 96.872% (-3.0%) from 99.892%
437

Pull #171

travis-pro

web-flow
Merge pull request #170 from gcivil-nyu-org/all_branch_merge

Develop V6.1.2
Pull Request #171: Production V2

153 of 230 new or added lines in 14 files covered. (66.52%)

17 existing lines in 4 files now uncovered.

3004 of 3101 relevant lines covered (96.87%)

0.97 hits per line

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

98.59
/user_profile/views.py
1
from .models import UserProfile, FamilyMembers
1✔
2
from django.contrib.auth.models import User
1✔
3
from accounts.models import UserReports
1✔
4
from django.shortcuts import render, get_object_or_404, redirect
1✔
5
from django.contrib.auth.decorators import login_required
1✔
6
from utils.s3_utils import (
1✔
7
    upload_file_to_s3,
8
    generate_presigned_url,
9
    delete_file_from_s3,
10
)
11
import uuid
1✔
12
from django.http import JsonResponse
1✔
13
from .decorators import verification_required
1✔
14
from django.views.decorators.http import require_http_methods
1✔
15
from utils.email_utils import FamilyMemberEmails, validate_family_members_input
1✔
16
from django.template.loader import render_to_string
1✔
17
import json
1✔
18

19

20
@login_required(login_url="home")
1✔
21
@verification_required
1✔
22
def profile_view(request, user_id=None):
1✔
23
    user_to_view = None
1✔
24
    familyMembers = None
1✔
25

26
    if user_id is None:
1✔
27
        profile = get_object_or_404(UserProfile, user=request.user)
1✔
28
        is_user = True
1✔
29
        family_members = FamilyMembers.objects.filter(user=request.user)
1✔
30
        familyMembers = [
1✔
31
            {
32
                "id": member.id,
33
                "full_name": member.full_name,
34
                "email": member.email,
35
            }
36
            for member in family_members
37
        ]
38
    else:
39
        user_to_view = get_object_or_404(User, id=user_id)
1✔
40
        profile = get_object_or_404(UserProfile, user=user_to_view)
1✔
41
        is_user = user_id == request.user.id
1✔
42

43
    if request.method == "POST":
1✔
44
        if is_user:
1✔
45
            first_name = request.POST.get("first_name", "").strip()
1✔
46
            last_name = request.POST.get("last_name", "").strip()
1✔
47

48
            request.user.first_name = first_name
1✔
49
            request.user.last_name = last_name
1✔
50
            request.user.save()
1✔
51

52
            new_bio = request.POST.get("bio", "").strip()
1✔
53
            profile.bio = new_bio
1✔
54
            profile.save()
1✔
55
            return redirect("profile")
1✔
56

57
    if profile.photo_key:
1✔
58
        profile_picture_url = generate_presigned_url(profile.photo_key, expiration=3600)
1✔
59
    else:
60
        profile_picture_url = None
1✔
61

62
    return render(
1✔
63
        request,
64
        "profile/user_profile.html",
65
        {
66
            "profile": profile,
67
            "is_user": is_user,
68
            "profile_picture_url": profile_picture_url,
69
            "user_to_view": user_to_view,
70
            "family_members": familyMembers,
71
        },
72
    )
73

74

75
@login_required(login_url="home")
1✔
76
@verification_required
1✔
77
@require_http_methods(["POST"])
1✔
78
def update_family_members(request):
1✔
79
    try:
1✔
80
        data = json.loads(request.body)
1✔
81

82
        # Check for duplicate emails in submitted data
83
        email_list = [member["email"] for member in data]
1✔
84
        if len(email_list) != len(set(email_list)):
1✔
85
            return JsonResponse(
1✔
86
                {
87
                    "success": False,
88
                    "error": "Duplicate email addresses are not allowed.",
89
                },
90
                status=400,
91
            )
92

93
        is_valid, error_message = validate_family_members_input(data)
1✔
94
        if not is_valid:
1✔
95
            return JsonResponse({"success": False, "error": error_message}, status=400)
1✔
96

97
        # Get existing members and create sets of emails only
98
        existing_members = FamilyMembers.objects.filter(user=request.user)
1✔
99
        existing_emails = {member.email for member in existing_members}
1✔
100
        new_emails = {member["email"] for member in data}
1✔
101

102
        # Find emails to remove and add
103
        emails_to_remove = existing_emails - new_emails
1✔
104
        emails_to_add = new_emails - existing_emails
1✔
105

106
        # Handle removals
107
        if emails_to_remove:
1✔
108
            removed_members = existing_members.filter(email__in=emails_to_remove)
1✔
109
            removed_members.delete()
1✔
110

111
            html_message_removed = render_to_string(
1✔
112
                "emails/removed_fam_email.html",
113
                {"username": request.user.username},
114
            )
115
            FamilyMemberEmails(
1✔
116
                list(emails_to_remove),
117
                html_message_removed,
118
                f"You've Been Removed from {request.user.username}'s Family List",
119
            )
120

121
        # Handle additions and updates
122
        for member_data in data:
1✔
123
            FamilyMembers.objects.update_or_create(
1✔
124
                user=request.user,
125
                email=member_data["email"],
126
                defaults={"full_name": member_data["name"]},
127
            )
128

129
        # Send welcome emails only to new members
130
        if emails_to_add:
1✔
131
            html_message_added = render_to_string(
1✔
132
                "emails/welcome_fam_email.html",
133
                {"username": request.user.username},
134
            )
135
            FamilyMemberEmails(
1✔
136
                list(emails_to_add),
137
                html_message_added,
138
                f"You've Been Added to {request.user.username}'s Family List",
139
            )
140

141
        return JsonResponse({"success": True})
1✔
142

UNCOV
143
    except Exception as e:
×
UNCOV
144
        return JsonResponse({"success": False, "error": str(e)})
×
145

146

147
@login_required(login_url="home")
1✔
148
@verification_required
1✔
149
@require_http_methods(["POST"])
1✔
150
def upload_profile_picture(request):
1✔
151
    if request.FILES.get("photo"):
1✔
152
        file = request.FILES["photo"]
1✔
153

154
        try:
1✔
155
            profile = get_object_or_404(UserProfile, user=request.user)
1✔
156
            unique_key = str(uuid.uuid4())
1✔
157

158
            if upload_file_to_s3(file, unique_key):
1✔
159

160
                if profile.photo_key:
1✔
161
                    delete_file_from_s3(profile.photo_key)
1✔
162

163
                profile.photo_key = unique_key
1✔
164
                profile.file_name = file.name
1✔
165
                profile.file_type = file.content_type
1✔
166
                profile.save()
1✔
167

168
                return JsonResponse({"success": True})
1✔
169

170
            return JsonResponse({"success": False, "error": "Upload failed"})
1✔
171
        except Exception as e:
1✔
172
            return JsonResponse({"success": False, "error": str(e)})
1✔
173
    return JsonResponse({"success": False, "error": "Photo attachment not found."})
1✔
174

175

176
@login_required(login_url="home")
1✔
177
@verification_required
1✔
178
@require_http_methods(["POST"])
1✔
179
def report_user(request):
1✔
180
    subject = request.POST.get("subject")
1✔
181
    description = request.POST.get("description")
1✔
182
    reported_user_id = request.POST.get("reported_user_id")
1✔
183
    reporter = request.user
1✔
184

185
    try:
1✔
186
        reported_user = User.objects.get(id=reported_user_id)
1✔
187

188
        report = UserReports(
1✔
189
            reporter=reporter,
190
            reported_user=reported_user,
191
            subject=subject,
192
            description=description,
193
        )
194
        report.save()
1✔
195
        return JsonResponse({"success": True})
1✔
196
    except Exception as e:
1✔
197
        return JsonResponse({"success": False, "error_message": str(e)})
1✔
198

199

200
@login_required(login_url="home")
1✔
201
@verification_required
1✔
202
@require_http_methods(["POST"])
1✔
203
def remove_profile_picture(request):
1✔
204
    user = request.user
1✔
205
    try:
1✔
206
        profile = UserProfile.objects.get(user=user)
1✔
207

208
        if profile.photo_key:
1✔
209
            result = delete_file_from_s3(profile.photo_key)
1✔
210
            if not result:
1✔
211
                print(
1✔
212
                    f"Failed to delete old profile picture with key: {profile.photo_key}"
213
                )
214

215
            profile.photo_key = None
1✔
216
            profile.file_name = None
1✔
217
            profile.file_type = None
1✔
218
            profile.save()
1✔
219

220
            return JsonResponse({"success": True})
1✔
221
        return JsonResponse(
1✔
222
            {"success": False, "error_message": "No profile picture to remove."}
223
        )
224
    except Exception as e:
1✔
225
        return JsonResponse({"success": False, "error_message": str(e)})
1✔
226

227

228
@login_required(login_url="home")
1✔
229
@verification_required
1✔
230
@require_http_methods(["POST"])
1✔
231
def update_social_handles(request):
1✔
232
    try:
1✔
233
        profile = UserProfile.objects.get(user=request.user)
1✔
234

235
        instagram = request.POST.get("instagram", "").strip() or None
1✔
236
        facebook = request.POST.get("facebook", "").strip() or None
1✔
237
        twitter = request.POST.get("twitter", "").strip() or None
1✔
238

239
        profile.instagram_handle = instagram
1✔
240
        profile.facebook_handle = facebook
1✔
241
        profile.twitter_handle = twitter
1✔
242
        profile.save()
1✔
243

244
        return JsonResponse({"success": True})
1✔
245
    except Exception as e:
1✔
246
        return JsonResponse({"success": False, "error_message": str(e)})
1✔
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