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

gcivil-nyu-org / Wednesday-Fall2023-Team-4 / 876

10 Dec 2023 07:34AM UTC coverage: 85.546% (-0.6%) from 86.181%
876

Pull #239

travis-pro

web-flow
Merge b58f10b69 into 708755513
Pull Request #239: Add img size validator

24 of 41 new or added lines in 2 files covered. (58.54%)

4 existing lines in 1 file now uncovered.

1527 of 1785 relevant lines covered (85.55%)

0.86 hits per line

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

71.72
/rrapp/views.py
1
from django.shortcuts import get_object_or_404, render
1✔
2
from django.db.models import Q, Case, When, Value, IntegerField
1✔
3

4
# Create your views here.
5
from django.contrib import messages
1✔
6

7
from django.contrib.auth import authenticate, login, logout
1✔
8
from django.contrib.auth.decorators import login_required
1✔
9

10
from django.contrib.auth import get_user_model
1✔
11
from django.contrib.auth.views import PasswordResetView, PasswordResetConfirmView
1✔
12

13
from django.contrib.messages.views import SuccessMessageMixin
1✔
14
from django.contrib.sites.shortcuts import get_current_site
1✔
15

16
from django.conf import settings
1✔
17

18
from django.core.paginator import Paginator
1✔
19
from django.core.mail import EmailMessage
1✔
20
from django.core.exceptions import PermissionDenied
1✔
21

22
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect, JsonResponse
1✔
23
from django.urls import reverse
1✔
24
from django.urls import reverse_lazy
1✔
25
from django.utils.decorators import method_decorator
1✔
26
from django.utils.encoding import force_bytes, force_str
1✔
27
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
1✔
28
from django.utils import timezone
1✔
29

30
from django.views import generic
1✔
31
from django.template.loader import render_to_string
1✔
32

33
from psycopg2.extras import NumericRange
1✔
34
from typing import Any
1✔
35
import numpy as np
1✔
36
from sklearn.neighbors import NearestNeighbors
1✔
37

38
from chat.models import DirectMessagePermission, Permission
1✔
39
from chat.utils import get_pending_connections_count
1✔
40

41
from .models import (
1✔
42
    Listing,
43
    Renter,
44
    Rentee,
45
    SavedListing,
46
    Photo,
47
    Rating,
48
    Quiz,
49
)
50
from .forms import MyUserCreationForm, ListingForm, UserForm, LoginForm, QuizForm
1✔
51
from .utils import check_user_listing_match
1✔
52
from .tokens import account_activation_token
1✔
53
from .HEOM import HEOM
1✔
54

55
User = get_user_model()
1✔
56

57

58
def healthcheck(request):
1✔
59
    return HttpResponse(status=200)
×
60

61

62
class HomeView(generic.View):
1✔
63
    def dispatch(self, request, *args, **kwargs):
1✔
64
        # will redirect to the home page if a user tries to
65
        # access the register page while logged in
66
        if request.user.is_authenticated:
1✔
67
            return HttpResponseRedirect(reverse("rrapp:rentee_listings"))
1✔
68
        # else process dispatch as it otherwise normally would
69
        return super(HomeView, self).dispatch(request, *args, **kwargs)
1✔
70

71
    def get(self, request, *args, **kwargs):
1✔
72
        return render(request, "rrapp/home.html")
1✔
73

74

75
class LoginView(generic.View):
1✔
76
    context = {"page": "login"}
1✔
77

78
    def dispatch(self, request, *args, **kwargs):
1✔
79
        # will redirect to the home page if a user tries to
80
        # access the register page while logged in
81
        if request.user.is_authenticated:
1✔
82
            return HttpResponseRedirect(reverse("rrapp:rentee_listings"))
1✔
83
        # else process dispatch as it otherwise normally would
84
        return super(LoginView, self).dispatch(request, *args, **kwargs)
1✔
85

86
    def get(self, request, *args, **kwargs):
1✔
87
        form = LoginForm()
1✔
88
        self.context["form"] = form
1✔
89
        return render(request, "rrapp/login_register.html", self.context)
1✔
90

91
    def post(self, request, *args, **kwargs):
1✔
92
        loginForm = LoginForm(request.POST)
1✔
93
        if loginForm.is_valid():
1✔
94
            email = loginForm.cleaned_data.get("email")
1✔
95
            password = loginForm.cleaned_data.get("password")
1✔
96
            user = authenticate(request, email=email, password=password)
1✔
97
            if user is not None:
1✔
98
                login(request, user)
1✔
99
                return HttpResponseRedirect(reverse("rrapp:rentee_listings"))
1✔
100
            else:
101
                messages.error(request, "Username OR password does not exist")
1✔
102
        else:
103
            self.context["form"] = loginForm
×
104
        return render(request, "rrapp/login_register.html", self.context)
1✔
105

106

107
class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
1✔
108
    template_name = 'rrapp/password_reset.html'
1✔
109
    email_template_name = 'rrapp/password_reset_email.html'
1✔
110
    subject_template_name = 'rrapp/password_reset_subject.txt'
1✔
111
    success_url = reverse_lazy('rrapp:password_reset_done')
1✔
112

113

114
class ConfirmPasswordResetView(PasswordResetConfirmView):
1✔
115
    success_url = reverse_lazy('rrapp:password_reset_complete')
1✔
116

117

118
class LogoutView(generic.View):
1✔
119
    def get(self, request, *args, **kwargs):
1✔
120
        logout(request)
