示例#1
0
class KeynoteEvent(BaseEvent):

    speaker_name = models.CharField(
        verbose_name=_('speaker name'),
        max_length=100,
    )
    slug = models.SlugField(
        verbose_name=_('slug'),
        help_text=format_html_lazy(
            _("This is used to link to the speaker's introduction on the "
              "Keynote page, e.g. 'liang2' will link to "
              "'{link}#keynote-speaker-liang2'."),
            link=reverse_lazy('page', kwargs={'path': 'events/keynotes'}),
        ))

    class Meta:
        verbose_name = _('keynote event')
        verbose_name_plural = _('keynote events')

    def __str__(self):
        return ugettext('Keynote: {speaker}'.format(
            speaker=self.speaker_name, ))

    def get_absolute_url(self):
        url = reverse('page', kwargs={'path': 'events/keynotes'})
        split = urllib.parse.urlsplit(url)
        frag = 'keynote-speaker-{slug}'.format(slug=self.slug)
        return urllib.parse.urlunsplit(split._replace(fragment=frag))
示例#2
0
class KeynoteEvent(BaseEvent):

    speaker_name = models.CharField(
        verbose_name=_('speaker name'),
        max_length=100,
    )
    slug = models.SlugField(
        verbose_name=_('slug'),
        help_text=format_html_lazy(
            _("This is used to link to the speaker's introduction on the "
              "Keynote page, e.g. 'liang2' will link to "
              "'{link}#keynote-speaker-liang2'."),
            link=reverse_lazy('page', kwargs={'path': 'conference/keynotes'}),
        ))
    is_remote = models.BooleanField(
        verbose_name=_('is remote'),
        default=False,
    )

    class Meta:
        verbose_name = _('keynote event')
        verbose_name_plural = _('keynote events')

    def __str__(self):
        return gettext('Keynote: {speaker}'.format(
            speaker=self.speaker_name, ))

    def get_absolute_url(self):
        url = reverse('page', kwargs={'path': 'conference/keynotes'})
        split = urllib.parse.urlsplit(url)
        frag = 'keynote-speaker-{slug}'.format(slug=self.slug)
        return urllib.parse.urlunsplit(split._replace(fragment=frag))

    def get_static_data(self):
        path = '/'.join([
            settings.CONFERENCE_DEFAULT_SLUG,
            'assets/keynotes',
            f'{self.slug}.json',
        ])

        keynote_info = finders.find(path)
        if not keynote_info:
            raise FileNotFoundError(path)

        with open(keynote_info) as f:
            data = json.load(f)
        return data

    def get_static_data_for_locale(self, code=None):
        if code is None:
            code = get_language()
        code = code.split('-', 1)[0]

        data = self.get_static_data()
        data = {
            k: v[code] if isinstance(v, dict) and code in v else v
            for k, v in data.items()
        }
        return data
