예제 #1
0
class BaseImage(DirtyFieldsMixin, models.Model):
    locale = models.CharField(max_length=2, choices=settings.LANGUAGES,
                              default=settings.LANGUAGE_CODE)
    slug = models.CharField(max_length=64)
    image = models.ImageField(upload_to=RandomizedUploadPath('page'), null=True)

    class Meta:
        abstract = True

    @property
    def optimized_url(self):
        path = urlparse(self.image.url).path
        return (settings.S3_BASE + '/filters:quality(80):format(jpeg)' + path)

    def __str__(self):
        try:
            path = urlparse(self.image.url).path
            return (settings.S3_BASE + '/filters:quality(80):format(jpeg)' + path)
        except:
            return self.image.url if self.image else ''

    def __bool__(self):
        return self.image is not None

    def save(self, *args, **kwargs):
        dirty_fields = self.get_dirty_fields()

        super().save(*args, **kwargs)

        if 'image' in dirty_fields and self.image:
            optimize_jpeg(self.image)
예제 #2
0
class TeamMember(DirtyFieldsMixin, models.Model):
    club = models.ForeignKey(
        Club,
        related_name='team_members',
        on_delete=models.PROTECT
        )
    name = models.CharField(max_length=255)
    job_title_en = models.CharField(max_length=255)
    job_title_fr = models.CharField(max_length=255, blank=True)
    phone = models.CharField(max_length=255)
    email = models.EmailField()
    photo = models.ImageField(
        null=True, upload_to=RandomizedUploadPath('team_member'), blank=True)
    sort = models.IntegerField(default=0)

    class Meta:
        ordering = ('sort',)

    def __str__(self):
        return self.name

    def __repr__(self):
        return '<TeamMember: {}>'.format(self.name)

    @property
    def job_title(self):
        return getattr(self, 'job_title_{}'.format(get_language()), self.job_title_en)

    def save(self, *args, **kwargs):
        dirty_fields = self.get_dirty_fields()

        super().save(*args, **kwargs)

        if 'photo' in dirty_fields and self.photo:
            optimize_jpeg(self.photo)
예제 #3
0
class CertificateAd(models.Model):
    name = models.CharField(max_length=255)
    image = models.ImageField(
        upload_to=RandomizedUploadPath('certificate_ads'))
    image_fr = models.ImageField(
        upload_to=RandomizedUploadPath('certificate_ads_fr'),
        null=True,
        blank=True)

    class Meta:
        ordering = ('name', )

    def __str__(self):
        return self.name

    def __repr__(self):
        return '<CertificateAd: {}>'.format(self.name)
예제 #4
0
class ClubAdvertisement(BaseAdvertisement):
    LANGUAGE_CHOICES = (
        ('EN', 'English'),
        ('FR', 'French'),
    )
    language = models.CharField(
        max_length=2,
        choices=LANGUAGE_CHOICES,
        default='EN',
    )
    thumbnail = models.ImageField(upload_to=RandomizedUploadPath('sponsors/club/thumbnail/'))
    image = models.ImageField(upload_to=RandomizedUploadPath('sponsors/club/image/'))
    club = models.ForeignKey(
        Club,
        related_name='advertisements',
        on_delete=models.PROTECT
        )

    class Meta:
        ordering = ('sort',)
