models.py 6.59 KB
Newer Older
1
2
3
# standard library imports
from datetime import date
# core django imports
4
from django.db import models
Daniel W Bond's avatar
Daniel W Bond committed
5
from django.core.urlresolvers import reverse
6
7
8
9
from django.core.validators import MaxValueValidator, RegexValidator
# third party imports
from randomslugfield import RandomSlugField
from model_utils.models import TimeStampedModel
10
from dateutil.relativedelta import relativedelta
11
# imports from your apps
Daniel W Bond's avatar
Daniel W Bond committed
12
from core.models import Student  # and later, Course
13

Daniel W Bond's avatar
Daniel W Bond committed
14

15
class Listing(TimeStampedModel):
16

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    NEW = 'New'
    LIKE_NEW = 'Like New'
    VERY_GOOD = 'Very Good'
    GOOD = 'Good'
    ACCEPTABLE = 'Acceptable'
    UNACCEPTABLE = 'Unacceptable'

    BOOK_CONDITION_CHOICES = (
        (NEW, 'New'),
        (LIKE_NEW, 'Like New'),
        (VERY_GOOD, 'Very Good'),
        (GOOD, 'Good'),
        (ACCEPTABLE, 'Acceptable'),
        (UNACCEPTABLE, 'Unacceptable'),
    )

33
34
35
36
37
38
39
40
41
42
    NOT_APPLICABLE = 'Not Applicable'
    AC_INCLUDED = 'Access Code Included'
    AC_NOT_INCLUDED = 'Access Code NOT Included'

    ACCESS_CODE_CHOICES = (
        (NOT_APPLICABLE, 'Not Applicable'),
        (AC_INCLUDED, 'Access Code Included'),
        (AC_NOT_INCLUDED, 'Access Code NOT Included'),
    )

43
    poster = models.ForeignKey(Student)
44

Daniel W Bond's avatar
Daniel W Bond committed
45
46
47
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=200)
    isbn = models.CharField(max_length=20,
48
                            validators=[RegexValidator('^[0-9xX-]{10,20}',
Daniel W Bond's avatar
Daniel W Bond committed
49
                                        message='Please enter a valid ISBN.')])
Daniel W Bond's avatar
Daniel W Bond committed
50
    year = models.IntegerField(null=True, blank=True,
51
        # some professors may assign books still to be officially published
Daniel W Bond's avatar
Daniel W Bond committed
52
        validators=[MaxValueValidator(date.today().year+1)])
53
    edition = models.PositiveIntegerField(null=True, blank=True, default=1,
Daniel W Bond's avatar
Daniel W Bond committed
54
        validators=[MaxValueValidator(1000)])
55
    # would have to load in every conceivable course first
Daniel W Bond's avatar
Daniel W Bond committed
56
    # course = models.ForeignKey(Course)
Daniel W Bond's avatar
Daniel W Bond committed
57
58
59
60
    condition = models.CharField(choices=BOOK_CONDITION_CHOICES,
                                 max_length=20, default=GOOD)
    access_code = models.CharField(choices=ACCESS_CODE_CHOICES,
                                   max_length=30, default=NOT_APPLICABLE)
Daniel W Bond's avatar
Daniel W Bond committed
61
    course_abbr = models.CharField(max_length=10, blank=True,
62
                                   validators=[RegexValidator('^([a-zA-Z]){2,4} (\d){3}$',
Daniel W Bond's avatar
Daniel W Bond committed
63
                                               message='Please enter a valid course.')])
Daniel W Bond's avatar
Daniel W Bond committed
64
65
66
67
    description = models.TextField(blank=True, max_length=2000)
    price = models.PositiveIntegerField(default=0,
                                        validators=[MaxValueValidator(1000)])
    photo = models.ImageField(max_length=1000, upload_to='listing_photos',
68
                              default='listing_photos/default_listing_photo.jpg')
69
70

    # these remaining fields are for internal usage, not for users
71
    exchanged = models.BooleanField(default=False)
Daniel W Bond's avatar
Daniel W Bond committed
72
    cancelled = models.BooleanField(default=False)
73

Daniel W Bond's avatar
Daniel W Bond committed
74
    email_message = models.TextField(blank=True, max_length=2000)
75

76
    # future feature: tell posters what price their book has been getting
Daniel W Bond's avatar
Daniel W Bond committed
77
78
    winning_bid = models.ForeignKey('Bid', blank=True, null=True,
                                    related_name='winning_bid')
79
    # the date either cancelled or exchanged
Daniel W Bond's avatar
Daniel W Bond committed
80
    date_closed = models.DateField(null=True, blank=True)
81

Daniel W Bond's avatar
Daniel W Bond committed
82
    slug = RandomSlugField(length=6)
Daniel W Bond's avatar
Daniel W Bond committed
83

84
    def active(self):
85
        today = date.today()
86
        # listing last created/modified + a month
Daniel W Bond's avatar
Daniel W Bond committed
87
88
        created_plus_month = self.created.date() + relativedelta(months=1)
        modified_plus_month = self.modified.date() + relativedelta(months=1)
89

90
        # last login + two weeks