示例#3
0
    class Meta:
        model = TalkProposal
        widgets = {
            'abstract': CharacterCountedTextarea(),
            'objective': CharacterCountedTextarea(),
            'detailed_description': SimpleMDEWidget(),
            'outline': SimpleMDEWidget(),
            'supplementary': SimpleMDEWidget(),
            'pre_recorded_policy': forms.CheckboxInput(),
        }
        help_texts = {
            'abstract': _(
                "<p><a href='#' data-toggle='modal' "
                "data-target='#proposalFieldExampleModal' "
                "data-content='abstract'>Proposal Examples</a>.</p>"
                "<p>The overview of what the talk is about. If the talk "
                "assume some domain knowledge please state it here. If your "
                "talk is accepted, this will be displayed on both the website "
                "and the handbook. Should be one paragraph.</p>"
            ),
            'python_level': format_html_lazy(
                _("The choice of talk level matters during the review "
                  "process. More definition of talk level can be found at the "
                  "<a href='{frontend_host}/en-us/speaking/talk' target='_blank'>"
                  "How to Propose a Talk</a> page. Note that a proposal won't "
                  "be more likely to be accepted because of being 'Novice' "
                  "level. We may contact you to change the talk level when "
                  "we find the content is too-hard or too-easy for the "
                  "target audience."),
                frontend_host=f"{settings.FRONTEND_HOST}",
            ),
            'detailed_description': _(
                "<p><a href='#' data-toggle='modal' "
                "data-target='#proposalFieldExampleModal' "
                "data-content='detailed description'>"
                "Proposal Examples</a>.</p>"
                "<p>Try not be too lengthy to scare away reviewers or "
                "potential audience. A comfortable length is less than 2000 "
                "characters (or about 1200 Chinese characters). Since most "
                "reviewers may not understand the topic as deep as you do, "
                "including related links to the talk topic will help "
                "reviewers understand the proposal. Edit using "
                "<a href='http://daringfireball.net/projects/markdown/basics' "
                "target='_blank' rel='noopener'>Markdown</a>."
            ),
            'recording_policy': format_html_lazy(
                _("PyCon Taiwan will do post-processing to all the presentation "
                  "videos, including but not limited to applying video template, "
                  "banners of sponsors’ logos. Do you agree to give permission to "
                  "PyCon Taiwan to release audio and video of your presentation "
                  "after the conference? More information can be found at "
                  "<a href='{frontend_host}/en-us/speaking/recording' target='_blank'>"
                  "Recording Release</a> page."),
                frontend_host=f"{settings.FRONTEND_HOST}"
            ),
            'slide_link': _(
                "You can add your slide link near or after the conference "
                "day. Not required for review."
            ),
            'referring_policy': _(
                "Whether you agree to give permission to PyCon Taiwan to refer your "
                "talk proposal to your local PyCon community if your proposal did not "
                "get accepted(APAC only)."
            ),
            'objective': _(
                "<p><a href='#' data-toggle='modal' "
                "data-target='#proposalFieldExampleModal' "
                "data-content='objective'>Proposal Examples</a>.</p>"
                "<p>Who is the intended audience for your talk? (Be specific, "
                "\"Python users\" is not a good answer). "
                "And what will the attendees get out of your talk? When they "
                "leave the room, what will they learn that they didn't know "
                "before? This is NOT made public and for REVIEW ONLY.</p>"
            ),
            'supplementary': _(
                "Anything else you'd like the program committee to know when "
                "making their selection: your past speaking experience, "
                "community experience, etc. This is NOT made public and for "
                "REVIEW ONLY. Edit using "
                "<a href='http://daringfireball.net/projects/markdown/basics' "
                "target='_blank' rel='noopener'>Markdown</a>."
            ),
            'outline': _(
                "<p><a href='#' data-toggle='modal' "
                "data-target='#proposalFieldExampleModal' "
                "data-content='outline'>Proposal Examples</a>.</p>"
                "<p>How the talk will be arranged. It is highly recommended "
                "to attach the estimated time length for each sections in the "
                "talk. Talks in favor of 45min should have a fallback plan "
                "about how to shrink the content into a 30min one. Edit using "
                "<a href='http://daringfireball.net/projects/markdown/basics' "
                "target='_blank' rel='noopener'>Markdown</a>. "
                "This is NOT made public and for REVIEW ONLY.</p>"
            ),
            'live_stream_policy': _(
                "If you disagree to authorize PyCon Taiwan to record video, "
                "whether you agree to let PyCon Taiwan live stream your presentation "
                "only for the remote attendees to watch ? (we will not release "
                "audio and video of your presentation)"
            ),
            'first_time_speaker': _(
                "Have you ever been a speaker at PyCon Taiwan in the past?"

            ),
            'pre_recorded_policy': _(
                "Due to the ongoing global pandemic, <strong>PyCon APAC 2022 will "
                "be held fully remote. All online talks this year will be pre-recorded "
                "</strong> so we can broadcast your presentation to the audience "
                "without the network troubles and the time zone differences.<br>"
                "After your proposal is accepted, a pre-recording of the presentation "
                "will be provided to the PyCon APAC 2022 organizing team prior to the "
                "conference.<br>"
                "<strong>Bonus for living in Taiwan</strong><br>"
                "For speakers who reside in Taiwan, we will offer a slot and assist you "
                "to record the presentation in a professional video studio in Taipei "
                "(travel expenses covered)."
            ),
            'talk_language': _(
                "If your previous option is not English, please enter your speaking language"
            ),
        }
