views.py 25.3 KB
Newer Older
1 2 3 4
# standard library imports
from datetime import date
from cStringIO import StringIO
# core django imports
Daniel W Bond's avatar
Daniel W Bond committed
5 6
from django.views.generic import View, DetailView, ListView, CreateView,\
    UpdateView, DeleteView
7
from django.http import Http404, HttpResponseForbidden
8
from django.forms.widgets import HiddenInput
9
from django.core.urlresolvers import reverse
10
from django.core.files.uploadedfile import SimpleUploadedFile
11 12 13
from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template
from django.template import Context
14
from django.utils.safestring import mark_safe
15
# third party imports
16
import requests
17
from PIL import Image
18
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
19
from braces.views import FormValidMessageMixin
Daniel W Bond's avatar
Daniel W Bond committed
20
from ratelimit.decorators import ratelimit
21 22
# imports from your apps
from .models import Listing, Bid, Flag, Rating
23 24
from .forms import ListingForm, BidForm, FlagForm, ExchangeListingForm,\
    UnExchangeListingForm, RatingForm
25
from core.models import Student
26

Daniel W Bond's avatar
Daniel W Bond committed
27

28 29
# pulls worldcat metadata from ISBNs
def ISBNMetadata(standardISBN):
30
    # passing in numbers starting with 0 throws "SyntaxError: invalid token"
Daniel W Bond's avatar
Daniel W Bond committed
31 32 33
    url = "http://xisbn.worldcat.org/webservices/xid/isbn/" +\
          str(standardISBN) +\
          "?method=getMetadata&format=json&fl=title,year,author,ed"
Jean Michel Rouly's avatar
Jean Michel Rouly committed
34 35 36

    # In case the API fails to return, simply return None.
    try:
37
        metadata = requests.get(url, timeout=3)
Jean Michel Rouly's avatar
Jean Michel Rouly committed
38 39 40
    except ConnectionError:
        return None

41 42 43 44 45 46 47 48
    # format into a dictionary
    dejson = metadata.json()
    try:
        metadataDict = dejson.get('list')
        return metadataDict[0]
    except:
        return None

Daniel W Bond's avatar
Daniel W Bond committed
49

50 51 52
# flagging
# you can only flag a listing once...
def can_flag(flagger, listing):
Daniel W Bond's avatar
Daniel W Bond committed
53 54
    user_flag_num = Flag.objects.filter(flagger=flagger,
                                        listing=listing).count()
55 56 57 58 59 60
    # we're assuming that this isn't going to go over 1
    if user_flag_num:
        return False
    else:
        return True

Daniel W Bond's avatar
Daniel W Bond committed
61

62 63 64 65 66 67 68
# get the listing's slug to pass to the create flag page
def flag_slug(flagger, listing):
    if not can_flag(flagger, listing):
        return Flag.objects.get(flagger=flagger, listing=listing).slug
    else:
        return None

69

Daniel W Bond's avatar
Daniel W Bond committed
70 71 72 73
# rating
# (basically) duplicated code!!!
def can_rate(rater, listing):
    user_rate_num = Rating.objects.filter(rater=rater,
Daniel W Bond's avatar
Daniel W Bond committed
74
                                          listing=listing).count()
Daniel W Bond's avatar
Daniel W Bond committed
75 76 77 78 79 80 81
    # we're assuming that this isn't going to go over 1
    if user_rate_num:
        return False
    else:
        return True


Daniel W Bond's avatar
Daniel W Bond committed
82 83 84
class ListListings(LoginRequiredMixin, ListView):
    model = Listing
    context_object_name = 'listings'
85
    paginate_by = 16
Daniel W Bond's avatar
Daniel W Bond committed
86
    queryset = Listing.objects.exclude(cancelled=True).order_by('-created')
87
    template_name = 'list_listings.html'
88
    login_url = 'login'
89

Daniel W Bond's avatar
Daniel W Bond committed
90

91
class CreateListing(LoginRequiredMixin, FormValidMessageMixin, CreateView):
92
    model = Listing
Daniel W Bond's avatar
Daniel W Bond committed
93 94
    fields = ['isbn', 'title', 'author', 'edition', 'year', 'course_abbr',
              'condition', 'access_code', 'price', 'photo', 'description']
95 96 97
    template_name = 'create_listing.html'
    context_object_name = 'listing'
    # ISBN query!
98
    login_url = 'login'
99