예제 #5
0
class News(models.Model):
    publish_date = models.DateField()
    headline_en = models.CharField(max_length=255)
    headline_fr = models.CharField(max_length=255, null=True, blank=True)
    slug = models.CharField(max_length=128, unique=True)
    summary_en = models.CharField(max_length=255)
    summary_fr = models.CharField(max_length=255, null=True, blank=True)
    content_en = models.TextField()
    content_fr = models.TextField(null=True, blank=True)
    photo = models.ImageField(upload_to=RandomizedUploadPath('corp_news'))
    clubs = models.ManyToManyField(Club, related_name='news')
    show_on_corp_site = models.BooleanField(default=True)
    show_on_club_site = models.BooleanField(default=True)

    class Meta:
        ordering = ('-publish_date',)

    def get_photo(self):
        path = urlparse(self.photo.url).path
        return (
            settings.S3_BASE + '/filters:quality(80):format(jpeg)' + path)

    @property
    def fully_french(self):
        return (self.summary_fr and self.headline_fr and self.content_fr)

    @property
    def headline(self):
        locale = get_language()
        localized = getattr(self, 'headline_{}'.format(locale), self.headline_en)
        return localized or self.headline_en

    @property
    def summary(self):
        locale = get_language()
        localized = getattr(self, 'summary_{}'.format(locale), self.summary_en)
        return localized or self.summary_en

    @property
    def content(self):
        locale = get_language()
        localized = getattr(self, 'content_{}'.format(locale), self.content_en)
        return localized or self.content_en

    def get_absolute_url(self):
        return reverse('news-item', kwargs={'slug': self.slug})
예제 #6
0
class BaseGalleryImage(DirtyFieldsMixin, models.Model):
    sort = models.IntegerField(default=0)
    image = models.ImageField(upload_to=RandomizedUploadPath('gallery/club/'))

    class Meta:
        abstract = True

    def __str__(self):
        return self.image.url if self.image else ''

    def __bool__(self):
        return self.image is not None

    def save(self, *args, **kwargs):
        dirty_fields = self.get_dirty_fields()

        super().save(*args, **kwargs)

        if 'image' in dirty_fields and self.image:
            optimize_jpeg(self.image)