示例#4
0
 class Meta:
     model = TalkProposal
     widgets = {
         'abstract': CharacterCountedTextarea(),
         'objective': CharacterCountedTextarea(),
         'detailed_description': SimpleMDEWidget(),
         'outline': SimpleMDEWidget(),
         'supplementary': SimpleMDEWidget(),
     }
     help_texts = {
         'abstract':
         _("<p><a href='#' data-toggle='modal' "
           "data-target='#proposalFieldExampleModal' "
           "data-content='abstract'>Proposal Examples</a>.</p>"
           "<p>The overview of what the talk is about. If the talk "
           "assume some domain knowledge please state it here. If your "
           "talk is accepted, this will be displayed on both the website "
           "and the handbook. Should be one paragraph.</p>"),
         'python_level':
         format_html_lazy(
             _("The choice of talk level matters during the review "
               "process. More definition of talk level can be found at the "
               "<a href='{speaking_talk_url}' target='_blank'>"
               "How to Propose a Talk</a> page. Note that a proposal won't "
               "be more likely to be accepted because of being 'Novice' "
               "level. We may contact you to change the talk level when "
               "we find the content is too-hard or too-easy for the "
               "target audience."),
             speaking_talk_url=reverse_lazy(
                 'page',
                 kwargs={'path': 'speaking/talk'},
             ),
         ),
         'detailed_description':
         _("<p><a href='#' data-toggle='modal' "
           "data-target='#proposalFieldExampleModal' "
           "data-content='detailed description'>"
           "Proposal Examples</a>.</p>"
           "<p>Try not be too lengthy to scare away reviewers or "
           "potential audience. A comfortable length is less than 2000 "
           "characters (or about 1200 Chinese characters). Since most "
           "reviewers may not understand the topic as deep as you do, "
           "including related links to the talk topic will help "
           "reviewers understand the proposal. Edit using "
           "<a href='http://daringfireball.net/projects/markdown/basics' "
           "target='_blank' rel='noopener'>Markdown</a>."),
         'recording_policy':
         format_html_lazy(_(
             "Whether you agree to give permission to PyCon Taiwan to "
             "record, edit, and release audio and video of your "
             "presentation. More information can be found at "
             "<a href='{recording_policy_url}' target='_blank'>"
             "Recording Release</a> page."),
                          recording_policy_url=reverse_lazy(
                              'page',
                              kwargs={'path': 'speaking/recording'},
                          )),
         'slide_link':
         _("You can add your slide link near or after the conference "
           "day. Not required for review."),
         'referring_policy':
         _("Whether you agree to give permission to PyCon Taiwan to "
           "refer your talk proposal to local communities if your "
           "proposal did not get accepted."),
         'objective':
         _("<p><a href='#' data-toggle='modal' "
           "data-target='#proposalFieldExampleModal' "
           "data-content='objective'>Proposal Examples</a>.</p>"
           "<p>Who is the intended audience for your talk? (Be specific, "
           "\"Python users\" is not a good answer). "
           "And what will the attendees get out of your talk? When they "
           "leave the room, what will they learn that they didn't know "
           "before? This is NOT made public and for REVIEW ONLY.</p>"),
         'supplementary':
         _("Anything else you'd like the program committee to know when "
           "making their selection: your past speaking experience, "
           "community experience, etc. This is NOT made public and for "
           "REVIEW ONLY. Edit using "
           "<a href='http://daringfireball.net/projects/markdown/basics' "
           "target='_blank' rel='noopener'>Markdown</a>."),
         'outline':
         _("<p><a href='#' data-toggle='modal' "
           "data-target='#proposalFieldExampleModal' "
           "data-content='outline'>Proposal Examples</a>.</p>"
           "<p>How the talk will be arranged. It is highly recommended "
           "to attach the estimated time length for each sections in the "
           "talk. Talks in favor of 45min should have a fallback plan "
           "about how to shrink the content into a 30min one. Edit using "
           "<a href='http://daringfireball.net/projects/markdown/basics' "
           "target='_blank' rel='noopener'>Markdown</a>. "
           "This is NOT made public and for REVIEW ONLY.</p>"),
         'remoting_policy':
         _("Whether you agree to deliver the talk remotely if the epidemic "
           "(coronavirus) is still raging this August?"),
     }