1✔
121
        return render(request, "rrapp/home.html")
1✔
122

123

124
class RegisterView(generic.View):
1✔
125
    initial = {"key": "value"}
1✔
126

127
    def dispatch(self, request, *args, **kwargs):
1✔
128
        # will redirect to the home page if a user tries to
129
        # access the register page while logged in
130
        if request.user.is_authenticated:
1✔
131
            return HttpResponseRedirect(reverse("rrapp:rentee_listings"))
1✔
132
        # else process dispatch as it otherwise normally would
133
        return super(RegisterView, self).dispatch(request, *args, **kwargs)
1✔
134

135
    def get(self, request, *args, **kwargs):
1✔
136
        form = MyUserCreationForm(initial=self.initial)
1✔
137
        return render(request, "rrapp/login_register.html", {"form": form})
1✔
138

139
    def post(self, request, *args, **kwargs):
1✔
140
        form = MyUserCreationForm(request.POST, request.FILES)
1✔
141
        if form.is_valid():
1✔
142
            user = form.save(commit=False)
1✔
143
            user.save()
1✔
144

145
            type_renter = Renter.objects.create(user=user)
1✔
146
            type_rentee = Rentee.objects.create(user=user)
1✔
147
            type_renter.save()
1✔
148
            type_rentee.save()
1✔
149

150
            login(request, user)
1✔
151
            messages.success(request, "Registration successful.")
1✔
152
            return HttpResponseRedirect(reverse("rrapp:rentee_listings"))
1✔
153

154
        return render(request, "rrapp/login_register.html", {"form": form})
1✔
155

156

157
def verificationCheck(request, uidb64, token):
1✔
158
    try:
×
159
        uid = force_str(urlsafe_base64_decode(uidb64))
×
160
        user = User.objects.get(pk=uid)
×
161
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
×
162
        user = None
×
163
    if user is not None and account_activation_token.check_token(user, token):
×
164
        user.is_verified = True
×
165
        user.save()
×
166
        # login(request, user)
167
        # return redirect('home')
168
        messages.success(
×
169
            request,
170
            "Thank you for confirming. Your email is now verified!",
171
            extra_tags='alert alert-success',
172
        )
173
    else:
174
        messages.error(
×
175
            request, "Verification link is invalid!", extra_tags='alert alert-danger'
176
        )
177

178
    # return redirect('rrapp:home')
179
    return HttpResponseRedirect(reverse("rrapp:profile"))
×
180

181

182
@login_required
1✔
183
def verifyEmail(request):
1✔
184
    mail_subject = "Activate your user account."
1✔
185
    message = render_to_string(
1✔
186
        "rrapp/template_verify_account.html",
187
        {
188
            'user': request.user.username,
189
            'domain': get_current_site(request).domain,
190
            'uid': urlsafe_base64_encode(force_bytes(request.user.pk)),
191
            'token': account_activation_token.make_token(request.user),
192
            "protocol": 'https' if request.is_secure() else 'http',
193
        },
194
    )
195
    email = EmailMessage(mail_subject, message, to=[request.user.email])
1✔
196
    if email.send():
1✔
197
        # return HttpResponse(f'Dear {request.user}, please go to your email \
198
        #     {request.user.email} inbox and click on \
199
        #         received activation link to confirm and \
200
        #         complete the registration. Note: Check your spam folder.')
201
        messages.success(
1✔
202
            request,
203
            f'Hi {request.user}, please check your email \
204
            {request.user.email}\'s inbox and click on \
205
                received activation link to confirm and \
206
                complete the verification. Don\'t forget to check your spam folder.',
207
            extra_tags='alert alert-primary',
208
        )
209
        # return redirect('rrapp:home')
210
        return HttpResponseRedirect(reverse("rrapp:profile"))
1✔
211
    else:
212
        messages.error(
×
213
            request,
214
            f'Problem sending email to {request.user.email}, \
215
            please check if you typed it correctly.',
216
            extra_tags='alert alert-danger',
217
        )
218
        # return redirect('rrapp:home')
219
        return HttpResponseRedirect(reverse("rrapp:profile"))
×
220

221

222
@method_decorator(login_required, name="dispatch")
1✔
223
class ListingIndexView(generic.ListView):
1✔
224
    template_name = "rrapp/my_listings.html"
1✔
225
    context_object_name = "latest_listings"
1✔
226

227
    def get_queryset(self):
1✔
228
        """Return the last five published questions."""
229
        user_id = self.request.user.id
1✔
230
        all_listings = Listing.objects.filter(user=user_id).order_by("-created_at")
1✔
231
        paginator = Paginator(all_listings, 10)
1✔
232
        page_number = self.request.GET.get("page")
1✔
233
        latest_listings_page = paginator.get_page(page_number)
1✔
234
        return latest_listings_page
1✔
235

236
    def get_context_data(self, **kwargs: Any):
1✔
237
        context_data = super().get_context_data(**kwargs)
1✔
238
        user_id = self.request.user.id
1✔
239
        context_data["user_id"] = user_id
1✔
240
        context_data["user"] = self.request.user
1✔
241
        context_data["path"] = self.request.path_info.__contains__("renter")
1✔
242
        context_data["inbox"] = get_inbox_count(User.objects.get(id=user_id).username)
1✔
243
        return context_data
1✔
244

245

246
@method_decorator(login_required, name="dispatch")
1✔
247
class ShortListView(generic.ListView):
1✔
248
    template_name = "rrapp/shortListing.html"