91
        last_login_plus_two_weeks = self.poster.last_login.date() +\
Daniel W Bond's avatar
Daniel W Bond committed
92
            relativedelta(weeks=2)
93
94
95
96
97
98

        last_poked = ((today > created_plus_month) or (today > modified_plus_month))
        recent_login = (today > last_login_plus_two_weeks)

        if last_poked and recent_login:
            return False
99
        else:
100
101
            return True

102
103
104
    def bids(self):
        return Bid.objects.filter(listing=self)

105
106
107
108
109
110
    def final_price(self):
        try:
            price = self.winning_bid.price
        except:
            price = None
        return price
111

112
    # retrieve url for object
Daniel W Bond's avatar
Daniel W Bond committed
113
    def get_absolute_url(self):
Daniel W Bond's avatar
Daniel W Bond committed
114
        return reverse('detail_listing', kwargs={'slug': self.slug})
Daniel W Bond's avatar
Daniel W Bond committed
115

116
117
    # object call
    def __unicode__(self):
118
        return '%s : %s' % (self.isbn, self.title)
119
120

    class Meta:
121
        # unique_together = (("ISBN", "poster"),)
Daniel W Bond's avatar
Daniel W Bond committed
122
        ordering = ['isbn', 'title']
123

Daniel W Bond's avatar
Daniel W Bond committed
124

125
126
class Bid(TimeStampedModel):

127
    bidder = models.ForeignKey(Student)
128
    listing = models.ForeignKey(Listing)
Daniel W Bond's avatar
Daniel W Bond committed
129
    price = models.PositiveIntegerField(default=0,
Daniel W Bond's avatar
Daniel W Bond committed
130
                                        validators=[MaxValueValidator(1000)],)
Daniel W Bond's avatar
Daniel W Bond committed
131
    text = models.CharField(blank=True, max_length=2000,)
132

Daniel W Bond's avatar
Daniel W Bond committed
133
134
    slug = RandomSlugField(length=6)

135
    def __unicode__(self):
Daniel W Bond's avatar
Daniel W Bond committed
136
        return '%s\'s bid for $%s' % (self.bidder, str(self.price))
137
138

    class Meta:
Daniel W Bond's avatar
Daniel W Bond committed
139
        ordering = ['-created']
Daniel W Bond's avatar
Daniel W Bond committed
140

Daniel W Bond's avatar
Daniel W Bond committed
141

Daniel W Bond's avatar
Daniel W Bond committed
142
143
144
145
146
147
class Flag(TimeStampedModel):

    WRONG_PRODUCT_TYPE = 'Not a Textbook'
    # may eventually change as additional product categories are added
    OBSCENE = 'Obscene'
    SPAM = 'Spam'
Daniel W Bond's avatar
Daniel W Bond committed
148
149
    COPYRIGHT = 'Copyright Violation'
    ILLEGAL = 'Otherwise Illegal'
Daniel W Bond's avatar
Daniel W Bond committed
150
151
152
153
154

    FLAGGING_REASON_CHOICES = (
        (WRONG_PRODUCT_TYPE, 'Not a Textbook'),
        (OBSCENE, 'Obscene'),
        (SPAM, 'Spam'),
Daniel W Bond's avatar
Daniel W Bond committed
155
156
        (COPYRIGHT, 'Copyright Violation'),
        (ILLEGAL, 'Otherwise Illegal'),
Daniel W Bond's avatar
Daniel W Bond committed
157
158
159
160
161
    )

    flagger = models.ForeignKey(Student)
    listing = models.ForeignKey(Listing)

Daniel W Bond's avatar
Daniel W Bond committed
162
163
164
    reason = models.CharField(choices=FLAGGING_REASON_CHOICES, max_length=30,)

    slug = RandomSlugField(length=6)
Daniel W Bond's avatar
Daniel W Bond committed
165

Daniel W Bond's avatar
Daniel W Bond committed
166
    def __unicode__(self):
Daniel W Bond's avatar
Daniel W Bond committed
167
168
169
170
        return "%s's %s for %s" % (self.flagger.user.username,
                                   self.listing.title,
                                   self.reason)

Daniel W Bond's avatar
Daniel W Bond committed
171
172
    class Meta:
        ordering = ['listing', 'created']
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200


class Rating(TimeStampedModel):

    ONE_STAR = '1'
    TWO_STAR = '2'
    THREE_STAR = '3'
    FOUR_STAR = '4'
    FIVE_STAR = '5'

    STAR_CHOICES = (
        (ONE_STAR, '1'),
        (TWO_STAR, '2'),
        (THREE_STAR, '3'),
        (FOUR_STAR, '4'),
        (FIVE_STAR, '5'),
    )

    rater = models.ForeignKey(Student)
    listing = models.ForeignKey(Listing)

    stars = models.CharField(choices=STAR_CHOICES, max_length=10,)
    review = models.TextField(blank=True, max_length=3000,)

    slug = RandomSlugField(length=6)

    def __unicode__(self):
        return "%s's review of %s" % (self.rater.user.username,
201
                                      self.listing.poster.user.username)
202
203
204

    class Meta:
        ordering = ['rater', 'listing', 'created']