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

gcivil-nyu-org / INT2-Monday-Spring2024-Team-2 / 700

14 Apr 2024 02:25PM UTC coverage: 89.474% (-1.6%) from 91.062%
700

Pull #221

travis-pro

wuyasan
update the comments
Pull Request #221: Community v2

74 of 91 new or added lines in 8 files covered. (81.32%)

16 existing lines in 1 file now uncovered.

1683 of 1881 relevant lines covered (89.47%)

0.89 hits per line

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

68.88
/Dashboard/views.py
1
from django.shortcuts import render, redirect
1✔
2
from django.contrib.auth import logout
1✔
3
from django.contrib.auth.decorators import login_required
1✔
4
from .forms.tutor_info import (
1✔
5
    TutorForm,
6
    AvailabilityForm,
7
    TutorImageForm,
8
    TutorTranscriptForm,
9
)
10
from .forms.student_info import StudentForm, StudentImageForm
1✔
11
from .forms.review_form import TutorReviewForm
1✔
12
from TutorRegister.models import (
1✔
13
    Expertise,
14
    Availability,
15
    ProfileT,
16
    ProfileS,
17
    TutoringSession,
18
    TutorReview,
19
)
20
from django.urls import reverse
1✔
21
from django.http import HttpResponseRedirect
1✔
22
import json
1✔
23
from datetime import datetime, time, date
1✔
24
from django.db.models import Q
1✔
25
from PIL import Image, ImageDraw, ImageOps
1✔
26
from io import BytesIO
1✔
27
from django.core.files.base import ContentFile
1✔
28
from django.core.mail import send_mail
1✔
29
from django.core.mail import EmailMessage
1✔
30
from django.template.loader import render_to_string
1✔
31
from django.contrib.auth.models import User
1✔
32
from django.utils import timezone
1✔
33
from django.http import FileResponse
1✔
34
from django.shortcuts import get_object_or_404
1✔
35
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
1✔
36
from django.views.decorators.csrf import csrf_exempt
1✔
37

38
import mimetypes
1✔
39

40

41
@login_required
1✔
42
def TutorInformation(request):
1✔
43
    if request.user.usertype.user_type == "tutor":
1✔
44
        initial_availabilities_json = "[]"
1✔
45
        tutor_form = TutorForm()
1✔
46
        try:
1✔
47
            tutor_image_form = TutorImageForm(
1✔
48
                instance=ProfileT.objects.get(user=request.user)
49
            )
50
            tutor_transcript_form = TutorTranscriptForm(
1✔
51
                instance=ProfileT.objects.get(user=request.user)
52
            )
53
        except ProfileT.DoesNotExist:
×
54
            tutor_image_form = TutorImageForm()
×
55
            tutor_transcript_form = TutorTranscriptForm()
×
56
        existing_expertise = list(
1✔
57
            Expertise.objects.filter(user=request.user).values_list(
58
                "subject", flat=True
59
            )
60
        )
61

62
        if request.method == "POST":
1✔
63
            profile, created = ProfileT.objects.get_or_create(user=request.user)
1✔
64
            tutor_form = TutorForm(request.POST, instance=profile)
1✔
65
            tutor_image_form = TutorImageForm(
1✔
66
                request.POST, request.FILES, instance=profile
67
            )
68
            tutor_transcript_form = TutorTranscriptForm(
1✔
69
                request.POST, request.FILES, instance=profile
70
            )
71
            availability_form = AvailabilityForm(request.POST)
1✔
72
            if (
1✔
73
                tutor_form.is_valid()
74
                and tutor_image_form.is_valid()
75
                and tutor_transcript_form.is_valid()
76
                and availability_form.is_valid()
77
            ):
78
                user = request.user
1✔
79
                profile = tutor_form.save(commit=False)
1✔
80
                # Handle the image field separately using tutor_image_form
81
                if "image" in request.FILES:
1✔
82
                    image = Image.open(request.FILES["image"])
×
83
                    image = image.resize((300, 300), Image.Resampling.LANCZOS)
×
84
                    # Resize and fill with white background