示例#5
0
class User(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(
        verbose_name=_('email address'),
        max_length=255, unique=True, db_index=True,
    )
    speaker_name = models.CharField(
        verbose_name=_('speaker name'),
        max_length=100,
    )
    bio = EAWTextField(
        verbose_name=_('biography'),
        max_length=1000,
        help_text=_(
            "Describe yourself with 1000 characters or less. "
            "There will be no formatting."
        ),
    )
    photo = models.ImageField(
        verbose_name=_('photo'),
        blank=True, default='', upload_to=photo_upload_to,
    )
    facebook_profile_url = models.URLField(
        verbose_name=_('Facebook'),
        blank=True,
        help_text=format_html_lazy(_(
            "Link to your Facebook profile page. This will be shown when "
            "we display your public information. If you do not know what your "
            "profile page link is, click on "
            "<a href='https://www.facebook.com/me' target='_blank'>"
            "this link</a>, and copy-paste the URL of the page opened. "
            "Remember to log in to Facebook first!"
        )),
    )
    twitter_id = models.CharField(
        verbose_name=_('Twitter'),
        blank=True, max_length=100, validators=[
            RegexValidator(r'^[0-9a-zA-Z_]*$', 'Not a valid Twitter handle'),
        ],
        help_text=_(
            "Your Twitter handle, without the \"@\" sign. This will be "
            "shown when we display your public information."
        ),
    )
    github_id = models.CharField(
        verbose_name=_('GitHub'),
        blank=True, max_length=100, validators=[
            RegexValidator(r'^[0-9a-zA-Z_-]*$', 'Not a valid GitHub account'),
        ],
        help_text=_(
            "Your GitHub account, without the \"@\" sign. This will be "
            "shown when we display your public information."
        ),
    )
    verified = models.BooleanField(
        verbose_name=_('verified'),
        default=False,
        help_text=_(
            "Designates whether the user has verified email ownership."
        ),
    )
    is_staff = models.BooleanField(
        verbose_name=_('staff status'),
        default=False,
        help_text=_(
            "Designates whether the user can log into this admin site."
        ),
    )
    is_active = models.BooleanField(
        verbose_name=_('active'),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as "
            "active. Unselect this instead of deleting accounts."
        ),
    )
    date_joined = models.DateTimeField(
        verbose_name=_('date joined'),
        default=timezone.now,
    )

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        swappable = 'AUTH_USER_MODEL'

    def __str__(self):
        return self.email

    def as_hash(self):
        """Return the user's hash representation.

        Pipeline:
            - pk -> md5 -> first 2 bytes -> b16 -> str.
        """
        return '#{hash_user}'.format(
            hash_user=(
                base64.b16encode(
                    # Picking the first 2 bytes may still run into hash
                    # collisions if you take the whole users list into account,
                    # but as this is used for representing reviewers which is a
                    # comparatively small subset, it's not a big deal.
                    hashlib.md5(str(self.pk).encode('utf-8')).digest()[:2],
                ).decode('utf-8')
            )
        )

    as_hash.short_description = _('Reviewer ID')

    def get_full_name(self):
        return self.speaker_name

    def get_short_name(self):
        return self.speaker_name

    def get_thumbnail_url(self):
        """Return URL to compressed profile photo if set, or a default image.
        """
        if self.photo and os.path.exists(self.photo.path):
            return get_thumbnail(self.photo, '800x800').url
        return static('images/default_head.png')

    def is_valid_speaker(self):
        """Whether the user is a valid speaker.

        :seealso: ``UserQuerySet.get_valid_speakers``
        """
        return (
            self.verified and self.is_active and
            self.speaker_name and self.bio
        )

    def is_reviewer(self):
        return self.has_perm('reviews.add_review')

    @property
    def cospeaking_info_set(self):
        return self.additionalspeaker_set.filter(
            cancelled=False,
            conference=settings.CONFERENCE_DEFAULT_SLUG,
        )

    @property
    def twitter_profile_url(self):
        if not self.twitter_id:
            return ""
        return 'https://twitter.com/{}'.format(self.twitter_id)

    @property
    def github_profile_url(self):
        if not self.github_id:
            return ""
        return 'https://github.com/{}'.format(self.github_id)

    def get_verification_key(self):
        key = signing.dumps(
            obj=getattr(self, self.USERNAME_FIELD),
            salt=settings.SECRET_KEY,
        )
        return key

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def send_verification_email(self, request):
        verification_key = self.get_verification_key()
        verification_url = request.build_absolute_uri(
            reverse('user_verify', kwargs={
                'verification_key': verification_key,
            }),
        )
        context = {
            'user': self,
            'host': request.get_host(),
            'verification_key': verification_key,
            'verification_url': verification_url,
        }
        message = render_to_string(
            'registration/verification_email.txt', context,
        )
        self.email_user(
            subject=gettext('Verify your email address on {host}').format(
                **context
            ),
            message=message, fail_silently=False,
        )

    @property
    def has_agreed_coc(self):
        return CocRecord.objects.filter(user=self, coc_version=settings.COC_VERSION).count() == 1