1✔
249
    context_object_name = "latest_listings"
1✔
250

251
    def get_queryset(self):
1✔
252
        """Return the last five published questions."""
253
        user_id = self.request.user.id
×
254
        # shortlistings = Listing.objects.filter(user=user_id).order_by("-created_at")
255
        shortlistings = SavedListing.objects.filter(rentee_id__user=user_id).order_by(
×
256
            "-saved_listings__created_at"
257
        )
258
        paginator = Paginator(shortlistings, 10)
×
259
        page_number = self.request.GET.get("page")
×
260
        latest_listings_page = paginator.get_page(page_number)
×
261
        return latest_listings_page
×
262

263
    def get_context_data(self, **kwargs: Any):
1✔
264
        context_data = super().get_context_data(**kwargs)
×
265
        user_id = self.request.user.id
×
266
        context_data["user_id"] = user_id
×
267
        context_data["user"] = self.request.user
×
268
        context_data["path"] = self.request.path_info.__contains__("renter")
×
269
        context_data["inbox"] = get_inbox_count(User.objects.get(id=user_id).username)
×
270
        return context_data
×
271

272

273
@method_decorator(login_required, name="dispatch")
1✔
274
class ListingDetailView(generic.DetailView):
1✔
275
    model = Listing
1✔
276
    template_name = "rrapp/listing_detail.html"
1✔
277

278
    def get_object(self, queryset=None):
1✔
279
        return Listing.objects.get(id=self.kwargs["pk"])
1✔
280

281
    def get(self, request, *args, **kwargs):
1✔
282
        self.object = self.get_object()
1✔
283
        if self.object.user != request.user:
1✔
284
            raise PermissionDenied('User not authorized to access this listing')
1✔
285
        return super().get(request, *args, **kwargs)
1✔
286

287
    def get_context_data(self, **kwargs: Any):
1✔
288
        context_data = super().get_context_data(**kwargs)
1✔
289
        user_id = self.request.user.id
1✔
290
        context_data["user_id"] = user_id
1✔
291
        context_data["user"] = self.request.user
1✔
292
        context_data["path"] = self.request.path_info.__contains__("renter")
1✔
293
        context_data["photos"] = Photo.objects.filter(listing=self.kwargs["pk"])
1✔
294
        context_data["inbox"] = get_inbox_count(User.objects.get(id=user_id).username)
1✔
295
        return context_data
1✔
296

297

298
@method_decorator(login_required, name="dispatch")
1✔
299
class ListingDetailRenteeView(generic.DetailView):
1✔
300
    model = Listing
1✔
301
    template_name = "rrapp/rentee_listing_detail.html"
1✔
302

303
    def get_context_data(self, **kwargs: Any):
1✔
304
        context_data = super().get_context_data(**kwargs)
1✔
305
        user_id = self.request.user.id
1✔
306
        context_data["user_id"] = user_id
1✔
307
        context_data["user"] = User.objects.get(id=user_id)
1✔
308
        context_data["path"] = self.request.path_info.__contains__("renter")
1✔
309
        context_data["saved"] = self.check_state(user_id, self.kwargs["pk"])
1✔
310
        context_data["cur_permission"] = self.cur_permission(user_id, self.kwargs["pk"])
1✔
311
        context_data["photos"] = Photo.objects.filter(listing=self.kwargs["pk"])
1✔
312
        context_data["inbox"] = get_inbox_count(User.objects.get(id=user_id).username)
1✔
313
        context_data["quizState"] = check_quiz_state(user_id)
1✔
314
        return context_data
1✔
315

316
    def check_state(self, user_id, listing_id):
1✔
317
        # print(user_id, listing_id)
318
        if SavedListing.objects.filter(
1✔
319
            rentee_id__user=user_id, saved_listings=listing_id
320
        ).exists():
321
            return True
×
322
        else:
323
            return False
1✔
324

325
    def cur_permission(self, user_id, listing_id):
1✔
326
        # print(user_id, listing_id)
327
        listing = Listing.objects.get(id=listing_id)
1✔
328
        cur_user = User.objects.get(id=user_id)
1✔
329
        try:
1✔
330
            p = list(
1✔
331
                DirectMessagePermission.objects.filter(
332
                    sender=cur_user, receiver=listing.user
333
                )
334
            )
335

336
            p_equivalent = list(
1✔
337
                DirectMessagePermission.objects.filter(
338
                    receiver=cur_user, sender=listing.user
339
                )
340
            )
341
        except DirectMessagePermission.DoesNotExist:
×
342
            p = None
×
343

344
        if len(p) > 0:
1✔
345
            return p[0].permission
×
346
        else:
347
            if len(p_equivalent) > 0:
1✔
348
                return p_equivalent[0].permission
×
349
            return None
1✔
350

351
    def get_object(self, queryset=None):
1✔
352
        return Listing.objects.get(id=self.kwargs["pk"])
1✔
353

354
    def get(self, request, *args, **kwargs):
1✔
355
        self.object = self.get_object()
1✔
356
        if self.object.restrict_to_matches and not check_user_listing_match(
1✔
357
            request.user, self.object
358
        ):
359
            raise PermissionDenied('User must match the listing preferences to view it')
1✔
360
        return super().get(request, *args, **kwargs)
1✔
361

362
    def post(self, request, *args, **kwargs):
1✔
363
        listing_id = self.kwargs["pk"]
1✔
364
        user_id = request.user.id