85
                    new_image = Image.new("RGB", (300, 300), (255, 255, 255))
×
86
                    image.thumbnail((300, 300), Image.Resampling.LANCZOS)
×
87
                    new_image.paste(
×
88
                        image, ((300 - image.width) // 2, (300 - image.height) // 2)
89
                    )
90

91
                    # Crop as a circle
92
                    mask = Image.new("L", (300, 300), 0)
×
93
                    draw = ImageDraw.Draw(mask)
×
94
                    draw.ellipse((0, 0, 300, 300), fill=255)
×
95
                    circle_image = ImageOps.fit(
×
96
                        new_image, (300, 300), centering=(0.5, 0.5)
97
                    )
98
                    circle_image.putalpha(mask)
×
99

100
                    # Save the image
101
                    image_io = BytesIO()
×
102
                    circle_image.save(image_io, format="PNG")
×
103
                    image_name = request.FILES["image"].name
×
104
                    profile.image.save(
×
105
                        image_name, ContentFile(image_io.getvalue()), save=False
106
                    )
107
                if "transcript" in request.FILES:
1✔
108
                    profile.transcript = request.FILES["transcript"]
1✔
109

110
                profile.user = user
1✔
111
                profile.save()
1✔
112

113
                Availability.objects.filter(user=request.user).delete()
1✔
114
                serialized_availabilities = request.POST.get("availabilities")
1✔
115
                availabilities = json.loads(serialized_availabilities)
1✔
116

117
                for availability_data in availabilities:
1✔
118
                    availability_data["user"] = user
1✔
119
                    Availability.objects.create(**availability_data)
1✔
120

121
                # Save expertise data to database
122
                Expertise.objects.filter(user=request.user).delete()
1✔
123
                selected_expertise = request.POST.getlist("expertise")
1✔
124
                if selected_expertise:
1✔
125
                    for expertise in selected_expertise:
1✔
126
                        Expertise.objects.create(user=user, subject=expertise)
1✔
127
                user.usertype.has_profile_complete = True
1✔
128
                user.usertype.save()
1✔
129
                return HttpResponseRedirect(reverse("Dashboard:dashboard"))
1✔
130
        else:
131
            profile = None
1✔
132
            existing_availabilities = None
1✔
133
            try:
1✔
134
                profile = ProfileT.objects.get(user=request.user)
1✔
135
                existing_availabilities = Availability.objects.filter(user=request.user)
1✔
136
                initial_availabilities_json = json.dumps(
1✔
137
                    list(
138
                        existing_availabilities.values(
139
                            "day_of_week", "start_time", "end_time"
140
                        )
141
                    ),
142
                    cls=DateTimeEncoder,
143
                )
144
                tutor_form = TutorForm(instance=profile)
1✔
145
            except Exception as e:
×
146
                print("Error " + str(e))
×
147
            availability_form = AvailabilityForm()
1✔
148
            tutor_form.initial["expertise"] = existing_expertise
1✔
149
        context = {
1✔
150
            "tutor_form": tutor_form,
151
            "tutor_image_form": tutor_image_form,
152
            "tutor_transcript_form": tutor_transcript_form,  # Add this to the context
153
            "availability_form": availability_form,
154
            "initial_availabilities_json": initial_availabilities_json,
155
        }
156

157
        return render(request, "Dashboard/tutor_info.html", context)
1✔
158

159
    else:
160
        return redirect("Dashboard:student_profile")
×
161

162

163
@login_required
1✔
164
def StudentInformation(request):
1✔
165
    if request.user.usertype.user_type == "student":
1✔
166
        if request.method == "POST":
1✔
167
            profile, created = ProfileS.objects.get_or_create(user=request.user)
1✔
168
            student_form = StudentForm(request.POST, instance=profile)
1✔
169
            student_image_form = StudentImageForm(
1✔
170
                request.POST, request.FILES, instance=profile
171
            )
172

173
            if student_form.is_valid() and student_image_form.is_valid():
1✔
174
                user = request.user
1✔
175
                profile = student_form.save(commit=False)
1✔
176

177
                # Handle the image field separately using student_image_form
178
                if "image" in request.FILES:
1✔
179
                    image = Image.open(request.FILES["image"])
×
180
                    # Resize to 300x300, enlarging if necessary
181
                    image = image.resize((300, 300), Image.Resampling.LANCZOS)
×
182
                    # Fill with white background and crop as a circle
183
                    new_image = Image.new("RGB", (300, 300), (255, 255, 255))
×
184
                    new_image.paste(
×
185
                        image, ((300 - image.width) // 2, (300 - image.height) // 2)
186
                    )
187
                    mask = Image.new("L", (300, 300), 0)
×
188
                    draw = ImageDraw.Draw(mask)
×
189
                    draw.ellipse((0, 0, 300, 300), fill=255)
×
190
                    new_image.putalpha(mask)
×
191
                    # Save the image
192
                    image_io = BytesIO()
×
193
                    new_image.save(image_io, format="PNG")
×
194
                    image_name = request.FILES["image"].name
×
195
                    profile.image.save(
×
196
                        image_name, ContentFile(image_io.getvalue()), save=False
197
                    )
198

199
                profile.user = user
1✔
200
                profile.save()
1✔
201
                user.usertype.has_profile_complete = True
1✔
202
                user.usertype.save()
1✔
203
                return HttpResponseRedirect(reverse("Dashboard:dashboard"))
1✔
204
        else:
205
            profile = None
×
206
            try:
×
207
                profile = ProfileS.objects.get(user=request.user)
×
208
            except Exception as e:
×
209
                print("Error " + str(e))
×
210

211
            student_form = StudentForm(instance=profile)
×
212
            student_image_form = StudentImageForm(instance=profile)
×
213

214
        context = {
×
215
            "student_form": student_form,
216
            "student_image_form": student_image_form,
217
            "profile": profile,
218
        }
219

220
        return render(request, "Dashboard/student_info.html", context)
×
221

222
    else:
223
        return redirect("Dashboard:tutor_profile")
×
224

225

226
@login_required
1✔
227
def UserDashboard(request):
1✔
228
    userType = request.user.usertype.user_type
1✔
229

230
    if userType == "student":
1✔
231
        sessions = TutoringSession.objects.filter(
1✔
232
            student_id=request.user.id, status="Accepted"
233
        ).select_related("tutor_id__profilet")
234
    else:
235
        sessions = TutoringSession.objects.filter(
1✔
236
            tutor_id=request.user.id, status="Accepted"
237
        ).select_related("student_id__profiles")
238

239
    now = datetime.now()
1✔
240

241
    upcomingSessions = sessions.filter(
1✔
242
        Q(date__gt=now.date()) | Q(date=now.date(), end_time__gt=now.time())
243
    )
244

245
    pastSessions = sessions.filter(
1✔
246
        Q(date__lt=now.date()) | Q(date=now.date(), end_time__lt=now.time())
247
    )
248
    has_upcomingSessions = upcomingSessions
1✔
249
    has_pastSessions = pastSessions
1✔
250

251
    page_number1 = request.GET.get("upcoming_page", 1)
1✔
252
    page_number2 = request.GET.get("past_page", 1)
1✔
253

254
    paginator1 = Paginator(upcomingSessions, 3)
1✔
255
    paginator2 = Paginator(pastSessions, 3)
1✔
256

257
    try:
1✔
258
        upcomingSessions = paginator1.page(page_number1)
1✔
259
    except PageNotAnInteger:
×
260
        upcomingSessions = paginator1.page(1)
×
261
    except EmptyPage:
×
262
        upcomingSessions = paginator1.page(paginator1.num_pages)
×
263

264
    try:
1✔
265
        pastSessions = paginator2.page(page_number2)
1✔
266
    except PageNotAnInteger:
×
267
        pastSessions = paginator2.page(1)
×
268
    except EmptyPage:
×
269
        pastSessions = paginator2.page(paginator2.num_pages)
×
270

271
    context = {
1✔
272
        "baseTemplate": (
273
            "Dashboard/base_student.html"
274
            if userType == "student"
275
            else "Dashboard/base_tutor.html"
276
        ),
277
        "userType": userType,
278
        "upcomingSessions": upcomingSessions,
279
        "has_upcomingSessions": has_upcomingSessions,
280
        "pastSessions": pastSessions,
281
        "has_pastSessions": has_pastSessions,
282
        "upcoming_page": page_number1,
283
        "past_page": page_number2,
284
    }
285

286
    return render(request, "Dashboard/dashboard.html", context)
1✔
287

288

289
@login_required
1✔
290
def ProvideFeedback(request, session_id):
1✔
291
    session = TutoringSession.objects.get(pk=session_id)
1✔
292

293
    s_id = session.student_id
1✔
294
    t_id = session.tutor_id
1✔
295

296
    tutor_profile = get_object_or_404(ProfileT, user=t_id)
1✔
297

298
    if request.method == "POST":
1✔
299
        form = TutorReviewForm(request.POST)
1✔
300

301
        if form.is_valid():
1✔
302
            review = form.save(commit=False)
1✔
303
            review.student_id = s_id
1✔
304
            review.tutor_id = t_id
1✔
305
            review.tutoring_session = session
1✔
306
            review.save()
1✔
307

308
            session.reviewed_by_student = True
1✔
309
            session.save()
1✔
310
            return redirect("Dashboard:dashboard")
1✔
311
    else:
312
        form = TutorReviewForm()
×
313
    return render(
×
314
        request, "Dashboard/feedback.html", {"form": form, "profilet": tutor_profile}
315
    )
316

317

318
@login_required
1✔
319
def TutorFeedback(request):
1✔
320
    reviews = (
1✔
321
        TutorReview.objects.all()
322
        .filter(tutor_id=request.user.id)
323
        .select_related("student_id__profiles")
324
    )
325

326
    has_reviews = reviews
1✔
327
    page = request.GET.get("page")
1✔
328
    paginator = Paginator(reviews, 5)
1✔
329
    try:
1✔
330
        reviews = paginator.page(page)
1✔
331
    except PageNotAnInteger:
1✔
332
        # If page is not an integer, deliver first page.
333
        reviews = paginator.page(1)
1✔
334
    except EmptyPage:
×
335
        # If page is out of range, deliver last page of results.
336
        reviews = paginator.page(paginator.num_pages)
×
337

338
    return render(
1✔
339
        request,
340
        "Dashboard/tutor_feedback.html",
341
        {"has_reviews": has_reviews, "reviews": reviews},
342
    )
343

344

345
@login_required
1✔
346
def CancelSession(request, session_id):
1✔
347
    userType = request.user.usertype.user_type
1✔
348

349
    session = TutoringSession.objects.get(pk=session_id)
1✔
350

351
    student = session.student_id
1✔
352
    student_email = student.username
1✔
353
    studentFname = student.first_name
1✔
354
    studentLname = student.last_name
1✔
355

356
    tutor = session.tutor_id
1✔
357
    tutor_email = tutor.username
1✔
358
    tutorFname = tutor.first_name
1✔
359
    tutorLname = tutor.last_name
1✔
360

361
    sessionDate = session.date
1✔
362
    sessionTime = session.start_time
1✔
363
    session.status = "Cancelled"
1✔
364
    session.save()
1✔
365

366
    # send email notification to the students about session cancellation
367
    html_content = render_to_string(
1✔
368
        "Email/cancellation_template.html",
369
        {
370
            "studentFname": studentFname,
371
            "studentLname": studentLname,
372
            "tutorFname": tutorFname,
373
            "tutorLname": tutorLname,
374
            "sessionDate": sessionDate,
375
            "sessionTime": sessionTime,
376
        },
377
    )
378

379
    email = EmailMessage(
1✔
380
        "Tutoring Session Cancelled",
381
        html_content,
382
        "tutornyuengineeringverify@gmail.com",
383
        [student_email if userType == "tutor" else tutor_email],
384
    )
385
    email.content_subtype = "html"
1✔
386
    email.send()
1✔
387

388
    return redirect("Dashboard:dashboard")
1✔
389

390

391
@login_required
1✔
392
def Requests(request):
1✔
393
    userType = request.user.usertype.user_type
1✔
394

395
    if userType == "tutor":
1✔
396
        tutorRequests = TutoringSession.objects.filter(
1✔
397
            tutor_id=request.user.id, status="Pending", date__gte=date.today()
398
        ).select_related("student_id__profiles")
399
    else:
400
        tutorRequests = TutoringSession.objects.filter(
×
401
            student_id=request.user.id,
402
            status__in=["Pending", "Declined"],
403
            date__gte=date.today(),
404
        ).select_related("tutor_id__profilet")
405

406
    has_tutorRequests = tutorRequests
1✔
407
    page = request.GET.get("page")
1✔
408
    paginator = Paginator(tutorRequests, 5)
1✔
409
    try:
1✔
410
        tutorRequests = paginator.page(page)
1✔
411
    except PageNotAnInteger:
1✔
412
        # If page is not an integer, deliver first page.
413
        tutorRequests = paginator.page(1)
1✔
414
    except EmptyPage:
×
415
        # If page is out of range, deliver last page of results.
416
        tutorRequests = paginator.page(paginator.num_pages)
×
417

418
    context = {
1✔
419
        "baseTemplate": (
420
            "Dashboard/base_student.html"
421
            if userType == "student"
422
            else "Dashboard/base_tutor.html"
423
        ),
424
        "userType": userType,
425
        "tutorRequests": tutorRequests,
426
        "has_tutorRequests": has_tutorRequests,
427
    }
428
    return render(request, "Dashboard/requests.html", context)
1✔
429

430

431
def AcceptRequest(request, session_id):
1✔
432
    session = TutoringSession.objects.get(pk=session_id)
1✔
433
    session.status = "Accepted"
1✔
434
    session.save()
1✔
435
    return redirect("Dashboard:requests")
1✔
436

437

438
def DeclineRequest(request, session_id):
1✔
439
    session = TutoringSession.objects.get(pk=session_id)
1✔
440
    session.status = "Declined"
1✔
441
    session.save()
1✔
442
    return redirect("Dashboard:requests")
1✔
443

444

445
@login_required
1✔
446
def DeleteRequest(request, session_id):
1✔
447
    session = TutoringSession.objects.get(pk=session_id)
×
448
    if session.status == "Declined":
×
449
        session.delete()
×
450

451
    return redirect("Dashboard:requests")
×
452

453

454
@login_required
1✔
455
def CancelRequest(request, session_id):
1✔
456
    session = TutoringSession.objects.get(pk=session_id)
×
457
    if session.status == "Pending":
×
458
        session.status = "Cancelled"
×
459
        session.save()
×
460

461
    return redirect("Dashboard:requests")
×
462

463

464
def logout_view(request):
1✔
465
    logout(request)
1✔
466
    return redirect("home")
1✔
467

468

469
class DateTimeEncoder(json.JSONEncoder):
1✔
470
    def default(self, obj):
1✔
471
        if isinstance(obj, (datetime, time)):
×
472
            return obj.strftime("%H:%M")
×
473
        return json.JSONEncoder.default(self, obj)
×
474

475

476
@login_required
1✔
477
def download_attachment(request, session_id):
1✔
UNCOV
478
    session = get_object_or_404(TutoringSession, pk=session_id)
×
479

UNCOV
480
    if session.attachment:
×
481
        # Open the file directly from the storage backend
UNCOV
482
        file = session.attachment.open("rb")
×
483
        # Create a FileResponse with the file's content
UNCOV
484
        response = FileResponse(
×
485
            file, as_attachment=True, filename=session.attachment.name
486
        )
487
        # Set the content type to the file's content type, if available
UNCOV
488
        content_type, _ = mimetypes.guess_type(session.attachment.name)
×
UNCOV
489
        if content_type:
×
UNCOV
490
            response["Content-Type"] = content_type
×
UNCOV
491
        return response
×
492

493
    return redirect("Dashboard:requests")
×
494

495

496
@login_required
1✔
497
def download_transcript(request, tutor_id):
1✔
UNCOV
498
    tutor_profile = get_object_or_404(ProfileT, pk=tutor_id)
×
499

UNCOV
500
    if tutor_profile.transcript:
×
501
        # Open the file directly from the storage backend
UNCOV
502
        file = tutor_profile.transcript.open("rb")
×
503
        # Create a FileResponse with the file's content
UNCOV
504
        response = FileResponse(
×
505
            file, as_attachment=True, filename=tutor_profile.transcript.name
506
        )
507
        # Set the content type to the file's content type, if available
UNCOV
508
        content_type, _ = mimetypes.guess_type(tutor_profile.transcript.name)
×
UNCOV
509
        if content_type:
×
UNCOV
510
            response["Content-Type"] = content_type
×
UNCOV
511
        return response
×
512

513
    return redirect("Dashboard:tutor_profile")
×
514

515

516
@csrf_exempt
1✔
517
def VideoCall(request):
1✔
518
    if request.method == "POST":
1✔
519
        action = request.POST.get("action")
×
520
        if action == "session":
×
521
            sessionId = request.POST.get("sessionID", "")
×
522
            request.session["temp"] = sessionId
×
523
            print("Session ID:", sessionId)
×
524
        elif action == "url":
×
525
            url = request.POST.get("url", "")
×
526
            sessionId = request.session.get("temp")
×
527
            if sessionId:
×
528
                session = TutoringSession.objects.get(pk=sessionId)
×
529
                session.meeting_link = url
×
530
                session.save()
×
531
                print("Received URL:", url)
×
532
            else:
533
                print("SessionId not found")
×
534

535
    if request.user.usertype.user_type == "tutor":
1✔
536
        tutor = ProfileT.objects.get(user=request.user)
1✔
537
        fname = tutor.fname
1✔
538
        lname = tutor.lname
1✔
539
    else:
540
        student = ProfileS.objects.get(user=request.user)
1✔
541
        fname = student.fname
1✔
542
        lname = student.lname
1✔
543
    return render(request, "Dashboard/video_call.html", {"name": fname + " " + lname})
1✔
544

545

546
@login_required
1✔
547
def AdminDashboard(request):
1✔
548
    tutors = ProfileT.objects.order_by("id")
1✔
549
    expertise = Expertise.objects.all()
1✔
550
    return render(
1✔
551
        request,
552
        "Dashboard/admin_dashboard.html",
553
        {"tutors": tutors, "expertise": expertise},
554
    )
555

556

557
def UpdateQualification(request):
1✔
558
    if request.method == "POST":
1✔
559
        tutor_id = request.POST.get("tutor_id")
1✔
560
        qualifiction = request.POST.get(f"qualification_{tutor_id}")
1✔
561
        tutor = ProfileT.objects.get(id=tutor_id)
1✔
562
        tutor.qualified = qualifiction == "qualified"
1✔
563
        tutor.save()
1✔
564

565
        tutor_name = tutor.fname
1✔
566
        tutor_user = tutor.user
1✔
567
        tutor_email = tutor_user.username
1✔
568

569
        if tutor.qualified:
1✔
570
            html_content = render_to_string(
×
571
                "Email/qualification_email.html", {"tutor_name": tutor_name}
572
            )
573
        else:
574
            html_content = render_to_string(
1✔
575
                "Email/unqualify_email.html", {"tutor_name": tutor_name}
576
            )
577

578
        email = EmailMessage(
1✔
579
            "Qualification Updated -- TutorNYU",
580
            html_content,
581
            "tutornyuengineeringverify@gmail.com",
582
            [tutor_email],
583
        )
584
        email.content_subtype = "html"
1✔
585
        email.send()
1✔
586

587
        return HttpResponseRedirect(reverse("Dashboard:admin_dashboard"))
1✔
588

589
    return render(request, "Dashboard/admin_dashboard.html")
×
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