示例#6
0
 class Meta:
     model = TutorialProposal
     widgets = {
         'abstract': CharacterCountedTextarea(),
         'objective': CharacterCountedTextarea(),
         'detailed_description': SimpleMDEWidget(),
         'outline': SimpleMDEWidget(),
         'supplementary': SimpleMDEWidget(),
     }
     help_texts = {
         'duration':
         _("<p>Tutorials are limited to 1.5 hours in length this year.</p>"
           ),
         'abstract':
         _("<p><a href='#' data-toggle='modal' "
           "data-target='#proposalFieldExampleModal' "
           "data-content='abstract'>Proposal Examples</a>.</p>"
           "<p>The overview of what the talk is about. If the talk "
           "assume some domain knowledge please state it here. If your "
           "talk is accepted, this will be displayed on both the website "
           "and the handbook. Should be one paragraph.</p>"),
         'python_level':
         format_html_lazy(
             _("The choice of talk level matters during the review "
               "process. More definition of talk level can be found at the "
               "<a href='{frontend_host}/en-us/speaking/talk' target='_blank'>"
               "How to Propose a Talk</a> page. Note that a proposal won't "
               "be more likely to be accepted because of being 'Novice' "
               "level. We may contact you to change the talk level when "
               "we find the content is too-hard or too-easy for the "
               "target audience."),
             frontend_host=f"{settings.FRONTEND_HOST}",
         ),
         'detailed_description':
         _("<p><a href='#' data-toggle='modal' "
           "data-target='#proposalFieldExampleModal' "
           "data-content='detailed description'>"
           "Proposal Examples</a>.</p>"
           "<p>Try not be too lengthy to scare away reviewers or "
           "potential audience. A comfortable length is less than 2000 "
           "characters (or about 1200 Chinese characters). Since most "
           "reviewers may not understand the topic as deep as you do, "
           "including related links to the talk topic will help "
           "reviewers understand the proposal. Edit using "
           "<a href='http://daringfireball.net/projects/markdown/basics' "
           "target='_blank' rel='noopener'>Markdown</a>."),
         'recording_policy':
         format_html_lazy(_(
             "Whether you agree to give permission to PyCon Taiwan to "
             "record, edit, and release audio and video of your "
             "presentation. More information can be found at "
             "<a href='{frontend_host}/en-us/speaking/recording' target='_blank'>"
             "Recording Release</a> page."),
                          frontend_host=f"{settings.FRONTEND_HOST}"),
         'slide_link':
         _("You can add your slide link near or after the conference "
           "day. Not required for review."),
         'objective':
         _("Who is the intended audience for your talk? (Be specific, "
           "\"Python users\" is not a good answer). "
           "And what will the attendees get out of your talk? When they "
           "leave the room, what will they learn that they didn't know "
           "before? This is NOT made public and for REVIEW ONLY."),
         'supplementary':
         _("Anything else you'd like the program committee to know when "
           "making their selection: your past speaking experience, "
           "community experience, etc. This is NOT made public and for "
           "REVIEW ONLY. Edit using "
           "<a href='http://daringfireball.net/projects/markdown/basics' "
           "target='_blank' rel='noopener'>Markdown</a>."),
         'outline':
         _("How the tutorial will be arranged. You should enumerate over "
           "each section in your talk and attach each section with the "
           "estimated time length. Edit using "
           "<a href='http://daringfireball.net/projects/markdown/basics' "
           "target='_blank' rel='noopener'>Markdown</a>. "
           "This is NOT made public and for REVIEW ONLY."),
         'remoting_policy':
         _("Whether you agree to deliver the tutorial remotely if the epidemic "
           "(coronavirus) is still raging this August?"),
         'live_stream_policy':
         _("If you disagree to authorize PyCon Taiwan to record video, "
           "whether you agree to let PyCon Taiwan live stream your presentation "
           "only for the remote attendees to watch ? (we will not release "
           "audio and video of your presentation)"),
     }