1✔
365
        save_state = self.check_state(user_id, listing_id)
1✔
366
        if "shortlist" in request.POST:
1✔
367
            if save_state:
1✔
368
                SavedListing.objects.filter(
×
369
                    rentee_id__user=user_id, saved_listings=listing_id
370
                ).delete()
NEW
371
                messages.success(request, "Removed from shortlist.")
×
372
            else:
373
                rentee = Rentee.objects.get(user=user_id)
1✔
374
                listing = Listing.objects.get(id=listing_id)
1✔
375
                SavedListing.objects.create(rentee_id=rentee, saved_listings=listing)
1✔
376
                messages.success(request, "Added to shortlist.")
1✔
377

378
        if "connection_request" in request.POST:
1✔
379
            listing = Listing.objects.get(id=listing_id)
1✔
380
            cur_user = User.objects.get(id=user_id)
1✔
381
            try:
1✔
382
                p = list(
1✔
383
                    DirectMessagePermission.objects.filter(
384
                        sender=cur_user, receiver=listing.user
385
                    )
386
                )
387
            except DirectMessagePermission.DoesNotExist:
×
388
                p = None
×
389

390
            if len(p) > 0:
1✔
391
                print("permission already exists", p)
1✔
392
                messages.error(
1✔
393
                    request,
394
                    "Cannot send a new request until renter responds to the existing request.",
395
                )
396
            else:
397
                # create DirectMessagePermission object in db
398
                DirectMessagePermission.objects.create(
1✔
399
                    sender=cur_user,
400
                    receiver=listing.user,
401
                    permission=Permission.REQUESTED,
402
                )
403
                messages.success(request, "Request sent to renter.")
1✔
404
        return HttpResponseRedirect(request.path_info)  # redirect to the same page
1✔
405

406

407
@method_decorator(login_required, name="dispatch")
1✔
408
class ListingResultsView(generic.ListView):
1✔
409
    template_name = "rrapp/rentee_listings.html"
1✔
410
    context_object_name = "queried_listings_page"
1✔
411

412
    def get_queryset(self):
1✔
413
        all_listings = Listing.objects.all().order_by("-created_at")
1✔
414
        sort_option = self.request.GET.get("sort", "created_at")
1✔
415

416
        # Extract the sorting order (Low to High or High to Low)
417
        sorting_order = ""  # Default to ascending order
1✔
418
        if sort_option.startswith("-"):
1✔
419
            sort_option = sort_option[1:]
1✔
420
            sorting_order = "-"  # Set to descending order
1✔
421

422
        # Apply sorting
423
        if sort_option == "recommendation":
1✔
424
            # all raters in our dataset
425
            all_ratings = Rating.objects.all()
1✔
426
            if len(all_ratings) == 0:
1✔
427
                pass
1✔
428
            else:
429
                full_rater_list = []
1✔
430
                for rating in all_ratings:
1✔
431
                    full_rater_list.append(rating.rater)
1✔
432
                full_rater_list = list(set(full_rater_list))
1✔
433

434
                # all users that have listings
435
                listing_user_list = []
1✔
436
                for listing in all_listings:
1✔
437
                    listing_user_list.append(listing.user)
1✔
438
                listing_user_list = list(set(listing_user_list))
1✔
439

440
                # get the listing users and their corresponding recommendation level
441
                smokes_dic = {True: 1, False: 0}
1✔
442
                pets_dic = {'cats': 0, 'dogs': 1, 'none': 2, 'all': 3}
1✔
443
                food_group_dic = {
1✔
444
                    'vegan': 0,
445
                    'vegetarian': 1,
446
                    'non_vegetarian': 2,
447
                    'all': 3,
448
                }
449
                data = np.array(
1✔
450
                    [
451
                        [
452
                            (timezone.now().date() - user.birth_date).days,
453
                            smokes_dic[user.smokes],
454
                            pets_dic[user.pets],
455
                            food_group_dic[user.food_group],
456
                        ]
457
                        for user in full_rater_list
458
                    ]
459
                )
460
                full_rater_id = [user.id for user in full_rater_list]
1✔
461
                categorical_ix = [1, 2, 3]  # categorical feature indices
1✔
462
                nan_eqv = 12345  # this can change to whatever nan in our dataset is
1✔
463

464
                heom_metric = HEOM(data, categorical_ix, nan_equivalents=[nan_eqv])
1✔
465
                neighbor = NearestNeighbors(metric=heom_metric.heom)
1✔
466
                neighbor.fit(data)
1✔
467
                curr_user_data = np.array(
1✔
468
                    [
469
                        (timezone.now().date() - self.request.user.birth_date).days,
470
                        smokes_dic[self.request.user.smokes],
471
                        pets_dic[self.request.user.pets],
472
                        food_group_dic[self.request.user.food_group],
473
                    ]
474
                ).reshape(1, -1)
475
                sim_index, index = neighbor.kneighbors(
1✔
476
                    curr_user_data, n_neighbors=len(data)
477
                )
478
                sim_index, index = sim_index[0], index[0]
1✔
479
                recom_tuple_list = []
1✔
480
                for ratee in listing_user_list:
1✔
481
                    rater_list = Rating.objects.filter(ratee=ratee)
1✔
482
                    if len(rater_list) == 0:
1✔
483
                        recommendation_level = 0.0
×
484
                    else:
485
                        recommendation_level = 0
1✔
486
                        for rating in rater_list:
1✔
487
                            recommendation_level += (
1✔
488
                                (rating.rating - 3.0)
489
                                / 2.0
490
                                / (
491
                                    sim_index[
492
                                        index[full_rater_id.index(rating.rater.id)]
493
                                    ]
494
                                    + 1
495
                                )
496
                            )
497
                        recommendation_level /= np.sqrt(len(rater_list))
1✔
498
                    recom_tuple_list.append((ratee.id, recommendation_level))
1✔
499

500
                # sort the users according to recommendation level
501
                sorted_recom_tuple_list = sorted(
1✔
502
                    recom_tuple_list, key=lambda x: x[1], reverse=True
503
                )
504
                sorted_listing_user_id = [x[0] for x in sorted_recom_tuple_list]
1✔
505

506
                # get the sorted listing ids
507
                order_indices = []
1✔
508
                for user_id in sorted_listing_user_id:
1✔
509
                    user = User.objects.filter(id=user_id)[0]
1✔
510
                    listing_list = Listing.objects.filter(user=user)
1✔
511
                    for listing in listing_list:
1✔
512
                        order_indices.append(listing.id)
1✔
513

514
                # Create a Case expression to order the queryset based on the indices
515
                ordering_cases = [
1✔
516
                    When(id=pk, then=Value(index))
517
                    for index, pk in enumerate(order_indices, start=1)
518
                ]
519
                ordering = Case(
1✔
520
                    *ordering_cases, default=Value(0), output_field=IntegerField()
521
                )
522

523
                # Use the queryset with the order_by method
524
                all_listings = all_listings.order_by(ordering)
1✔
525

526
        else:
527
            if sort_option not in [
1✔
528
                "monthly_rent",
529
                "number_of_bedrooms",
530
                "number_of_bathrooms",
531
                "recommendation",
532
            ]:
533
                sort_option = "created_at"
1✔
534
            all_listings = all_listings.order_by(f"{sorting_order}{sort_option}")
1✔
535

536
        # Apply filters
537
        filters = Q()
1✔
538

539
        try:
1✔
540
            monthly_rent_min = int(self.request.GET.get("monthly_rent_min", "0"))
1✔
541
            monthly_rent_max = int(self.request.GET.get("monthly_rent_max", "10000"))
1✔
542
            filters &= Q(
1✔
543
                monthly_rent__gte=monthly_rent_min, monthly_rent__lte=monthly_rent_max
544
            )
545
        except ValueError:
×
546
            # Handle error or ignore
547
            pass
×
548

549
        # Convert and apply Number of Bedrooms filter
550
        try:
1✔
551
            number_of_bedrooms_min = int(
1✔
552
                self.request.GET.get("number_of_bedrooms_min", "0")
553
            )
554
            number_of_bedrooms_max = int(
1✔
555
                self.request.GET.get("number_of_bedrooms_max", "10")
556
            )
557
            filters &= Q(
1✔
558
                number_of_bedrooms__gte=number_of_bedrooms_min,
559
                number_of_bedrooms__lte=number_of_bedrooms_max,
560
            )
561
        except ValueError:
×
562
            # Handle error or ignore
563
            pass
×
564

565
        # Convert and apply Number of Bathrooms filter
566
        try:
1✔
567
            number_of_bathrooms_min = int(
1✔
568
                self.request.GET.get("number_of_bathrooms_min", "0")
569
            )
570
            number_of_bathrooms_max = int(
1✔
571
                self.request.GET.get("number_of_bathrooms_max", "10")
572
            )
573
            filters &= Q(
1✔
574
                number_of_bathrooms__gte=number_of_bathrooms_min,
575
                number_of_bathrooms__lte=number_of_bathrooms_max,
576
            )
577
        except ValueError:
×
578
            # Handle error or ignore
579
            pass
×
580

581
        washer = self.request.GET.get("washer")
1✔
582
        if washer == "on":
1✔
583
            filters &= Q(washer=True)
1✔
584

585
        dryer = self.request.GET.get("dryer")
1✔
586
        if dryer == "on":
1✔
587
            filters &= Q(dryer=True)
1✔
588

589
        utilities_included = self.request.GET.get("utilities_included")
1✔
590
        if utilities_included == "on":
1✔
591
            filters &= Q(utilities_included=True)
1✔
592

593
        furnished = self.request.GET.get("furnished")
1✔
594
        if furnished == "on":
1✔
595
            filters &= Q(furnished=True)
1✔
596

597
        dishwasher = self.request.GET.get("dishwasher")
1✔
598
        if dishwasher == "on":
1✔
599
            filters &= Q(dishwasher=True)
1✔
600

601
        parking = self.request.GET.get("parking")
1✔
602
        if parking == "on":
1✔
603
            filters &= Q(parking=True)
1✔
604

605
        room_type = self.request.GET.get("room_type")
1✔
606
        if room_type:
1✔
607
            filters &= Q(room_type=room_type)
1✔
608

609
        food_groups_allowed = self.request.GET.get("food_groups_allowed")
1✔
610
        if food_groups_allowed:
1✔
611
            filters &= Q(food_groups_allowed=food_groups_allowed)
1✔
612

613
        pets_allowed = self.request.GET.get("pets_allowed")
1✔
614
        if pets_allowed:
1✔
615
            filters &= Q(pets_allowed=pets_allowed)
1✔
616

617
        preferred_gender = self.request.GET.get("preferred_gender")
1✔
618
        if preferred_gender:
1✔
619
            filters &= Q(preferred_gender=preferred_gender)
×
620