예제 #7
0
class CertificateType(models.Model):
    ONE_YEAR_EXPIRY = 1
    DYNAMIC_EXPIRY_CHOICES = ((ONE_YEAR_EXPIRY, _('One year from creation')), )

    DEFAULT_TEMPLATE = 0
    AG30_TEMPLATE = 1
    PRESTIGE_50_TEMPLATE = 2
    GOLF_FOR_LIFE_TEMPLATE = 3
    TEMPLATE_CHOICES = (
        (DEFAULT_TEMPLATE, _('Default')),
        (AG30_TEMPLATE, _('AG30 Template')),
        (PRESTIGE_50_TEMPLATE, _('Prestige 50 Template')),
        (GOLF_FOR_LIFE_TEMPLATE, _('Golf for Life Template')),
    )

    DEFAULT_CATEGORY = 0
    PLAYERS_CLUB_CATEGORY = 1
    MERCHANDISE_CATEGORY = 2
    RESORT_STAY_CATEGORY = 3
    RAIN_CHECK_CATEGORY = 4
    PRESTIGE_50_CATEGORY = 5
    LEFT_SIDE_CUSTOM = 6
    US_ROUND_CERT_PROGRAM = 7
    CATEGORY_CHOICES = ((DEFAULT_CATEGORY, _('Default')),
                        (PLAYERS_CLUB_CATEGORY, _("Player's Club")),
                        (MERCHANDISE_CATEGORY,
                         _('Merchandise')), (RESORT_STAY_CATEGORY,
                                             _('Resort Stay')),
                        (RAIN_CHECK_CATEGORY,
                         _('Rain Check')), (PRESTIGE_50_CATEGORY,
                                            _('Prestige 50')),
                        (LEFT_SIDE_CUSTOM,
                         _('Left Side Custom')), (US_ROUND_CERT_PROGRAM,
                                                  _('US Round Cert Program')))

    GOLF_SHOP_LOCATION = 'Golf Shop'
    FRONT_DESK_LOCATION = 'Front Desk'
    REDEMPTION_LOCATION_CHOICES = (
        (GOLF_SHOP_LOCATION, _('Golf Shop')),
        (FRONT_DESK_LOCATION, _('Front Desk')),
    )

    header = models.ImageField(
        upload_to=RandomizedUploadPath('certificate_headers'),
        blank=True,
        null=True,
        help_text='Single club banner')

    double_header = models.ImageField(
        upload_to=RandomizedUploadPath('certificate_headers'),
        blank=True,
        null=True,
        help_text='2-club banner')

    name = models.CharField(max_length=255)
    name_fr = models.CharField(max_length=255, blank=True)
    code = models.CharField('Code', max_length=3)
    staging_code = models.CharField('Staging Code',
                                    max_length=3,
                                    blank=True,
                                    null=True)
    advertisement = models.ForeignKey(CertificateAd,
                                      related_name='certificate_types',
                                      null=True,
                                      blank=True,
                                      on_delete=models.PROTECT)
    club = models.ForeignKey(Club,
                             related_name='certificate_types',
                             null=True,
                             blank=True,
                             on_delete=models.PROTECT)
    quantity = models.DecimalField(max_digits=6,
                                   decimal_places=2,
                                   null=True,
                                   blank=True)
    power_cart = models.IntegerField(choices=Certificate.POWER_CART_CHOICES,
                                     null=True,
                                     blank=True)
    expiry_date = models.DateField(null=True, blank=True)
    dynamic_expiry = models.IntegerField(null=True,
                                         blank=True,
                                         choices=DYNAMIC_EXPIRY_CHOICES)
    message = models.TextField(blank=True)
    message_fr = models.TextField(blank=True)
    restrictions = models.TextField(blank=True)
    restrictions_fr = models.TextField(blank=True)
    redemption_location = models.CharField(max_length=64,
                                           default=GOLF_SHOP_LOCATION,
                                           choices=REDEMPTION_LOCATION_CHOICES)
    redemption_details = models.TextField(blank=True)
    redemption_details_fr = models.TextField(blank=True)
    departments = models.ManyToManyField(Department,
                                         through='DepartmentCertificateType',
                                         related_name='certificate_types')
    hide_recipient_name = models.BooleanField(default=False)
    players_club_clubs = models.ManyToManyField(
        Club, related_name='cpc_certificate_types', blank=True)
    players_club_daily_fee_listing = models.BooleanField(default=False)
    template = models.IntegerField(choices=TEMPLATE_CHOICES,
                                   default=DEFAULT_TEMPLATE)
    category = models.IntegerField(choices=CATEGORY_CHOICES,
                                   default=DEFAULT_CATEGORY)

    def localized(self, name, locale='en'):
        if locale.lower() == 'en':
            return getattr(self, name)
        else:
            return getattr(self, '{}_{}'.format(name, locale.lower()))

    class Meta:
        ordering = ('name', )

    def __str__(self):
        return self.name

    def __repr__(self):
        return '<CertificateType: {}>'.format(self.name)