示例#7
0
class KeynoteEvent(BaseEvent):

    speaker_name = models.CharField(
        verbose_name=_('speaker name'),
        max_length=100,
        default='',
    )
    speaker_bio = models.TextField(
        verbose_name=_('speaker bio'),
        default='',
    )
    speaker_photo = models.ImageField(
        verbose_name=_('speaker photo'),
        default='',
        upload_to=photo_upload_to,
        storage=select_storage,
        help_text=_("Raster format of the speaker's photo, e.g. PNG, JPEG."),
    )
    session_title = models.CharField(
        verbose_name=_('keynote session title'),
        max_length=140,
        default='',
    )
    session_description = models.TextField(
        verbose_name=_('keynote session description'),
        default='',
    )
    session_slides = models.URLField(verbose_name=_('session slides'),
                                     blank=True)
    slido = models.URLField(verbose_name=_('slido'), blank=True)
    social_linkedin = models.URLField(verbose_name=_('social linkedin'),
                                      blank=True)
    social_twitter = models.URLField(verbose_name=_('social twitter'),
                                     blank=True)
    social_github = models.URLField(verbose_name=_('social github'),
                                    blank=True)
    slug = models.SlugField(
        verbose_name=_('slug'),
        help_text=format_html_lazy(
            _("This is used to link to the speaker's introduction on the "
              "Keynote page, e.g. 'liang2' will link to "
              "'{link}#keynote-speaker-liang2'."),
            link=reverse_lazy('page', kwargs={'path': 'conference/keynotes'}),
        ))
    is_remote = models.BooleanField(
        verbose_name=_('is remote'),
        default=False,
    )
    youtube_id = models.CharField(
        verbose_name=_('youtube id'),
        max_length=20,
        blank=True,
    )

    class Meta:
        verbose_name = _('keynote event')
        verbose_name_plural = _('keynote events')

    def __str__(self):
        return gettext('Keynote: {speaker}'.format(
            speaker=self.speaker_name, ))

    def get_absolute_url(self):
        url = reverse('page', kwargs={'path': 'conference/keynotes'})
        split = urllib.parse.urlsplit(url)
        frag = 'keynote-speaker-{slug}'.format(slug=self.slug)
        return urllib.parse.urlunsplit(split._replace(fragment=frag))

    def get_static_data(self):
        path = '/'.join([
            settings.CONFERENCE_DEFAULT_SLUG,
            'assets/keynotes',
            f'{self.slug}.json',
        ])

        keynote_info = finders.find(path)
        if not keynote_info:
            raise FileNotFoundError(path)

        with open(keynote_info) as f:
            data = json.load(f)
        return data

    def get_static_data_for_locale(self, code=None):
        if code is None:
            code = get_language()
        code = code.split('-', 1)[0]

        data = self.get_static_data()
        data = {
            k: v[code] if isinstance(v, dict) and code in v else v
            for k, v in data.items()
        }
        return data