621
        # Continue filtering for other fields if needed
622

623
        # Combine filters
624
        all_listings = all_listings.filter(filters)
1✔
625

626
        final_listings = []
1✔
627
        for listing in all_listings:
1✔
628
            if not listing.restrict_to_matches:
1✔
629
                # no restrictions
630
                final_listings.append(listing)
1✔
631
            else:
632
                # check is user matches listing preference
633
                if check_user_listing_match(self.request.user, listing):
1✔
634
                    final_listings.append(listing)
×
635

636
        paginator = Paginator(final_listings, 10)
1✔
637
        page_number = self.request.GET.get("page")
1✔
638
        queried_listings_page = paginator.get_page(page_number)
1✔
639
        return queried_listings_page
1✔
640

641
    def get_context_data(self, **kwargs: Any):
1✔
642
        context_data = super().get_context_data(**kwargs)
1✔
643
        user_id = self.request.user.id
1✔
644
        context_data["user_id"] = user_id
1✔
645
        context_data["user"] = self.request.user
1✔
646
        context_data["path"] = self.request.path_info.__contains__("renter")
1✔
647
        context_data["inbox"] = get_inbox_count(self.request.user.username)
1✔
648
        return context_data
1✔
649

650

651
@method_decorator(login_required, name="dispatch")
1✔
652
class ListingUpdateView(generic.UpdateView):
1✔
653
    model = Listing
1✔
654
    template_name = "rrapp/listing_detail_modify.html"
1✔
655
    form_class = ListingForm
1✔
656
    success_url = "rrapp:listing_detail"
1✔
657

658
    def get_success_url(self):
1✔
659
        listing_id = self.kwargs["pk"]
×
660
        return reverse("rrapp:listing_detail", args=(listing_id,))
×
661

662
    def get(self, request, *args, **kwargs):
1✔
663
        self.object = self.get_object()
×
664
        return super().get(request, *args, **kwargs)
×
665

666
    def get_object(self, queryset=None):
1✔
667
        return Listing.objects.get(id=self.kwargs["pk"])
×
668

669
    def get_context_data(self, **kwargs: Any):
1✔
670
        context_data = super().get_context_data(**kwargs)
×
671
        context_data["user_id"] = self.request.user.id
×
672
        context_data["listing_id"] = self.kwargs["pk"]
×
673
        context_data["list_title"] = Listing.objects.get(id=self.kwargs["pk"]).title
×
674
        context_data["user"] = self.request.user
×
675
        context_data["path"] = self.request.path_info.__contains__("renter")
×
676
        context_data["inbox"] = get_inbox_count(self.request.user.username)
×
677
        context_data['google_api_key'] = settings.GOOGLE_API_KEY
×
678
        context_data['base_country'] = settings.BASE_COUNTRY
×
679
        return context_data
×
680

681
    def post(self, request, *args, **kwargs):
1✔
682
        self.object = self.get_object()
×
683
        form = self.get_form()
×
684
        if form.is_valid():
×
685
            listing = form.save()
×
686
            existing_photos_pks = request.POST.getlist('existing_photos')
×
687
            Photo.objects.filter(listing=listing).exclude(
×
688
                pk__in=existing_photos_pks
689
            ).delete()
690
            listing.save()
×
691
            for file in request.FILES.getlist('add_photos'):
×
692
                Photo.objects.create(image=file, listing=listing)
×
NEW
693
            messages.success(request, "Changes saved successfully.")
×
UNCOV
694
            return self.form_valid(form)
×
695
        else:
696
            return self.form_invalid(form)
×
697

698

699
@method_decorator(login_required, name="dispatch")
1✔
700
class ListingNewView(generic.CreateView):
1✔
701
    model = Listing
1✔
702
    template_name = "rrapp/listing_new.html"
1✔
703
    form_class = ListingForm
1✔
704
    success_url = "rrapp:my_listings"
1✔
705

706
    def get(self, request, *args, **kwargs):
1✔
707
        self.object = self.get_object()
×
708
        return super().get(request, *args, **kwargs)
×
709

710
    def get_object(self, queryset=None):
1✔
711
        return self.request.user
×
712

713
    def get_success_url(self):
1✔
714
        return reverse("rrapp:listing_new")
×
715

716
    def get_context_data(self, **kwargs: Any):
1✔
717
        context_data = super().get_context_data(**kwargs)
×
718
        context_data["user_id"] = self.request.user.id
×
719
        context_data["user"] = self.request.user
×
720
        context_data["path"] = self.request.path_info.__contains__("renter")
×
721
        context_data["inbox"] = get_inbox_count(self.request.user.username)
×
722
        context_data['google_api_key'] = settings.GOOGLE_API_KEY
×
723
        context_data['base_country'] = settings.BASE_COUNTRY
×
724

725
        return context_data
×
726

727
    def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse:
1✔
728
        """handle user login post req
729

730
        Args:
731
            request (HttpRequest): http request object
732

733
        Returns:
734
            HttpResponse: redirect or login view with error hints
735
        """
736
        self.object = self.get_object()
×
737
        form = self.get_form()
×
738

739
        if form.is_valid():
×
740
            form_data = form.cleaned_data