예제 #8
0
class Club(DirtyFieldsMixin, models.Model):
    SILVER = 'SI'
    GOLD = 'GO'
    PLATINUM = 'PL'
    PRESTIGE = 'PR'

    TIER_CHOICES = ((SILVER, _('Silver')), (GOLD, _('Gold')), (PLATINUM,
                                                               _('Platinum')),
                    (PRESTIGE, _('Prestige')))

    tier = models.CharField(
        max_length=2, choices=TIER_CHOICES, blank=True, null=True)

    slug = models.CharField(max_length=64, unique=True, null=True, blank=True)
    name = models.CharField(max_length=255)
    address = models.CharField(max_length=255, null=True, blank=True)
    city = models.CharField(max_length=255, null=True, blank=True)
    state = models.CharField(max_length=2, choices=PROVINCES + STATES, null=True, blank=True)
    postal_code = models.CharField(max_length=10, null=True, blank=True)
    phone = models.CharField(max_length=30, null=True, blank=True)
    fax = models.CharField(max_length=30, null=True, blank=True)
    svg_logo = models.FileField(null=True, upload_to=RandomizedUploadPath('club_logos_svg'),
                                blank=True)
    logo = models.ImageField(null=True, upload_to=RandomizedUploadPath('club_logos'), blank=True)
    dark_logo = models.FileField(null=True, upload_to=RandomizedUploadPath('dark_logos'),
                                 blank=True)
    academy_logo = models.FileField(null=True, upload_to=RandomizedUploadPath('academy_logos'),
                                    blank=True)
    listing_image = models.ImageField(null=True, upload_to=RandomizedUploadPath('listing_image'),
                                      blank=True)
    code = models.CharField(max_length=4, unique=False)
    youtube_url = models.URLField(null=True, blank=True)
    twitter_url = models.URLField(null=True, blank=True)
    facebook_url = models.URLField(null=True, blank=True)
    instagram_url = models.URLField(null=True, blank=True)
    admins = models.ManyToManyField('users.User', related_name='clubs', blank=True)
    region = models.ForeignKey(
        Region,
        related_name='clubs',
        null=True,
        blank=True,
        on_delete=models.PROTECT
        )
    daily_fee_location = models.BooleanField(default=False, help_text='NOTE: As per Brendan, also accounts for several other things in page scaffolding, and is therefore "needed" for building out pages for now.')
    hide_daily_fees = models.BooleanField(default=False, help_text='If you want to hide this club from http://clublink.ca/daily-fee-golf/')
    no_weddings = models.BooleanField(default=False, help_text='Avoid default showing in weddings')
    no_meetings = models.BooleanField(default=False, help_text='Avoid default showing in meetings')
    is_resort = models.BooleanField(default=False)
    bilingual = models.BooleanField(default=False)
    latitude = models.CharField(max_length=32, blank=True)
    longitude = models.CharField(max_length=32, blank=True)
    resort_url = models.URLField(null=True, blank=True)
    resort_weddings_url = models.URLField(null=True, blank=True)
    event_form_email = models.EmailField(null=True, blank=True)
    calendar_rsvp_email = models.EmailField(null=True, blank=True)

    # English uploads
    bistro_menu = models.FileField(null=True, upload_to=RandomizedUploadPath('bistro_menus'),
                                   blank=True)
    wedding_menu = models.FileField(null=True, upload_to=RandomizedUploadPath('wedding_menus'),
                                    blank=True)
    banquet_menu = models.FileField(null=True, upload_to=RandomizedUploadPath('banquet_menus'),
                                    blank=True)
    membership_brochure = models.FileField(null=True, upload_to=RandomizedUploadPath('membership_brochures'),
                                    blank=True)
    golf_tournament_menu = models.FileField(null=True, upload_to=RandomizedUploadPath('golf_tournament_menus'),
                                   blank=True)

    # French uploads
    bistro_menu_fr = models.FileField(null=True, upload_to=RandomizedUploadPath('bistro_menus'),
                                   blank=True)
    wedding_menu_fr = models.FileField(null=True, upload_to=RandomizedUploadPath('wedding_menus'),
                                    blank=True)
    banquet_menu_fr = models.FileField(null=True, upload_to=RandomizedUploadPath('banquet_menus'),
                                    blank=True)
    membership_brochure_fr = models.FileField(null=True, upload_to=RandomizedUploadPath('membership_brochures'),
                                    blank=True)
    golf_tournament_menu_fr = models.FileField(
        null=True,
        upload_to=RandomizedUploadPath('golf_tournament_menus'),
        blank=True)

    teeitup = models.URLField(blank=True, null=True, help_text='What is the TeeItUp url for iframing?')
    site = models.ForeignKey(
        Site,
        blank=True,
        null=True,
        related_name='clubs',
        on_delete=models.PROTECT
        )

    bcg_style = models.BooleanField(default=False, help_text='Use CMS style page like bcg?')

    use_corp_styles = models.BooleanField(
        default=False,
        help_text='This is meant to use the same background as your corp-page equivalent based on the path'
        )

    sales_email = models.EmailField(blank=True, null=True, help_text='Override email for membership inquiries.  Otherwise, it uses the generic one on the server.')

    class Meta:
        ordering = ('name',)

    objects = ClubManager()

    def get_club_tier(self):
        return getattr(Club, self.tier, None)

    @property
    def thumbor_svg_logo(self):
        path = self.svg_logo.url.split('amazonaws.com')[-1]
        return (settings.S3_BASE + '/trim/filters:quality(80):format(png)' + path)

    @property
    def logo_url(self):
        from django.conf import settings
        import os
        config = os.environ.get('DJANGO_CONFIGURATION', None)
        if self.logo:
            if 'development' in config.lower():
                url = 'https://stage-club.s3.amazonaws.com/media/' + self.logo.url.split('/static/')[-1]
            else:
                url = self.logo.url
        else:
            url = ''
        return url

    @property
    def is_us_club(self):
        return self.state in [s[0] for s in STATES]

    @property
    def country(self):
        if self.is_us_club:
            return _('USA')
        elif self.state in [p[0] for p in PROVINCES]:
            return _('Canada')
        return None

    @property
    def has_address(self):
        return bool(self.address or self.city or self.state or self.postal_code)

    def get_full_address(self):
        return '{}, {}, {} {}'.format(self.address, self.city, self.state, self.postal_code)

    def geocode(self):
        gmaps = GoogleMapsClient()
        try:
            lat, lng = gmaps.get_lat_lng('{}, {}'.format(self.name, self.get_full_address()))
        except HTTPError:
            pass
        else:
            self.latitude = lat if lat is not None else ''
            self.longitude = lng if lng is not None else ''

    def __str__(self):
        return self.name

    def __repr__(self):
        return '<Club: {}>'.format(self.name)

    def save(self, *args, **kwargs):
        dirty_fields = self.get_dirty_fields()

        geodata_changed = list_intersect(dirty_fields.keys(),
                                         ['name', 'address', 'city', 'state', 'postal_code'])

        if geodata_changed:
            self.geocode()

        super().save(*args, **kwargs)

        if 'listing_image' in dirty_fields and self.listing_image:
            optimize_jpeg(self.listing_image)