示例#8
0
class User(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(
        verbose_name=_('email address'),
        max_length=255, unique=True, db_index=True,
    )
    speaker_name = models.CharField(
        verbose_name=_('speaker name'),
        max_length=100,
    )
    bio = EAWTextField(
        verbose_name=_('biography'),
        max_length=1000,
        help_text=_(
            "Describe yourself with 500 characters or less. "
            "There will be no formatting."
        ),
    )
    photo = models.ImageField(
        verbose_name=_('photo'),
        blank=True, default='', upload_to=photo_upload_to,
    )
    facebook_profile_url = models.URLField(
        verbose_name=_('Facebook'),
        blank=True,
        help_text=format_html_lazy(_(
            "Link to your Facebook profile page. This will be shown when "
            "we display your public information. If you do not know what your "
            "profile page link is, click on "
            "<a href='https://www.facebook.com/me' target='_blank'>"
            "this link</a>, and copy-paste the URL of the page opened. "
            "Remember to log in to Facebook first!"
        )),
    )
    twitter_id = models.CharField(
        verbose_name=_('Twitter'),
        blank=True, max_length=100, validators=[
            RegexValidator(r'^[0-9a-zA-Z_]*$', 'Not a valid Twitter handle'),
        ],
        help_text=_(
            "Your Twitter handle, without the \"@\" sign. This will be "
            "shown when we display your public information."
        ),
    )
    github_id = models.CharField(
        verbose_name=_('GitHub'),
        blank=True, max_length=100, validators=[
            RegexValidator(r'^[0-9a-zA-Z_-]*$', 'Not a valid GitHub account'),
        ],
        help_text=_(
            "Your GitHub account, without the \"@\" sign. This will be "
            "shown when we display your public information."
        ),
    )
    verified = models.BooleanField(
        verbose_name=_('verified'),
        default=False,
        help_text=_(
            "Designates whether the user has verified email ownership."
        ),
    )
    is_staff = models.BooleanField(
        verbose_name=_('staff status'),
        default=False,
        help_text=_(
            "Designates whether the user can log into this admin site."
        ),
    )
    is_active = models.BooleanField(
        verbose_name=_('active'),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as "
            "active. Unselect this instead of deleting accounts."
        ),
    )
    date_joined = models.DateTimeField(
        verbose_name=_('date joined'),
        default=timezone.now,
    )

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        swappable = 'AUTH_USER_MODEL'

    def __str__(self):
        return self.email

    def get_full_name(self):
        return self.speaker_name

    def get_short_name(self):
        return self.speaker_name

    def is_valid_speaker(self):
        """Whether the user is a valid speaker.

        :seealso: ``UserQuerySet.get_valid_speakers``
        """
        return (
            self.verified and self.is_active and
            self.speaker_name and self.bio
        )

    def is_reviewer(self):
        return self.has_perm('reviews.add_review')

    @property
    def cospeaking_info_set(self):
        return self.additionalspeaker_set.filter(
            cancelled=False,
            conference=settings.CONFERENCE_DEFAULT_SLUG,
        )

    @property
    def twitter_profile_url(self):
        return 'https://twitter.com/{}'.format(self.twitter_id)

    @property
    def github_profile_url(self):
        return 'https://github.com/{}'.format(self.github_id)

    def get_verification_key(self):
        key = signing.dumps(
            obj=getattr(self, self.USERNAME_FIELD),
            salt=settings.SECRET_KEY,
        )
        return key

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def send_verification_email(self, request):
        verification_key = self.get_verification_key()
        verification_url = request.build_absolute_uri(
            reverse('user_verify', kwargs={
                'verification_key': verification_key,
            }),
        )
        context = {
            'user': self,
            'host': request.get_host(),
            'verification_key': verification_key,
            'verification_url': verification_url,
        }
        message = render_to_string(
            'registration/verification_email.txt', context,
        )
        self.email_user(
            subject=ugettext('Verify your email address on {host}').format(
                **context
            ),
            message=message, fail_silently=False,
        )