×
741
            listing = Listing.objects.create(
×
742
                user=request.user,
743
                status=form_data.get("status"),
744
                title=form_data.get("title"),
745
                description=form_data.get("description"),
746
                monthly_rent=form_data.get("monthly_rent"),
747
                date_available_from=form_data.get("date_available_from"),
748
                date_available_to=form_data.get("date_available_to"),
749
                property_type=form_data.get("property_type"),
750
                room_type=form_data.get("room_type"),
751
                address1=form_data.get("address1"),
752
                address2=form_data.get("address2"),
753
                zip_code=form_data.get("zip_code"),
754
                city=form_data.get("city"),
755
                country=form_data.get("country"),
756
                washer=form_data.get("washer"),
757
                dryer=form_data.get("dryer"),
758
                dishwasher=form_data.get("dishwasher"),
759
                microwave=form_data.get("microwave"),
760
                baking_oven=form_data.get("baking_oven"),
761
                parking=form_data.get("parking"),
762
                number_of_bedrooms=form_data.get("number_of_bedrooms"),
763
                number_of_bathrooms=form_data.get("number_of_bathrooms"),
764
                furnished=form_data.get("furnished"),
765
                utilities_included=form_data.get("utilities_included"),
766
                lease_type=form_data.get("lease_type"),
767
                smoking_allowed=form_data.get("smoking_allowed"),
768
                pets_allowed=form_data.get("pets_allowed"),
769
                preferred_gender=form_data.get("preferred_gender"),
770
                food_groups_allowed=form_data.get("food_groups_allowed"),
771
                age_range=NumericRange(
772
                    int(form_data.get("age_range").lower),
773
                    int(form_data.get("age_range").upper),
774
                ),
775
                restrict_to_matches=form_data.get("restrict_to_matches"),
776
            )
777
            listing.save()
×
778
            for file in request.FILES.getlist('add_photos'):
×
779
                Photo.objects.create(image=file, listing=listing)
×
NEW
780
            messages.success(request, "Listing created successfully.")
×
UNCOV
781
            return HttpResponseRedirect(reverse("rrapp:my_listings"))
×
782
        else:
783
            return self.form_invalid(form)
×
784

785

786
@method_decorator(login_required, name='dispatch')
1✔
787
class ProfileView(generic.UpdateView):
1✔
788
    model = User
1✔
789
    template_name = "rrapp/profile.html"
1✔
790
    form_class = UserForm
1✔
791
    success_url = 'rrapp:profile'
1✔
792

793
    def get(self, request, *args, **kwargs):
1✔
794
        self.object = self.get_object()
×
795
        return super().get(request, *args, **kwargs)
×
796

797
    def get_object(self, queryset=None):
1✔
798
        return self.request.user
×
799

800
    def get_context_data(self, **kwargs: Any):
1✔
801
        context_data = super().get_context_data(**kwargs)
×
802
        context_data["user_id"] = self.request.user.id
×
803
        context_data["user"] = self.request.user
×
804
        context_data["path"] = self.request.path_info.__contains__("renter")
×
805
        context_data["inbox"] = get_inbox_count(self.request.user.username)
×
806
        return context_data
×
807

808
    def get_success_url(self):
1✔
809
        return reverse('rrapp:profile')
×
810

811
    def post(self, request, *args, **kwargs):
1✔
812
        self.object = self.get_object()
×
813
        form = self.get_form()
×
814
        if form.is_valid():
×
NEW
815
            messages.success(request, 'Changes saved successfully.')
×
UNCOV
816
            return self.form_valid(form)
×
817
        else:
818
            return self.form_invalid(form)
×
819

820

821
@method_decorator(login_required, name='dispatch')
1✔
822
class PublicProfileView(generic.DetailView):
1✔
823
    model = User
1✔
824
    template_name = "rrapp/public_profile.html"
1✔
825
    fields = [
1✔
826
        'username',
827
        'first_name',
828
        'last_name',
829
        'gender',
830
        'bio',
831
        'smokes',
832
        'pets',
833
        'food_group',
834
        'rating',
835
        'is_verified',
836
        'verified_student',
837
    ]
838

839
    def get_context_data(self, **kwargs: Any):
1✔
840
        context_data = super().get_context_data(**kwargs)
×
841
        context_data["user_id"] = self.request.user.id
×
842
        context_data["user"] = self.request.user
×
843
        context_data["tarUser"] = User.objects.get(id=self.kwargs["pk"])
×
844
        context_data["path"] = self.request.path_info.__contains__("renter")
×
845
        context_data["inbox"] = get_inbox_count(self.request.user.username)
×
846
        if self.check_rating_exists(self.request.user, context_data["tarUser"]):
×
847
            context_data["rated"] = True
×
848
            context_data["rating"] = Rating.objects.get(
×
849
                rater=self.request.user,
850
                ratee=context_data["tarUser"],
851
            ).rating
852
        else:
853
            context_data["rated"] = False
×
854
        return context_data
×
855

856
    def check_rating_exists(self, rater, ratee):
1✔
857
        if Rating.objects.filter(
×
858
            rater=rater,
859
            ratee=ratee,
860
        ).exists():
861
            return True
×
862
        else:
863
            return False
×
864

865
    def get_success_url(self):
1✔
866
        return reverse('rrapp:rentee_listings')
×
867

868

869
@method_decorator(login_required, name='dispatch')
1✔
870
class RatingView(generic.UpdateView):
1✔
871
    model = Rating
1✔
872
    template_name = "rrapp/rate_user.html"
1✔
873
    fields = []
1✔
874

875
    def get(self, request, *args, **kwargs):
1✔
876
        self.object = self.get_object()
1✔
877
        return super().get(request, *args, **kwargs)
1✔
878

879
    def get_object(self, queryset=None):