100
    # eventually figure out how to use {% url 'create_listing' %}
101 102
    form_valid_message = mark_safe("""Your listing was successfully created!
                         <a href="/share/new/">Create another here!</a>""")
103

104
    def form_valid(self, form):
105 106
        me = Student.objects.get(user=self.request.user)

107
        form.instance.poster = me
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127

        image_name = form.instance.photo.name
        user_image = Image.open(form.instance.photo)
        image_format = user_image.format

        print user_image
        width, height = user_image.size
        print user_image.size
        print width, "width"
        print height, "height"
        maxsize = (2560, 1920)
        # five megapixels is 2560x1920
        if (width > 2560) or (height > 1920):
            user_image.thumbnail(maxsize)

            temp_image = StringIO()
            user_image.save(temp_image, image_format)
            temp_image.seek(0)

            new_uploaded_file = SimpleUploadedFile(image_name,
Daniel W Bond's avatar
Daniel W Bond committed
128
                                    temp_image.read(), content_type=image_format)
129 130 131

            form.instance.photo = new_uploaded_file

132
        return super(CreateListing, self).form_valid(form)
133

134 135
    def get_context_data(self, **kwargs):
        context = super(CreateListing, self).get_context_data(**kwargs)
136

137 138
        form = ListingForm()
        context['my_form'] = form
139 140
        return context

Daniel W Bond's avatar
Daniel W Bond committed
141
    @ratelimit(key='user', rate='5/m', method='POST', block=True)
142
    @ratelimit(key='user', rate='50/day', method='POST', block=True)
Daniel W Bond's avatar
Daniel W Bond committed
143 144 145
    def post(self, request, *args, **kwargs):
        return super(CreateListing, self).post(request, *args, **kwargs)

Daniel W Bond's avatar
Daniel W Bond committed
146