示例#9
0
class AbstractProposal(models.Model):

    submitter = models.ForeignKey(
        to=settings.AUTH_USER_MODEL,
        verbose_name=_('submitter'),
    )

    title = models.CharField(
        verbose_name=_('title'),
        max_length=140,
    )

    CATEGORY_CHOICES = (
        ('PRAC', _('Best Practices & Patterns')),
        ('COM', _('Community')),
        ('DB', _('Databases')),
        ('DATA', _('Data Analysis')),
        ('EDU', _('Education')),
        ('EMBED', _('Embedded Systems')),
        ('GAME', _('Gaming')),
        ('GRAPH', _('Graphics')),
        ('OTHER', _('Other')),
        ('CORE', _('Python Core (language, stdlib, etc.)')),
        ('INTNL', _('Python Internals')),
        ('LIBS', _('Python Libraries')),
        ('SCI', _('Science')),
        ('SEC', _('Security')),
        ('ADMIN', _('Systems Administration')),
        ('TEST', _('Testing')),
        ('WEB', _('Web Frameworks')),
    )
    category = models.CharField(
        verbose_name=_('category'),
        max_length=5,
        choices=CATEGORY_CHOICES,
    )

    LANGUAGE_CHOICES = (
        ('ENG', _('English')),
        ('CHI', _('Chinese')),
    )
    language = models.CharField(
        verbose_name=_('language'),
        max_length=3,
        choices=LANGUAGE_CHOICES,
    )

    target_audience = models.CharField(
        verbose_name=_('target audience'),
        max_length=140,
        help_text=_(
            "Who is the intended audience for your talk? (Be specific, "
            "\"Python users\" is not a good answer)"),
    )

    abstract = models.TextField(
        verbose_name=_('abstract'),
        max_length=500,
        help_text=_(
            "The overview of what the talk is about. If the talk assume some "
            "domain knowledge please state it here. If your talk is accepted, "
            "this will be displayed on both the website and the handbook. "
            "Should be one paragraph."),
    )

    PYTHON_LVL_CHOICES = (
        ('NOVICE', _('Novice')),
        ('INTERMEDIATE', _('Intermediate')),
        ('EXPERIENCED', _('Experienced')),
    )
    python_level = models.CharField(
        verbose_name=_('Python level'),
        max_length=12,
        choices=PYTHON_LVL_CHOICES,
        help_text=format_html_lazy(
            _("The choice of talk level matters during the review process. "
              "More definition of talk level can be found at the <a href=\""
              "{speaking_talk_url}\" target=\"_blank\">How to Propose a "
              "Talk</a> page. Note that a proposal won't be more likely to be "
              "accepted because of being \"Novice\" level. We may contact you "
              "to change the talk level when we find the content is too-hard "
              "or too-easy for the target audience."),
            speaking_talk_url=reverse_lazy(
                'page',
                kwargs={'path': 'speaking/talk'},
            ),
        ),
    )

    objective = models.TextField(
        verbose_name=_('objective'),
        max_length=500,
        help_text=_(
            "What will the attendees get out of your talk? When they leave "
            "the room, what will they learn that they didn't know before?"),
    )

    detailed_description = models.TextField(
        verbose_name=_('detailed description'),
        blank=True,
        help_text=_(
            "Try not be too lengthy to scare away reviewers or potential "
            "audience. A comfortable length is less than 1000 characters "
            "(or about 600 Chinese characters). Since most reviewers may not "
            "understand the topic as deep as you do, including related links "
            "to the talk topic will help reviewers understand the proposal. "
            "Edit using "
            "<a href='http://daringfireball.net/projects/markdown/basics' "
            "target='_blank'>Markdown</a>."),
    )

    supplementary = models.TextField(
        verbose_name=_('supplementary'),
        blank=True,
        default='',
        help_text=_(
            "Anything else you'd like the program committee to know when "
            "making their selection: your past speaking experience, community "
            "experience, etc. This is not made public. Edit using "
            "<a href='http://daringfireball.net/projects/markdown/basics' "
            "target='_blank'>Markdown</a>."),
    )

    RECORDING_POLICY_CHOICES = ((True, _('Yes')), (False, _('No')))
    recording_policy = models.BooleanField(
        verbose_name=_('recording policy'),
        default=True,
        choices=RECORDING_POLICY_CHOICES,
        help_text=_(
            "Whether you agree to give permission to PyCon Taiwan to "
            "record, edit, and release audio and video of your presentation."),
    )

    slide_link = models.URLField(
        verbose_name=_('slide link'),
        blank=True,
        default='',
        help_text=_(
            "You can add your slide link near or after the conference day. "
            "Not required for review."),
    )

    created_at = models.DateTimeField(
        verbose_name=_('created at'),
        auto_now_add=True,
        db_index=True,
    )

    cancelled = models.BooleanField(
        verbose_name=_('cancelled'),
        default=False,
        db_index=True,
    )

    class Meta:
        abstract = True
        ordering = ['-created_at']

    def __str__(self):
        return self.title