1✔
880
        return self.request.user
1✔
881

882
    def get_context_data(self, **kwargs: Any):
1✔
883
        context_data = super().get_context_data(**kwargs)
1✔
884
        context_data["pk"] = self.kwargs["ratee_id"]
1✔
885
        return context_data
1✔
886

887
    def post(self, request, *args, **kwargs):
1✔
888
        if not DirectMessagePermission.objects.filter(
1✔
889
            sender=request.user,
890
            receiver=User.objects.get(id=self.kwargs["ratee_id"]),
891
            permission=Permission.ALLOWED,
892
        ).exists():
893
            raise PermissionDenied('You should get permission from the user.')
1✔
894
        else:
895
            val = request.POST.get('val')
1✔
896
            ratee = User.objects.get(id=self.kwargs["ratee_id"])
1✔
897
            if Rating.objects.filter(
1✔
898
                rater=request.user,
899
                ratee=ratee,
900
            ).exists():
901
                rating = Rating.objects.get(
1✔
902
                    rater=request.user,
903
                    ratee=ratee,
904
                )
905
                rating.rating = val
1✔
906
                rating.save()
1✔
907
            else:
908
                Rating.objects.create(
1✔
909
                    rater=request.user,
910
                    ratee=ratee,
911
                    rating=val,
912
                )
913
            rating_list = Rating.objects.filter(
1✔
914
                ratee=ratee,
915
            )
916
            mean_rating = 0.0
1✔
917
            for item in rating_list:
1✔
918
                mean_rating += item.rating
1✔
919
            mean_rating /= len(rating_list)
1✔
920
            ratee.rating = mean_rating
1✔
921
            ratee.save()
1✔
922
            return JsonResponse({'success': 'true', 'score': val}, safe=False)
1✔
923
        return HttpResponseRedirect(
×
924
            reverse("rrapp:rate_user", args=(self.kwargs["ratee_id"],))
925
        )
926

927

928
@method_decorator(login_required, name='dispatch')
1✔
929
class PersonalQuizView(generic.UpdateView):
1✔
930
    model = Quiz
1✔
931
    template_name = "rrapp/quiz.html"
1✔
932
    form_class = QuizForm
1✔
933

934
    def get_object(self, queryset=None):
1✔
935
        quiz, created = Quiz.objects.get_or_create(user=self.request.user)
×
936
        if created:
×
937
            self.request.user.quiz = quiz
×
938
            self.request.user.save()
×
939
        return quiz
×
940

941
    def get(self, request, *args, **kwargs):
1✔
942
        self.object = self.get_object()
×
943
        return super().get(request, *args, **kwargs)
×
944

945
    def get_context_data(self, **kwargs: Any):
1✔
946
        context_data = super().get_context_data(**kwargs)
×
947
        context_data["user_id"] = self.request.user.id
×
948
        context_data["user"] = self.request.user
×
949
        context_data["path"] = self.request.path_info.__contains__("renter")
×
950
        context_data["inbox"] = get_inbox_count(self.request.user.username)
×
951
        return context_data
×
952

953
    def get_success_url(self):
1✔
954
        return reverse("rrapp:rentee_listings")
×
955

956
    def post(self, request, *args, **kwargs):
1✔
957
        self.object = self.get_object()
×
958
        form = self.get_form()
×
959

960
        if form.is_valid():
×
NEW
961
            messages.success(request, "Successfully saved your responses.")
×
UNCOV
962
            return self.form_valid(form)
×
963
        else:
964
            return self.form_invalid(form)
×
965

966

967
@login_required
1✔
968
def listing_delete(request, pk):
1✔
969
    listing = get_object_or_404(Listing, pk=pk, user_id=request.user.id)
×
970

971
    if request.method == 'POST':
×
972
        listing.delete()
×
973
        # Always return an HttpResponseRedirect after successfully dealing
974
        # with POST data. This prevents data from being posted twice if a
975
        # user hits the Back button.
NEW
976
        messages.success(request, "Listing deleted successfully.")
×
977
        return HttpResponseRedirect(reverse('rrapp:my_listings'))
×
978
    return render(
×
979
        request,
980
        'rrapp/confirm_delete_listing.html',
981
        {"user_id": request.user.id, "pk": pk},
982
    )
983

984

985
@login_required
1✔
986
def deleteAccount(request):
1✔
987
    user = get_object_or_404(User, pk=request.user.id)
×
988
    if request.method == 'POST':
×
989
        user.delete()
×
990
        logout(request)
×
991
        return HttpResponseRedirect(reverse('rrapp:home'))
×
992
    return render(
×
993
        request, 'rrapp/confirm_delete_user.html', {"user_id": request.user.id}
994
    )
995

996

997
def get_inbox_count(username):
1✔
998
    i = 0
1✔
999
    i += get_pending_connections_count(username)
1✔
1000
    return i
1✔
1001

1002

1003
def rrapp_403(request, exception):
1✔
1004
    return render(request, "rrapp/403.html", {}, status=403)
1✔
1005

1006

1007
def check_quiz_state(user_id):
1✔
1008
    if Quiz.objects.filter(user=user_id).exists():
1✔
1009
        cur_quiz = Quiz.objects.get(user=user_id)
×
1010
        for i in range(1, 9):
×
1011
            cur_field = "question" + str(i)
×
1012
            if getattr(cur_quiz, cur_field, None) is None:
×
1013
                return False
×
1014
        return True
×
1015
    else:
1016
        return False
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