147 148 149 150 151 152 153 154 155 156 157 158
class DeleteListing(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
    model = Listing
    context_object_name = 'listing'
    template_name = 'delete_listing.html'
    login_url = 'login'

    def get_success_url(self):
        return reverse('flag_mod')


# These next three views are tied together...
class DetailListing(LoginRequiredMixin, DetailView):
159 160
    model = Listing
    context_object_name = 'listing'
161
    template_name = 'detail_listing.html'
162

163 164
    login_url = 'login'

Daniel W Bond's avatar
Daniel W Bond committed
165 166
    def get_context_data(self, **kwargs):
        context = super(DetailListing, self).get_context_data(**kwargs)
167
        me = Student.objects.get(user=self.request.user)
168 169

        # make the form available to the template on get
170
        # set the bidder and the listing
171
        form = BidForm(initial={'listing': self.get_object()})
172
        form.fields['listing'].widget = HiddenInput()
173 174
        context['my_form'] = form

Daniel W Bond's avatar
Daniel W Bond committed
175 176 177 178 179
        try:
            context['rating'] = Rating.objects.get(listing=self.get_object())
        except:
            context['rating'] = False

Daniel W Bond's avatar
Daniel W Bond committed
180
        # bids, filter by listing name of the current listing, order by date created
181
        context['bids'] = Bid.objects.filter(listing=self.get_object()).order_by('-price')
182 183 184 185
        context['bid_count'] = Bid.objects.filter(listing=self.get_object).count()
        # flags
        context['flags'] = Flag.objects.filter(listing=self.get_object()).order_by('-created')
        context['flag_count'] = Flag.objects.filter(listing=self.get_object()).count()
186 187
        context['can_flag'] = can_flag(me, self.get_object())
        context['flag_slug'] = flag_slug(me, self.get_object())
Daniel W Bond's avatar
Daniel W Bond committed
188 189
        return context

190

191
class DetailListingNoAuth(DetailView):
192 193
    model = Listing
    context_object_name = 'listing'
194
    template_name = 'detail_listing_no_auth.html'
195

196 197 198 199 200
    def get_context_data(self, **kwargs):
        context = super(DetailListingNoAuth, self).get_context_data(**kwargs)
        context['flag_count'] = Flag.objects.filter(listing=self.get_object()).count()
        context['flags'] = Flag.objects.filter(listing=self.get_object()).order_by('-created')
        return context
201

202
class CreateBid(LoginRequiredMixin, CreateView):
203
    model = Bid
Daniel W Bond's avatar
Daniel W Bond committed
204
    fields = ['listing', 'price', 'text', ]
205
    context_object_name = 'bid'
206
    template_name = 'detail_listing.html'
207

208 209
    login_url = 'login'

210 211 212 213 214 215
    def form_valid(self, form):
        me = Student.objects.get(user=self.request.user)

        form.instance.bidder = me
        return super(CreateBid, self).form_valid(form)

216
    def get_success_url(self):
Daniel W Bond's avatar
Daniel W Bond committed
217 218 219
        return reverse('detail_listing',
                       kwargs={'slug': self.object.listing.slug})

220

221
# ...to make this single view
222
class ListingPage(View):
223 224 225 226 227

    # see this page for an explanation
    # https://docs.djangoproject.com/en/1.7/topics/class-based-views/mixins/#an-alternative-better-solution

    def get(self, request, *args, **kwargs):
228 229 230 231 232 233
        if self.request.user.is_authenticated():
            view = DetailListing.as_view()
            return view(request, *args, **kwargs)
        else:
            view = DetailListingNoAuth.as_view()
            return view(request, *args, **kwargs)
234

Daniel W Bond's avatar
Daniel W Bond committed
235 236
    @ratelimit(key='user', rate='5/m', method='POST', block=True)
    # rate limit is higher for bids
237
    @ratelimit(key='user', rate='100/d', method='POST', block=True)
238
    def post(self, request, *args, **kwargs):
239 240 241 242 243
        if self.request.user.is_authenticated():
            view = CreateBid.as_view()
            return view(request, *args, **kwargs)
        else:
            pass
244

245

Daniel W Bond's avatar
Daniel W Bond committed
246
# and we return to our regularly schedule programming
247 248
class CreateFlag(LoginRequiredMixin, CreateView):
    model = Flag
Daniel W Bond's avatar
Daniel W Bond committed
249
    fields = ['reason', ]
250
    template_name = 'create_flag.html'
251
    context_object_name = 'flag'
252
    login_url = 'login'
253

254
    def get(self, request, *args, **kwargs):
255 256
        me = Student.objects.get(user=self.request.user)

257
        # duplicated code!!!
258 259 260 261 262
        current_url = self.request.get_full_path()
        listing_slug = current_url.split('/')[3]
        # [u'', u'share', u'listing', u'C1s3oD', u'flag']
        selected_listing = Listing.objects.get(slug=listing_slug)

263
        posting_student = selected_listing.poster
264

265 266 267 268 269 270 271 272 273 274
        # can only create a flag if you haven't previously created one
        if not can_flag(me, selected_listing):
            # because the page shouldn't exist in this scenario
            raise Http404

        # you can't flag your own listing
        if (posting_student == me):
            return HttpResponseForbidden()
        else:
            return super(CreateFlag, self).get(request, *args, **kwargs)
275 276 277 278 279

    def get_context_data(self, **kwargs):
        context = super(CreateFlag, self).get_context_data(**kwargs)
        me = Student.objects.get(user=self.request.user)

280
        # duplicated code!!!
281 282 283 284 285 286
        current_url = self.request.get_full_path()
        listing_slug = current_url.split('/')[3]
        # [u'', u'share', u'listing', u'C1s3oD', u'flag']
        selected_listing = Listing.objects.get(slug=listing_slug)

        context['listing'] = selected_listing
287 288 289

        form = FlagForm()
        context['my_form'] = form
290 291
        return context

292 293 294 295 296 297 298 299 300 301 302 303
    def form_valid(self, form):
        me = Student.objects.get(user=self.request.user)

        current_url = self.request.get_full_path()
        listing_slug = current_url.split('/')[3]
        # [u'', u'share', u'listing', u'C1s3oD', u'flag']
        selected_listing = Listing.objects.get(slug=listing_slug)

        form.instance.flagger = me
        form.instance.listing = selected_listing
        return super(CreateFlag, self).form_valid(form)

Daniel W Bond's avatar
Daniel W Bond committed
304
    @ratelimit(key='user', rate='5/m', method='POST', block=True)
305
    @ratelimit(key='user', rate='100/d', method='POST', block=True)
Daniel W Bond's avatar
Daniel W Bond committed
306 307 308
    def post(self, request, *args, **kwargs):
        return super(CreateFlag, self).post(request, *args, **kwargs)

309 310 311 312
    def get_success_url(self):
        return reverse('detail_listing',
                       kwargs={'slug': self.object.listing.slug})

Daniel W Bond's avatar
Daniel W Bond committed
313

314 315
class DeleteFlag(LoginRequiredMixin, DeleteView):
    model = Flag
316
    context_object_name = 'flag'
317
    template_name = 'delete_flag.html'
318
    login_url = 'login'
319

320
    def get(self, request, *args, **kwargs):
321
        me = Student.objects.get(user=self.request.user)
322
        flag_student = self.get_object().flagger
323

324
        # if you didn't create the flag, you can't delete the flag
325
        if not(flag_student == me):
326
            return HttpResponseForbidden()
327 328
        else:
            return super(DeleteFlag, self).get(request, *args, **kwargs)
329

330 331 332
    def get_success_url(self):
        return reverse('detail_listing',
                       kwargs={'slug': self.object.listing.slug})
333

Daniel W Bond's avatar
Daniel W Bond committed
334

335
# not implemented -- tbd
336 337 338 339 340 341
class DeleteBid(LoginRequiredMixin, DeleteView):
    model = Bid
    success_url = '/'

    # can be deleted by either creator or person for lister

Daniel W Bond's avatar
Daniel W Bond committed
342

Daniel W Bond's avatar
Daniel W Bond committed
343
class EditBid(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
344
    model = Bid
Daniel W Bond's avatar
Daniel W Bond committed
345 346
    template_name = 'bid_edit.html'
    context_object_name = 'bid'
Daniel W Bond's avatar
Daniel W Bond committed
347
    # form_class = EditBidForm
Daniel W Bond's avatar
Daniel W Bond committed
348 349
    fields = ['price', 'text', ]

350
    login_url = 'login'
Daniel W Bond's avatar
Daniel W Bond committed
351

352
    form_valid_message = "Your bid was successfully updated!"
Daniel W Bond's avatar
Daniel W Bond committed
353

354
    def get(self, request, *args, **kwargs):
Daniel W Bond's avatar
Daniel W Bond committed
355 356 357
        me = Student.objects.get(user=self.request.user)
        bidding_student = self.get_object().bidder

358 359
        # if exchanged or cancelled, this page doesn't exist
        if self.get_object().listing.exchanged or self.get_object().listing.cancelled:
Daniel W Bond's avatar
Daniel W Bond committed
360 361
            raise Http404

362 363 364 365 366 367 368 369
        if not(bidding_student == me):
            return HttpResponseForbidden()
        else:
            return super(EditBid, self).get(request, *args, **kwargs)

    def get_success_url(self):
        return reverse('detail_listing',
                       kwargs={'slug': self.object.listing.slug})
370

Daniel W Bond's avatar
Daniel W Bond committed
371

372
class EditListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
Daniel W Bond's avatar
Daniel W Bond committed
373
    model = Listing
374 375
    template_name = 'listing_edit.html'
    context_object_name = 'listing'
Daniel W Bond's avatar
Daniel W Bond committed
376
    # form_class = EditListingForm
377 378 379
    fields = ['title', 'author', 'isbn', 'year', 'edition', 'condition',
              'access_code', 'description', 'price', 'photo', ]

380
    login_url = 'login'
Daniel W Bond's avatar
Daniel W Bond committed
381

382
    form_valid_message = "Your listing was successfully updated!"
383

384 385 386 387 388 389 390 391 392 393 394
    def get(self, request, *args, **kwargs):
        me = Student.objects.get(user=self.request.user)
        posting_student = self.get_object().poster

        if (self.get_object().cancelled is True):
            raise Http404

        if not(posting_student == me):
            return HttpResponseForbidden()
        else:
            return super(EditListing, self).get(request, *args, **kwargs)
395 396 397 398

    def get_context_data(self, **kwargs):
        context = super(EditListing, self).get_context_data(**kwargs)

399
        me = Student.objects.get(user=self.request.user)
400
        posting_student = self.get_object().poster
401

402
        if not(posting_student == me):
403
            return HttpResponseForbidden()
404

Daniel W Bond's avatar
Daniel W Bond committed
405 406
        return context

407

408
class ExchangeListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
409
    model = Listing
Daniel W Bond's avatar
Daniel W Bond committed
410
    fields = ['email_message', 'winning_bid', ]
411
    context_object_name = 'listing'
412
    template_name = 'listing_exchange.html'
413
    login_url = 'login'
414

415
    form_valid_message = "Your email was successfully sent!"
416

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
    def get(self, request, *args, **kwargs):
        me = Student.objects.get(user=self.request.user)
        posting_student = self.get_object().poster

        bid_count = Bid.objects.filter(listing=self.get_object).count()
        if bid_count < 1:
            # because the page shouldn't exist in this scenario
            raise Http404

        if (self.get_object().cancelled is True):
            raise Http404

        if not(posting_student == me):
            return HttpResponseForbidden()
        else:
            return super(ExchangeListing, self).get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(ExchangeListing, self).get_context_data(**kwargs)

        form = ExchangeListingForm()
        form.fields['winning_bid'].queryset = Bid.objects.filter(listing=self.get_object())

        context['my_form'] = form

        return context

444
    def form_valid(self, form):
445
        # filling out fields
446
        today = date.today()
447
        self.obj = self.get_object()
448

449
        form.instance.exchanged = True
450
        form.instance.date_closed = today
451 452

        # sending email
Daniel W Bond's avatar
Daniel W Bond committed
453
        # I'm still second guessing as to whether this should be in this method
454 455
        text_email = get_template('email/exchanged.txt')
        html_email = get_template('email/exchanged.html')
456

Daniel W Bond's avatar
Daniel W Bond committed
457 458
        email_context = Context({
            'bidder_first_name': form.instance.winning_bid.bidder.user.first_name,
459
            'poster_name': self.obj.poster.user.get_full_name(),
Daniel W Bond's avatar
Daniel W Bond committed
460 461
            'bid_num': form.instance.winning_bid.price,
            'listing_title': self.obj.title,
462
            'poster_email': self.obj.poster.user.email,
Daniel W Bond's avatar
Daniel W Bond committed
463
            'email_message': form.instance.email_message, })
464

Daniel W Bond's avatar
Daniel W Bond committed
465
        subject, from_email, to, cc = ('Your bid has been selected on Bookshare!',
Daniel W Bond's avatar
Daniel W Bond committed
466
                                       'no-reply@bookshare.srct.io',
Daniel W Bond's avatar
Daniel W Bond committed
467
                                       # form.instance.winning_bid.bidder.user.email,
468
                                       # self.obj.poster.user.email)
469 470
                                       'success@simulator.amazonses.com',
                                       'success@simulator.amazonses.com')
471
        text_content = text_email.render(email_context)
472
        html_content = html_email.render(email_context)
Daniel W Bond's avatar
Daniel W Bond committed
473 474
        msg = EmailMultiAlternatives(subject, text_content,
                                     from_email, [to], [cc])
475
        msg.attach_alternative(html_content, "text/html")
476 477
        msg.send()

478 479 480
        self.obj.poster.emails_sent += 2
        self.obj.poster.save()

481
        return super(ExchangeListing, self).form_valid(form)
482

Daniel W Bond's avatar
Daniel W Bond committed
483
    @ratelimit(key='user', rate='5/m', method='POST', block=True)
484
    @ratelimit(key='user', rate='50/d', method='POST', block=True)
Daniel W Bond's avatar
Daniel W Bond committed
485
    def post(self, request, *args, **kwargs):
486
        return super(ExchangeListing, self).post(request, *args, **kwargs)
Daniel W Bond's avatar
Daniel W Bond committed
487

488

489
class UnExchangeListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
490
    model = Listing
491
    fields = ['email_message', ]
492
    context_object_name = 'listing'
493
    template_name = 'listing_unexchange.html'
494
    login_url = 'login'
Daniel W Bond's avatar
Daniel W Bond committed
495

496
    form_valid_message = """Your exchange has been successfully cancelled,
497
                     and your email successfully sent!"""
498

499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
    def get(self, request, *args, **kwargs):
        me = Student.objects.get(user=self.request.user)
        posting_student = self.get_object().poster

        if (self.get_object().cancelled is True):
            raise Http404

        if not(posting_student == me):
            return HttpResponseForbidden()
        else:
            return super(UnExchangeListing, self).get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super(UnExchangeListing, self).get_context_data(**kwargs)

        form = UnExchangeListingForm()
        context['my_form'] = form

        return context

519
    def form_valid(self, form):
520
        self.obj = self.get_object()
521 522
        text_email = get_template('email/unexchanged.txt')
        html_email = get_template('email/unexchanged.html')
523 524 525

        email_context = Context({
            'bidder_first_name': self.obj.winning_bid.bidder.user.first_name,
526
            'poster_name': self.obj.poster.user.get_full_name(),
527 528
            'bid_num': self.obj.winning_bid.price,
            'listing_title': self.obj.title,
529 530
            'poster_email': self.obj.poster.user.email,
            'poster_email': form.instance.email_message, })
531 532 533

        subject, from_email, to, cc = ('Your transaction has been cancelled on Bookshare',
                                       'no-reply@bookshare.srct.io',
Daniel W Bond's avatar
Daniel W Bond committed
534
                                       # self.obj.winning_bid.bidder.user.email,
535
                                       # self.obj.poster.user.email)
536 537 538 539 540 541 542 543 544
                                       'success@simulator.amazonses.com',
                                       'success@simulator.amazonses.com')
        text_content = text_email.render(email_context)
        html_content = html_email.render(email_context)
        msg = EmailMultiAlternatives(subject, text_content,
                                     from_email, [to], [cc])
        msg.attach_alternative(html_content, "text/html")
        msg.send()

545 546 547
        self.obj.poster.emails_sent += 2
        self.obj.poster.save()

548
        # this has to come after the email has been sent, otherwise these are
Daniel W Bond's avatar
Daniel W Bond committed
549
        # cleaned out
550
        form.instance.exchanged = False
551 552
        form.instance.date_closed = None
        form.instance.winning_bid = None
553

554
        return super(UnExchangeListing, self).form_valid(form)
555

Daniel W Bond's avatar
Daniel W Bond committed
556
    @ratelimit(key='user', rate='5/m', method='POST', block=True)
557
    @ratelimit(key='user', rate='50/d', method='POST', block=True)
Daniel W Bond's avatar
Daniel W Bond committed
558
    def post(self, request, *args, **kwargs):
559
        return super(UnExchangeListing, self).post(request, *args, **kwargs)
560

Daniel W Bond's avatar
Daniel W Bond committed
561

562
class CancelListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
Daniel W Bond's avatar
Daniel W Bond committed
563
    model = Listing
564
    fields = []
565
    template_suffix_name = '_cancel'
566 567
    context_object_name = 'listing'
    template_name = 'listing_cancel.html'
568
    login_url = 'login'
Daniel W Bond's avatar
Daniel W Bond committed
569

570 571
    form_valid_message = "Your listing was successfully cancelled!"

572
    def get(self, request, *args, **kwargs):
573
        me = Student.objects.get(user=self.request.user)
574
        posting_student = self.get_object().poster
Daniel W Bond's avatar
Daniel W Bond committed
575

576 577 578 579
        # you can only cancel the listing if the listing isn't already cancelled
        if (self.get_object().cancelled is True):
            raise Http404

580
        if not(posting_student == me):
581
            return HttpResponseForbidden()
582 583
        else:
            return super(CancelListing, self).get(request, *args, **kwargs)
Daniel W Bond's avatar
Daniel W Bond committed
584

585 586 587 588 589 590
    def form_valid(self, form):
        today = date.today()

        form.instance.cancelled = True
        form.instance.date_closed = today
        return super(CancelListing, self).form_valid(form)
591

Daniel W Bond's avatar
Daniel W Bond committed
592

593
class ReopenListing(LoginRequiredMixin, FormValidMessageMixin, UpdateView):
594
    model = Listing
595
    fields = []
596
    template_suffix_name = '_reopen'
597 598
    context_object_name = 'listing'
    template_name = 'listing_reopen.html'
599
    login_url = 'login'
600

601
    form_valid_message = "Your listing was successfully reopened!"
602

603
    def get(self, request, *args, **kwargs):
604
        me = Student.objects.get(user=self.request.user)
605
        posting_student = self.get_object().poster
606

607 608 609 610
        # you can only reopen the listing if the listing is cancelled
        if (self.get_object().cancelled is False):
            raise Http404

611
        if not(posting_student == me):
612
            return HttpResponseForbidden()
613 614
        else:
            return super(ReopenListing, self).get(request, *args, **kwargs)
615

616 617 618 619
    def form_valid(self, form):
        form.instance.cancelled = False
        form.instance.date_closed = None
        return super(ReopenListing, self).form_valid(form)
Daniel W Bond's avatar
Daniel W Bond committed
620 621 622 623 624 625 626 627 628


class CreateRating(LoginRequiredMixin, CreateView):
    model = Rating
    fields = ['stars', 'review', ]
    template_name = 'create_rating.html'
    context_object_name = 'rating'
    login_url = 'login'

629
    def get(self, request, *args, **kwargs):
Daniel W Bond's avatar
Daniel W Bond committed
630 631
        me = Student.objects.get(user=self.request.user)

632
        # duplicated code!!!
Daniel W Bond's avatar
Daniel W Bond committed
633 634 635 636 637
        current_url = self.request.get_full_path()
        listing_slug = current_url.split('/')[3]
        # [u'', u'share', u'listing', u'C1s3oD', u'flag']
        selected_listing = Listing.objects.get(slug=listing_slug)

638
        winning_student = selected_listing.winning_bid.bidder
Daniel W Bond's avatar
Daniel W Bond committed
639

640 641 642 643 644 645 646 647 648 649
        # can only create a rating if you haven't previously created one
        if not can_rate(me, selected_listing):
            # because the page shouldn't exist in this scenario
            raise Http404

        # you can only rate a listing that you won
        if not (winning_student == me):
            return HttpResponseForbidden()
        else:
            return super(CreateRating, self).get(request, *args, **kwargs)
Daniel W Bond's avatar
Daniel W Bond committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668

    def get_context_data(self, **kwargs):
        context = super(CreateRating, self).get_context_data(**kwargs)
        me = Student.objects.get(user=self.request.user)

        # duplicated code!!!
        current_url = self.request.get_full_path()
        listing_slug = current_url.split('/')[3]
        # [u'', u'share', u'listing', u'C1s3oD', u'flag']
        selected_listing = Listing.objects.get(slug=listing_slug)

        winning_student = selected_listing.winning_bid.bidder

        context['listing'] = selected_listing

        form = RatingForm()
        context['my_form'] = form
        return context

669 670 671 672 673 674 675 676 677 678 679 680
    def form_valid(self, form):
        me = Student.objects.get(user=self.request.user)

        current_url = self.request.get_full_path()
        listing_slug = current_url.split('/')[3]
        # [u'', u'share', u'listing', u'C1s3oD', u'flag']
        selected_listing = Listing.objects.get(slug=listing_slug)

        form.instance.rater = me
        form.instance.listing = selected_listing
        return super(CreateRating, self).form_valid(form)

681
    # no per-day limit because you can only rate listings you've exchanged
Daniel W Bond's avatar
Daniel W Bond committed
682 683 684 685
    @ratelimit(key='user', rate='5/m', method='POST', block=True)
    def post(self, request, *args, **kwargs):
        return super(CreateRating, self).post(request, *args, **kwargs)

686 687 688 689
    def get_success_url(self):
        return reverse('ratings',
                       kwargs={'slug': self.object.listing.poster.slug})

Daniel W Bond's avatar
Daniel W Bond committed
690 691 692 693 694

class EditRating(LoginRequiredMixin, UpdateView):
    model = Rating
    template_name = 'rating_edit.html'
    context_object_name = 'rating'
Daniel W Bond's avatar
Daniel W Bond committed
695
    # form_class = EditListingForm
Daniel W Bond's avatar
Daniel W Bond committed
696 697 698 699
    login_url = 'login'

    fields = ['stars', 'review', ]

700
    def get(self, request, *args, **kwargs):
Daniel W Bond's avatar
Daniel W Bond committed
701 702 703 704 705
        me = Student.objects.get(user=self.request.user)
        rating_student = self.get_object().rater

        if not(rating_student == me):
            return HttpResponseForbidden()
706 707
        else:
            return super(EditRating, self).get(request, *args, **kwargs)
Daniel W Bond's avatar
Daniel W Bond committed
708

709 710 711
    def get_success_url(self):
        return reverse('ratings',
                       kwargs={'slug': self.object.listing.poster.slug})
Daniel W Bond's avatar
Daniel W Bond committed
712 713 714 715 716 717 718 719


class DeleteRating(LoginRequiredMixin, DeleteView):
    model = Rating
    context_object_name = 'rating'
    template_name = 'delete_rating.html'
    login_url = 'login'

720
    def get(self, request, *args, **kwargs):
Daniel W Bond's avatar
Daniel W Bond committed
721 722 723 724 725
        me = Student.objects.get(user=self.request.user)
        rating_student = self.get_object().rater

        if not(rating_student == me):
            return HttpResponseForbidden()
726 727
        else:
            return super(DeleteRating, self).get(request, *args, **kwargs)
Daniel W Bond's avatar
Daniel W Bond committed
728

729 730 731
    def get_success_url(self):
        return reverse('detail_listing',
                       kwargs={'slug': self.object.listing.slug})