예제 #9
0
class ClubEvent(models.Model):
    MEMBER_EVENT = 0
    NOTICE = 1
    OUTSIDE_EVENT = 2

    TYPE_CHOICES = (
        (MEMBER_EVENT, _('Member Event')),
        (NOTICE, _('Notice')),
        (OUTSIDE_EVENT, _('Outside Event')),
    )

    club = models.ForeignKey(
        Club,
        related_name='events',
        on_delete=models.PROTECT
        )
    event_series = models.ForeignKey(
        EventSeries,
        related_name='events',
        on_delete=models.PROTECT,
        null = True
        )
    name = models.CharField(max_length=255)
    email = models.EmailField(
        max_length=254,
        blank=True,
        null=True,
        help_text='Override for event contact')
    description = models.TextField()
    start_date = models.DateField()
    start_time = models.TimeField(null=True, blank=True)
    end_date = models.DateField()
    end_time = models.TimeField(null=True, blank=True)
    max_attendees = models.IntegerField(default=0)
    max_guests_per_rsvp = models.IntegerField(default=1)
    photo = models.ImageField(upload_to=RandomizedUploadPath('club_events'), null=True, blank=True)
    type = models.IntegerField(choices=TYPE_CHOICES)
    online_registration = models.BooleanField(default=False)
    registration_open_date = models.DateField(null=True, blank=True)
    registration_open_time = models.TimeField(null=True, blank=True)
    registration_close_date = models.DateField(null=True, blank=True)
    registration_close_time = models.TimeField(null=True, blank=True)
    custom_question_1 = models.CharField(max_length=255, null=True, blank=True)
    custom_question_2 = models.CharField(max_length=255, null=True, blank=True)
    custom_question_3 = models.CharField(max_length=255, null=True, blank=True)
    custom_question_4 = models.CharField(max_length=255, null=True, blank=True)
    custom_question_5 = models.CharField(max_length=255, null=True, blank=True)

    class Meta:
        ordering = ('start_date', 'start_time')

    class InvalidGuestCount(Exception):
        pass

    class LimitExceeded(Exception):
        pass

    class AlreadyAttending(Exception):
        pass

    def __str__(self):
        return self.name

    @property
    def start_datetime(self):
        dt = datetime.combine(self.start_date, self.start_time)
        return timezone.make_aware(dt)

    @property
    def end_datetime(self):
        dt = datetime.combine(self.end_date, self.end_time)
        return timezone.make_aware(dt)

    @property
    def date(self):
        if self.all_day:
            return self.start_datetime.strftime('%B %-d')

        d = self.start_datetime.strftime('%B %-d, %-I:%M%p')
        if not self.instant:
            d += ' - {}'.format(self.end_datetime.strftime('%B %-d, %-I:%M%p'))

        return d

    @property
    def all_day(self):
        start = datetime.combine(self.start_date, self.start_time)
        end = datetime.combine(self.end_date, self.end_time)
        twenty_four = start + timedelta(hours=24) == end
        return twenty_four and self.start_time == time(hour=0, minute=0, second=0)

    @property
    def instant(self):
        start = datetime.combine(self.start_date, self.start_time)
        end = datetime.combine(self.end_date, self.end_time)
        return start == end

    @property
    def total_guests(self):
        num_guests = models.F('number_of_adults') + models.F('number_of_children')
        query = self.rsvps.filter(parent__isnull=True).aggregate(number_of_guests=models.Sum(num_guests))
        return query.get('number_of_guests') or 0

    @property
    def is_full(self):
        if not self.max_attendees:
            return False
        return self.total_guests >= self.max_attendees

    @property
    def registration_open_datetime(self):
        if self.registration_open_date and self.registration_open_time:
            dt = datetime.combine(self.registration_open_date, self.registration_open_time)
            return timezone.make_aware(dt)
        return None

    @property
    def registration_close_datetime(self):
        if self.registration_close_date and self.registration_close_time:
            dt = datetime.combine(self.registration_close_date, self.registration_close_time)
            return timezone.make_aware(dt)
        return None

    @property
    def all_possible_registrants(self, user=None):
        from clublink.users.models import User
        return User.objects.all()[6000:6100]

    @property
    def all_registrants(self):
        userids = self.rsvps.values('user')
        from clublink.users.models import User
        return User.objects.filter(id__in=userids)

    @property
    def visible_registrants(self):
        all_users = self.all_registrants
        return all_users.filter(profile__show_in_roster=True).order_by('last_name')

    @property
    def hidden_registrants_count(self):
        return self.rsvps.filter(user__profile__show_in_roster=False).count()

    @property
    def total_registrants_count(self):
        return self.rsvps.filter(parent__isnull=True).count()

    @property
    def is_registration_open(self):
        if not self.online_registration:
            return False

        if self.registration_open_datetime and timezone.now() < self.registration_open_datetime:
            return False

        if self.registration_close_datetime and timezone.now() > self.registration_close_datetime:
            return False

        return True

    def is_rsvped(self, user):
        return self.rsvps.filter(user=user).exists()

    def rsvp(self, user, number_of_adults=1, number_of_children=0, custom_answer_1='',
             custom_answer_2='', custom_answer_3='', custom_answer_4='', custom_answer_5='', parent=None, **kwargs):
        number_of_guests = number_of_adults + number_of_children
        if number_of_guests < 1 or number_of_guests > self.max_guests_per_rsvp:
            raise self.InvalidGuestCount('The max number of guests for this event is {}'.format(self.max_guests_per_rsvp))

        if self.max_attendees and number_of_guests + self.total_guests > self.max_attendees:
            raise self.LimitExceeded('This event is already at full capacity.')

        if self.is_rsvped(user):
            raise self.AlreadyAttending('{} {} is already registered for this event.'.format(user.first_name, user.last_name))

        return ClubEventRSVP.objects.create(
            user=user, event=self, number_of_adults=number_of_adults,
            number_of_children=number_of_children, custom_answer_1=custom_answer_1,
            custom_answer_2=custom_answer_2, custom_answer_3=custom_answer_3,
            custom_answer_4=custom_answer_4, custom_answer_5=custom_answer_5,
            parent=parent
            )