Exemple #1
0
 def validate_amount(form, field):
     requested_refund_amount = field.data
     order = form.edit_parent
     if not order.paid_amount:
         raise StopValidation(__("Refunds can only be issued for paid orders"))
     if (order.refunded_amount + requested_refund_amount) > order.paid_amount:
         raise StopValidation(__("Invalid refund amount! Must be lesser than {amount}, the net amount paid for the order".format(amount=order.net_amount)))
Exemple #2
0
def available_seq(form, field):
	basequery = db.session.query(Category.id).filter(
		Category.item_collection == form.edit_parent, Category.seq == field.data)
	if form.edit_obj:
		basequery = basequery.filter(Category.id != form.edit_obj.id)

	if basequery.scalar() is not None:
		raise StopValidation(__("This sequence number has already been used. Please pick a different number"))
Exemple #3
0
def validate_gstin(form, field):
    """
    Raise a StopValidation exception if the supplied field's data is not a valid GSTIN.

    Checks if the data is:
    - 15 characters in length
    - First two characters form a valid short code for an Indian state
    - Contains a PAN (alphanumeric sub-string ranging from the 2nd to the 11th character)
    - Last character is an alphanumeric

    Reference: https://cleartax.in/s/know-your-gstin
    """
    # 15 length, first 2 digits, valid pan, checksum
    if len(field.data) != 15 or int(field.data[:2]) not in short_codes or not field.data[2:12].isalnum() or not field.data[-1].isalnum():
        raise forms.validators.StopValidation(__("This does not appear to be a valid GSTIN"))
Exemple #4
0
def proposal_label_form(project, proposal):
    """
    Returns a label form for the given project and proposal.
    """
    class ProposalLabelForm(forms.Form):
        pass

    for label in project.labels:
        if label.has_options and not label.archived and not label.restricted:
            setattr(ProposalLabelForm, label.name, forms.RadioField(
                label.form_label_text,
                description=label.description,
                validators=[forms.validators.DataRequired(__("Please select one"))] if label.required else [],
                choices=[(option.name, option.title) for option in label.options if not option.archived]
                ))

    return ProposalLabelForm(obj=proposal.formlabels if proposal else None, meta={'csrf': False})
class RenderProposalSubmittedNotification(RenderNotification):
    """Notify the proposer that their proposal has been submitted."""

    aliases = {'document': 'proposal'}
    emoji_prefix = "📤 "
    reason = __(
        "You are receiving this because you have submitted this proposal")

    def web(self):
        return render_template(
            'notifications/proposal_submitted_web.html.jinja2',
            view=self,
            proposal=self.proposal,
            project=self.proposal.project,
        )

    def email_subject(self):
        return self.emoji_prefix + _(
            "Proposal submitted to {project}: {proposal}").format(
                project=self.proposal.project.joined_title(),
                proposal=self.proposal.title)

    def email_content(self):
        return render_template(
            'notifications/proposal_submitted_email.html.jinja2',
            view=self,
            proposal=self.proposal,
            project=self.proposal.project,
        )

    def sms(self):
        return _("Your proposal has been submitted to {project} {url}").format(
            project=self.proposal.project.joined_title('>'),
            url=shortlink(
                self.proposal.url_for(_external=True,
                                      **self.tracking_tags('sms'))),
        )
Exemple #6
0
class EMPLOYER_RESPONSE(LabeledEnum):  # NOQA: N801
    #: New application
    NEW = (0, 'new', __("New"))
    #: Employer viewed on website
    PENDING = (1, 'pending', __("Pending"))
    #: Dismissed as not worth responding to
    IGNORED = (2, 'ignored', __("Ignored"))
    #: Employer replied to candidate
    REPLIED = (3, 'replied', __("Replied"))
    #: Employer reported a spammer
    FLAGGED = (4, 'flagged', __("Flagged"))
    #: Admin marked this as spam
    SPAM = (5, 'spam', __("Spam"))
    #: Employer rejected candidate with a message
    REJECTED = (6, 'rejected', __("Rejected"))

    __order__ = (NEW, PENDING, IGNORED, REPLIED, FLAGGED, SPAM, REJECTED)

    CAN_REPLY = {NEW, PENDING, IGNORED}
    CAN_REJECT = CAN_REPLY
    CAN_IGNORE = {NEW, PENDING}
    CAN_REPORT = {NEW, PENDING, IGNORED, REJECTED}
Exemple #7
0
class RenderOrganizationAdminMembershipNotification(RenderShared,
                                                    RenderNotification):
    """Notify organization admins of new admins and role changes."""

    aliases = {'document': 'organization', 'fragment': 'membership'}
    reason = __(
        "You are receiving this because you are an admin of this organization")

    fragments_order_by = [OrganizationMembership.granted_at.desc()]

    def activity_template(self, membership=None):
        """
        Returns a returns a Python string template with an appropriate message.

        Accepts an optional membership object for use in rollups.
        """
        if not membership:
            membership = self.membership
        for df in decision_factors:
            if df.match(
                    # LHS = user object, RHS = role proxy, so compare uuid
                    self.user_notification.user.uuid == membership.user.uuid,
                    self.record_type,
                    membership,
            ):
                return df.template

    def web(self):
        return render_template(
            'notifications/organization_membership_granted_web.html.jinja2',
            view=self)

    def email_content(self):
        return render_template(
            'notifications/organization_membership_granted_email.html.jinja2',
            view=self)
Exemple #8
0
def proposal_label_admin_form(project, proposal):
    """
    Returns a label form to use in admin panel for given project and proposal
    """
    class ProposalLabelAdminForm(forms.Form):
        pass

    for label in project.labels:
        if not label.archived and (label.restricted or not label.has_options):
            form_kwargs = {}
            if label.has_options:
                FieldType = forms.RadioField
                form_kwargs['choices'] = [(option.name, option.title) for option in label.options if not option.archived]
            else:
                FieldType = forms.BooleanField

            setattr(ProposalLabelAdminForm, label.name, FieldType(
                label.form_label_text,
                description=label.description,
                validators=[forms.validators.DataRequired(__("Please select one"))] if label.required else [],
                **form_kwargs
                ))

    return ProposalLabelAdminForm(obj=proposal.formlabels if proposal else None, meta={'csrf': False})
Exemple #9
0
class DiscountPolicyForm(forms.Form):
    title = forms.StringField(__("Discount title"),
                              validators=[
                                  forms.validators.DataRequired(
                                      __("Please specify a discount title")),
                                  forms.validators.Length(max=250)
                              ],
                              filters=[forms.filters.strip()])
    discount_type = forms.RadioField(__("Discount type"),
                                     choices=list(DISCOUNT_TYPE.items()),
                                     coerce=int,
                                     default=DISCOUNT_TYPE.COUPON)
    is_price_based = forms.RadioField(__("Price based discount"),
                                      coerce=getbool,
                                      default=1,
                                      choices=[
                                          (1, __("Special price discount")),
                                          (0, __("Percentage based discount"))
                                      ])
Exemple #10
0
class BuyerForm(forms.Form):
    email = forms.EmailField(__("Email"),
                             validators=[
                                 forms.validators.DataRequired(
                                     __("Please enter an email address")),
                                 forms.validators.Length(min=5, max=80)
                             ])
    fullname = forms.StringField(
        __("Full name"),
        validators=[
            forms.validators.DataRequired(
                __("Please enter the buyer's full name")),
            forms.validators.Length(max=80)
        ])
    phone = forms.StringField(__("Phone number"),
                              validators=[
                                  forms.validators.DataRequired(
                                      __("Please enter a phone number")),
                                  forms.validators.Length(max=16)
                              ])
Exemple #11
0
class VenueRoomForm(Form):
    title = wtforms.TextField(__("Name"),
                              description=__("Name of the room"),
                              validators=[
                                  wtforms.validators.Required(),
                                  wtforms.validators.length(max=250)
                              ])
    description = MarkdownField(
        __("Description"), description=__("An optional note about the room"))
    bgcolor = wtforms.TextField(
        __("Event Color"),
        validators=[
            wtforms.validators.Required(),
            wtforms.validators.length(max=6)
        ],
        description=__(
            "RGB Color for the event. Enter without the '#'. E.g. CCCCCC."),
        default=u"CCCCCC")

    def validate_bgcolor(self, field):
        if not valid_color_re.match(field.data):
            raise wtforms.ValidationError("Please enter a valid color code")
Exemple #12
0
class ResourceActionForm(forms.Form):
    """
    Edit an action associated with a resource
    """
    name = forms.StringField(__("Action name"), validators=[forms.validators.DataRequired()],
        description=__("Name of the action as a single word in lower case. "
        "This is provided by applications as part of the scope in the form "
        "'resource/action' when requesting access to a user's resources. "
        "Read actions are implicit when applications request just 'resource' "
        "in the scope and do not need to be specified as an explicit action"),
        widget_attrs={'autocorrect': 'none', 'autocapitalize': 'none'})
    title = forms.StringField(__("Title"), validators=[forms.validators.DataRequired()],
        description=__("Action title that is displayed to users"))
    description = forms.TextAreaField(__("Description"),
        description=__("An optional description of what the action is"))

    def validate_name(self, field):
        field.data = field.data.lower()
        if not valid_username(field.data):
            raise forms.ValidationError(_("Name contains invalid characters"))

        existing = self.edit_resource.get_action(field.data)
        if existing and existing.id != self.edit_id:
            raise forms.ValidationError(_("An action with that name already exists for this resource"))
Exemple #13
0
class INVOICE_STATUS(LabeledEnum):  # NOQA: N801
    DRAFT = (0, __("Draft"))
    FINAL = (1, __("Final"))
Exemple #14
0
class PROPOSALSTATUS(LabeledEnum):
    # Draft-state for future use, so people can save their proposals and submit only when ready
    DRAFT = (0, __("Draft"))
    SUBMITTED = (1, __("Submitted"))
    CONFIRMED = (2, __("Confirmed"))
    WAITLISTED = (3, __("Waitlisted"))
    SHORTLISTED = (4, __("Shortlisted"))
    REJECTED = (5, __("Rejected"))
    CANCELLED = (6, __("Cancelled"))

    AWAITING_DETAILS = (7, __("Awaiting details"))
    UNDER_EVALUATION = (8, __("Under evaluation"))
    SHORTLISTED_FOR_REHEARSAL = (9, __("Shortlisted for rehearsal"))
    REHEARSAL = (10, __("Rehearsal ongoing"))
Exemple #15
0
def get_currency_choices():
    choices = [('', __('None'))]
    choices.extend(CURRENCY.items())
    return choices
Exemple #16
0
def validate_json(form, field):
    try:
        json.loads(field.data)
    except ValueError:
        raise forms.validators.StopValidation(__("Invalid JSON"))
Exemple #17
0
class CAMPAIGN_STATE(LabeledEnum):  # NOQA: N801
    DISABLED = (False, 'disabled', __("Disabled"))
    ENABLED = (True, 'enabled', __("Enabled"))
Exemple #18
0
class UserFlags(object):
    """
    A collection of bi-directional flags to (a) set on a user or (b) find matching users.
    This is a convenience class, meant only to be an easy way to look up contents.
    """

    # Is a new user (under a day, a month)

    is_new_since_day = UserFlag(
        'user',
        __("Is a new user (joined <= a day ago)"),
        lambda user: user.created_at >= utcnow() - newlimit,
        lambda: db.session.query(User.id).filter(
            User.created_at >= utcnow() - newlimit
        ),
    )

    is_new_since_month = UserFlag(
        'user',
        __("Is a new user (joined <= a month ago)"),
        lambda user: user.created_at >= utcnow() - agelimit,
        lambda: db.session.query(User.id).filter(
            User.created_at >= utcnow() - agelimit
        ),
    )

    is_not_new = UserFlag(
        'user',
        __("Is not a new user (joined > a month ago)"),
        lambda user: user.created_at < utcnow() - agelimit,
        lambda: db.session.query(User.id).filter(User.created_at < utcnow() - agelimit),
    )

    # Is a candidate (all time, new, recent, past)

    is_candidate_alltime = UserFlag(
        'candidate',
        __("Is a candidate (applied at any time)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')),
    )

    is_candidate_day = UserFlag(
        'candidate',
        __("Is a candidate (applied <= a day ago)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user,
            JobApplication.created_at >= utcnow() - newlimit,
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.created_at >= utcnow() - newlimit
        ),
    )

    is_candidate_month = UserFlag(
        'candidate',
        __("Is a candidate (applied <= a month ago)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user,
            JobApplication.created_at >= utcnow() - agelimit,
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.created_at >= utcnow() - agelimit
        ),
    )

    is_candidate_past = UserFlag(
        'candidate',
        __("Is a candidate (applied > a month ago)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user, JobApplication.created_at < utcnow() - agelimit
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.created_at < utcnow() - agelimit
        ),
    )

    # Is a candidate who got a response from an employer

    has_jobapplication_response_alltime = UserFlag(
        'candidate',
        __("Is a candidate who received a response (at any time)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user, JobApplication.response.REPLIED
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.response.REPLIED
        ),
    )

    has_jobapplication_response_day = UserFlag(
        'candidate',
        __("Is a candidate who received a response (in <= a day)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user,
            JobApplication.replied_at >= utcnow() - newlimit,
            JobApplication.response.REPLIED,
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.response.REPLIED,
            JobApplication.replied_at >= utcnow() - newlimit,
        ),
    )

    has_jobapplication_response_month = UserFlag(
        'candidate',
        __("Is a candidate who received a response (in <= a month)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user,
            JobApplication.replied_at >= utcnow() - agelimit,
            JobApplication.response.REPLIED,
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.response.REPLIED,
            JobApplication.replied_at >= utcnow() - agelimit,
        ),
    )

    has_jobapplication_response_past = UserFlag(
        'candidate',
        __("Is a candidate who received a response (in > a month)"),
        lambda user: JobApplication.query.filter(
            JobApplication.user == user,
            JobApplication.replied_at < utcnow() - agelimit,
            JobApplication.response.REPLIED,
        ).notempty(),
        lambda: db.session.query(distinct(JobApplication.user_id).label('id')).filter(
            JobApplication.response.REPLIED,
            JobApplication.replied_at < utcnow() - agelimit,
        ),
    )

    # Is an employer (not including collaborators) (all time, new, recent, past)

    is_employer_alltime = UserFlag(
        'employer',
        __("Is an employer (posted at any time)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user, ~(JobPost.state.UNPUBLISHED)
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            ~(JobPost.state.UNPUBLISHED)
        ),
    )

    is_employer_day = UserFlag(
        'employer',
        __("Is an employer (posted <= a day ago)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user, JobPost.state.NEW, ~(JobPost.state.UNPUBLISHED)
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            JobPost.state.NEW, ~(JobPost.state.UNPUBLISHED)
        ),
    )

    is_employer_month = UserFlag(
        'employer',
        __("Is an employer (posted <= a month ago)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user, JobPost.state.LISTED, ~(JobPost.state.UNPUBLISHED)
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            JobPost.state.LISTED, ~(JobPost.state.UNPUBLISHED)
        ),
    )

    is_employer_past = UserFlag(
        'employer',
        __("Is an employer (posted > a month ago)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user,
            JobPost.datetime < utcnow() - agelimit,
            ~(JobPost.state.UNPUBLISHED),
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            JobPost.datetime < utcnow() - agelimit, ~(JobPost.state.UNPUBLISHED)
        ),
    )

    # Employer who didn't confirm a listing

    has_jobpost_unconfirmed_alltime = UserFlag(
        'employer',
        __("Is an employer who did not confirm a post (at any time)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user, JobPost.state.UNPUBLISHED
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            ~(JobPost.state.UNPUBLISHED)
        ),
    )

    has_jobpost_unconfirmed_day = UserFlag(
        'employer',
        __("Is an employer who did not confirm a post (posted <= a day ago)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user, JobPost.state.NEW, JobPost.state.UNPUBLISHED
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            JobPost.state.NEW, ~(JobPost.state.UNPUBLISHED)
        ),
    )

    has_jobpost_unconfirmed_month = UserFlag(
        'employer',
        __("Is an employer who did not confirm a post (posted <= a month ago)"),
        lambda user: JobPost.query.filter(
            JobPost.user == user, JobPost.state.LISTED, JobPost.state.UNPUBLISHED
        ).notempty(),
        lambda: db.session.query(db.distinct(JobPost.user_id).label('id')).filter(
            JobPost.state.LISTED, ~(JobPost.state.UNPUBLISHED)
        ),
    )

    # Employer who responded to a candidate

    has_responded_candidate_alltime = UserFlag(
        'candidate',
        __("Is an employer who responded to a candidate (at any time)"),
        lambda user: JobApplication.query.filter(
            JobApplication.replied_by == user, JobApplication.response.REPLIED
        ).notempty(),
        lambda: db.session.query(
            distinct(JobApplication.replied_by_id).label('id')
        ).filter(JobApplication.response.REPLIED),
    )

    has_responded_candidate_day = UserFlag(
        'candidate',
        __("Is an employer who responded to a candidate (in <= a day)"),
        lambda user: JobApplication.query.filter(
            JobApplication.replied_by == user,
            JobApplication.replied_at >= utcnow() - newlimit,
            JobApplication.response.REPLIED,
        ).notempty(),
        lambda: db.session.query(
            distinct(JobApplication.replied_by_id).label('id')
        ).filter(
            JobApplication.response.REPLIED,
            JobApplication.replied_at >= utcnow() - newlimit,
        ),
    )

    has_responded_candidate_month = UserFlag(
        'candidate',
        __("Is an employer who responded to a candidate (in <= a month)"),
        lambda user: JobApplication.query.filter(
            JobApplication.replied_by == user,
            JobApplication.replied_at >= utcnow() - agelimit,
            JobApplication.response.REPLIED,
        ).notempty(),
        lambda: db.session.query(
            distinct(JobApplication.replied_by_id).label('id')
        ).filter(
            JobApplication.response.REPLIED,
            JobApplication.replied_at >= utcnow() - agelimit,
        ),
    )

    has_responded_candidate_past = UserFlag(
        'candidate',
        __("Is an employer who responded to a candidate (in > a month)"),
        lambda user: JobApplication.query.filter(
            JobApplication.replied_by == user,
            JobApplication.replied_at < utcnow() - agelimit,
            JobApplication.response.REPLIED,
        ).notempty(),
        lambda: db.session.query(
            distinct(JobApplication.replied_by_id).label('id')
        ).filter(
            JobApplication.response.REPLIED,
            JobApplication.replied_at < utcnow() - agelimit,
        ),
    )

    # Account created in <= a day
    is_new_lurker_within_day = UserFlag(
        'lurker',
        __("Is a lurker (joined <= a day ago)"),
        lambda user: user.created_at >= utcnow() - newlimit
        and (not JobPost.query.filter(JobPost.user == user).notempty())
        or (not JobApplication.query.filter(JobApplication.user == user).notempty())
        or (
            not JobApplication.query.filter(
                JobApplication.replied_by == user
            ).notempty()
        ),
        lambda: db.session.query(User.id).filter(
            User.created_at >= utcnow() - newlimit,
            ~User.id.in_(
                db.session.query(JobApplication.user_id).filter(
                    JobApplication.user_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobApplication.replied_by_id).filter(
                    JobApplication.replied_by_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobPost.user_id).filter(JobPost.user_id.isnot(None))
            ),
        ),
    )

    # Account created in <= a month
    is_new_lurker_within_month = UserFlag(
        'lurker',
        __("Is a lurker (joined <= a month ago)"),
        lambda user: user.created_at >= utcnow() - agelimit
        and (not JobPost.query.filter(JobPost.user == user).notempty())
        or (not JobApplication.query.filter(JobApplication.user == user).notempty())
        or (
            not JobApplication.query.filter(
                JobApplication.replied_by == user
            ).notempty()
        ),
        lambda: db.session.query(User.id).filter(
            User.created_at >= utcnow() - agelimit,
            ~User.id.in_(
                db.session.query(JobApplication.user_id).filter(
                    JobApplication.user_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobApplication.replied_by_id).filter(
                    JobApplication.replied_by_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobPost.user_id).filter(JobPost.user_id.isnot(None))
            ),
        ),
    )

    # Account created > a month ago
    is_lurker_since_past = UserFlag(
        'lurker',
        __("Is a lurker (joined > a month ago)"),
        lambda user: user.created_at < utcnow() - agelimit
        and (not JobPost.query.filter(JobPost.user == user).notempty())
        or (not JobApplication.query.filter(JobApplication.user == user).notempty())
        or (
            not JobApplication.query.filter(
                JobApplication.replied_by == user
            ).notempty()
        ),
        lambda: db.session.query(User.id).filter(
            User.created_at < utcnow() - agelimit,
            ~User.id.in_(
                db.session.query(JobApplication.user_id).filter(
                    JobApplication.user_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobApplication.replied_by_id).filter(
                    JobApplication.replied_by_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobPost.user_id).filter(JobPost.user_id.isnot(None))
            ),
        ),  # NOQA
    )

    # Has always been a lurker
    is_lurker_since_alltime = UserFlag(
        'lurker',
        __("Is a lurker"),
        lambda user: (not JobPost.query.filter(JobPost.user == user).notempty())
        or (not JobApplication.query.filter(JobApplication.user == user).notempty())
        or (
            not JobApplication.query.filter(
                JobApplication.replied_by == user
            ).notempty()
        ),
        lambda: db.session.query(User.id).filter(
            ~User.id.in_(
                db.session.query(JobApplication.user_id).filter(
                    JobApplication.user_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobApplication.replied_by_id).filter(
                    JobApplication.replied_by_id.isnot(None)
                )
            ),
            ~User.id.in_(
                db.session.query(JobPost.user_id).filter(JobPost.user_id.isnot(None))
            ),
        ),
    )

    # Has been lurking for a day+
    is_inactive_since_day = UserFlag(
        'lurker',
        __("Is inactive (for a day+)"),
        lambda user: (
            not JobPost.query.filter(
                JobPost.user == user, JobPost.created_at >= utcnow() - newlimit
            ).notempty()
        )
        or (
            not JobApplication.query.filter(
                JobApplication.user == user,
                JobApplication.created_at >= utcnow() - newlimit,
            ).notempty()
        )
        or (
            not JobApplication.query.filter(
                JobApplication.replied_by == user,
                JobApplication.replied_at >= utcnow() - newlimit,
            ).notempty()
        ),
        lambda: db.session.query(User.id).filter(
            ~User.id.in_(
                db.session.query(JobApplication.user_id).filter(
                    JobApplication.user_id.isnot(None),
                    JobApplication.created_at >= utcnow() - newlimit,
                )
            ),
            ~User.id.in_(
                db.session.query(JobApplication.replied_by_id).filter(
                    JobApplication.replied_by_id.isnot(None),
                    JobApplication.replied_at >= utcnow() - newlimit,
                )
            ),
            ~User.id.in_(
                db.session.query(JobPost.user_id).filter(
                    JobPost.user_id.isnot(None),
                    JobPost.created_at >= utcnow() - newlimit,
                )
            ),
        ),
    )

    # Has been lurking for a month+
    is_inactive_since_month = UserFlag(
        'lurker',
        __("Is inactive (for a month+)"),
        lambda user: (
            not JobPost.query.filter(
                JobPost.user == user, JobPost.created_at >= utcnow() - agelimit
            ).notempty()
        )
        or (
            not JobApplication.query.filter(
                JobApplication.user == user,
                JobApplication.created_at >= utcnow() - agelimit,
            ).notempty()
        )
        or (
            not JobApplication.query.filter(
                JobApplication.replied_by == user,
                JobApplication.replied_at >= utcnow() - agelimit,
            ).notempty()
        ),
        lambda: db.session.query(User.id).filter(
            ~User.id.in_(
                db.session.query(JobApplication.user_id).filter(
                    JobApplication.user_id.isnot(None),
                    JobApplication.created_at >= utcnow() - agelimit,
                )
            ),
            ~User.id.in_(
                db.session.query(JobApplication.replied_by_id).filter(
                    JobApplication.replied_by_id.isnot(None),
                    JobApplication.replied_at >= utcnow() - agelimit,
                )
            ),
            ~User.id.in_(
                db.session.query(JobPost.user_id).filter(
                    JobPost.user_id.isnot(None),
                    JobPost.created_at >= utcnow() - newlimit,
                )
            ),
        ),
    )

    # One-way flags (no elegant way to do a reverse query)

    has_boards = UserFlag(
        'admin',
        __("Has a sub-board"),
        lambda user: Board.query.filter(
            Board.userid.in_(user.allowner_ids())
        ).notempty(),
        None,
    )
Exemple #19
0
class JobPost(BaseMixin, db.Model):
    __tablename__ = 'jobpost'

    # Metadata
    user_id = db.Column(None,
                        db.ForeignKey('user.id'),
                        nullable=True,
                        index=True)
    user = db.relationship(User,
                           primaryjoin=user_id == User.id,
                           backref=db.backref('jobposts', lazy='dynamic'))

    hashid = db.Column(db.String(5), nullable=False, unique=True)
    datetime = db.Column(db.DateTime,
                         default=db.func.utcnow(),
                         nullable=False,
                         index=True)  # Published
    closed_datetime = db.Column(db.DateTime,
                                nullable=True)  # If withdrawn or rejected
    # Pinned on the home page. Boards use the BoardJobPost.pinned column
    sticky = db.Column(db.Boolean, nullable=False, default=False)
    pinned = db.synonym('sticky')

    # Job description
    headline = db.Column(db.Unicode(100), nullable=False)
    headlineb = db.Column(db.Unicode(100), nullable=True)
    type_id = db.Column(None, db.ForeignKey('jobtype.id'), nullable=False)
    type = db.relation(JobType, primaryjoin=type_id == JobType.id)
    category_id = db.Column(None,
                            db.ForeignKey('jobcategory.id'),
                            nullable=False)
    category = db.relation(JobCategory,
                           primaryjoin=category_id == JobCategory.id)
    location = db.Column(db.Unicode(80), nullable=False)
    parsed_location = db.Column(JsonDict)
    # remote_location tracks whether the job is work-from-home/work-from-anywhere
    remote_location = db.Column(db.Boolean, default=False, nullable=False)
    relocation_assist = db.Column(db.Boolean, default=False, nullable=False)
    description = db.Column(db.UnicodeText, nullable=False)
    perks = db.Column(db.UnicodeText, nullable=False)
    how_to_apply = db.Column(db.UnicodeText, nullable=False)
    hr_contact = db.Column(db.Boolean, nullable=True)

    # Compensation details
    pay_type = db.Column(db.SmallInteger,
                         nullable=True)  # Value in models.PAY_TYPE
    pay_currency = db.Column(db.CHAR(3), nullable=True)
    pay_cash_min = db.Column(db.Integer, nullable=True)
    pay_cash_max = db.Column(db.Integer, nullable=True)
    pay_equity_min = db.Column(db.Numeric, nullable=True)
    pay_equity_max = db.Column(db.Numeric, nullable=True)

    # Company details
    company_name = db.Column(db.Unicode(80), nullable=False)
    company_logo = db.Column(db.Unicode(255), nullable=True)
    company_url = db.Column(db.Unicode(255), nullable=False, default=u'')
    twitter = db.Column(db.Unicode(15), nullable=True)
    fullname = db.Column(
        db.Unicode(80),
        nullable=True)  # Deprecated field, used before user_id was introduced
    email = db.Column(db.Unicode(80), nullable=False)
    email_domain = db.Column(db.Unicode(80), nullable=False, index=True)
    domain_id = db.Column(None, db.ForeignKey('domain.id'), nullable=False)
    md5sum = db.Column(db.String(32), nullable=False, index=True)

    # Payment, audit and workflow fields
    words = db.Column(
        db.UnicodeText,
        nullable=True)  # All words in description, perks and how_to_apply
    promocode = db.Column(db.String(40), nullable=True)
    ipaddr = db.Column(db.String(45), nullable=False)
    useragent = db.Column(db.Unicode(250), nullable=True)
    edit_key = db.Column(db.String(40),
                         nullable=False,
                         default=random_long_key)
    email_verify_key = db.Column(db.String(40),
                                 nullable=False,
                                 default=random_long_key)
    email_sent = db.Column(db.Boolean, nullable=False, default=False)
    email_verified = db.Column(db.Boolean, nullable=False, default=False)
    payment_value = db.Column(db.Integer, nullable=False, default=0)
    payment_received = db.Column(db.Boolean, nullable=False, default=False)
    reviewer_id = db.Column(None,
                            db.ForeignKey('user.id'),
                            nullable=True,
                            index=True)
    reviewer = db.relationship(User,
                               primaryjoin=reviewer_id == User.id,
                               backref="reviewed_posts")
    review_datetime = db.Column(db.DateTime, nullable=True)
    review_comments = db.Column(db.Unicode(250), nullable=True)

    search_vector = deferred(db.Column(TSVECTOR, nullable=True))

    _state = db.Column('status',
                       db.Integer,
                       StateManager.check_constraint('status', POST_STATE),
                       default=POST_STATE.DRAFT,
                       nullable=False)
    state = StateManager('_state',
                         POST_STATE,
                         doc="Current state of the job post")

    # Metadata for classification
    language = db.Column(db.CHAR(2), nullable=True)
    language_confidence = db.Column(db.Float, nullable=True)

    admins = db.relationship(User,
                             lazy='dynamic',
                             secondary=lambda: jobpost_admin_table,
                             backref=db.backref('admin_of', lazy='dynamic'))
    starred_by = db.relationship(User,
                                 lazy='dynamic',
                                 secondary=starred_job_table,
                                 backref=db.backref('starred_jobs',
                                                    lazy='dynamic'))
    #: Quick lookup locations this post is referring to
    geonameids = association_proxy('locations',
                                   'geonameid',
                                   creator=lambda l: JobLocation(geonameid=l))

    _defercols = [
        defer('user_id'),
        defer('closed_datetime'),
        defer('parsed_location'),
        defer('relocation_assist'),
        defer('description'),
        defer('perks'),
        defer('how_to_apply'),
        defer('hr_contact'),
        defer('company_logo'),
        defer('company_url'),
        defer('fullname'),
        defer('email'),
        defer('words'),
        defer('promocode'),
        defer('ipaddr'),
        defer('useragent'),
        defer('edit_key'),
        defer('email_verify_key'),
        defer('email_sent'),
        defer('email_verified'),
        defer('payment_value'),
        defer('payment_received'),
        defer('reviewer_id'),
        defer('review_datetime'),
        defer('review_comments'),
        defer('language'),
        defer('language_confidence'),

        # These are defined below JobApplication
        defer('new_applications'),
        defer('replied_applications'),
        defer('viewcounts_viewed'),
        defer('viewcounts_opened'),
        defer('viewcounts_applied'),

        # defer('pay_type'),
        # defer('pay_currency'),
        # defer('pay_cash_min'),
        # defer('pay_cash_max'),
        # defer('pay_equity_min'),
        # defer('pay_equity_max'),
    ]

    @classmethod
    def get(cls, hashid):
        return cls.query.filter_by(hashid=hashid).one_or_none()

    @classmethod
    def fetch(cls, hashid):
        """Returns a SQLAlchemy query object for JobPost"""
        return cls.query.filter_by(hashid=hashid).options(
            load_only('id', 'headline', 'headlineb', 'hashid', 'datetime',
                      '_state', 'email_domain', 'review_comments',
                      'company_url'))

    @classmethodproperty
    def query_listed(cls):
        """Returns a SQLAlchemy query for listed jobposts"""
        return cls.query.filter(JobPost.state.LISTED).options(
            db.load_only('id', 'hashid'))

    def __repr__(self):
        return '<JobPost {hashid} "{headline}">'.format(
            hashid=self.hashid, headline=self.headline.encode('utf-8'))

    def admin_is(self, user):
        if user is None:
            return False
        return user == self.user or bool(
            self.admins.options(
                db.load_only('id')).filter_by(id=user.id).count())

    @property
    def expiry_date(self):
        return self.datetime + agelimit

    @property
    def after_expiry_date(self):
        return self.expiry_date + timedelta(days=1)

    # NEW = Posted within last 24 hours
    state.add_conditional_state(
        'NEW',
        state.PUBLIC,
        lambda jobpost: jobpost.datetime >= datetime.utcnow() - newlimit,
        label=('new', __("New!")))
    # LISTED = Posted within last 30 days
    state.add_conditional_state(
        'LISTED',
        state.PUBLIC,
        lambda jobpost: jobpost.datetime >= datetime.utcnow() - agelimit,
        label=('listed', __("Listed")))
    # OLD = Posted more than 30 days ago
    state.add_conditional_state('OLD',
                                state.PUBLIC,
                                lambda jobpost: not jobpost.state.LISTED,
                                label=('old', __("Old")))
    # Checks if current user has the permission to confirm the jobpost
    state.add_conditional_state(
        'CONFIRMABLE',
        state.UNPUBLISHED,
        lambda jobpost: jobpost.current_permissions.edit,
        label=('confirmable', __("Confirmable")))

    @state.transition(state.PUBLIC,
                      state.WITHDRAWN,
                      title=__("Withdraw"),
                      message=__("This job post has been withdrawn"),
                      type='danger')
    def withdraw(self):
        self.closed_datetime = db.func.utcnow()

    @state.transition(state.PUBLIC,
                      state.CLOSED,
                      title=__("Close"),
                      message=__("This job post has been closed"),
                      type='danger')
    def close(self):
        self.closed_datetime = db.func.utcnow()

    @state.transition(state.UNPUBLISHED_OR_MODERATED,
                      state.CONFIRMED,
                      title=__("Confirm"),
                      message=__("This job post has been confirmed"),
                      type='success')
    def confirm(self):
        self.email_verified = True
        self.datetime = db.func.utcnow()

    @state.transition(state.CLOSED,
                      state.CONFIRMED,
                      title=__("Reopen"),
                      message=__("This job post has been reopened"),
                      type='success')
    def reopen(self):
        pass

    @state.transition(state.PUBLIC,
                      state.SPAM,
                      title=__("Mark as spam"),
                      message=__("This job post has been marked as spam"),
                      type='danger')
    def mark_spam(self, reason, user):
        self.closed_datetime = db.func.utcnow()
        self.review_datetime = db.func.utcnow()
        self.review_comments = reason
        self.reviewer = user

    @state.transition(
        state.DRAFT,
        state.PENDING,
        title=__("Mark as pending"),
        message=__("This job post is awaiting email verification"),
        type='danger')
    def mark_pending(self):
        pass

    @state.transition(state.PUBLIC,
                      state.REJECTED,
                      title=__("Reject"),
                      message=__("This job post has been rejected"),
                      type='danger')
    def reject(self, reason, user):
        self.closed_datetime = db.func.utcnow()
        self.review_datetime = db.func.utcnow()
        self.review_comments = reason
        self.reviewer = user

    @state.transition(state.PUBLIC,
                      state.MODERATED,
                      title=__("Moderate"),
                      message=__("This job post has been moderated"),
                      type='primary')
    def moderate(self, reason, user):
        self.closed_datetime = db.func.utcnow()
        self.review_datetime = db.func.utcnow()
        self.review_comments = reason
        self.reviewer = user

    def url_for(self, action='view', _external=False, **kwargs):
        if self.state.UNPUBLISHED and action in ('view', 'edit'):
            domain = None
        else:
            domain = self.email_domain

        # A/B test flag for permalinks
        if 'b' in kwargs:
            if kwargs['b'] is not None:
                kwargs['b'] = unicode(int(kwargs['b']))
            else:
                kwargs.pop('b')

        if action == 'view':
            return url_for('jobdetail',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'reveal':
            return url_for('revealjob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'apply':
            return url_for('applyjob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'edit':
            return url_for('editjob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'withdraw':
            return url_for('withdraw',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'close':
            return url_for('close',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'viewstats':
            return url_for('job_viewstats',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'related_posts':
            return url_for('job_related_posts',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'reopen':
            return url_for('reopen',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'moderate':
            return url_for('moderatejob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'pin':
            return url_for('pinnedjob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'reject':
            return url_for('rejectjob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'confirm':
            return url_for('confirm',
                           hashid=self.hashid,
                           _external=_external,
                           **kwargs)
        elif action == 'logo':
            return url_for('logoimage',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'confirm-link':
            return url_for('confirm_email',
                           hashid=self.hashid,
                           domain=domain,
                           key=self.email_verify_key,
                           _external=True,
                           **kwargs)
        elif action == 'star':
            return url_for('starjob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'manage':
            return url_for('managejob',
                           hashid=self.hashid,
                           domain=domain,
                           _external=_external,
                           **kwargs)
        elif action == 'browse':
            if is_public_email_domain(self.email_domain, default=False):
                return url_for('browse_by_email',
                               md5sum=self.md5sum,
                               _external=_external,
                               **kwargs)
            else:
                return url_for('browse_by_domain',
                               domain=self.email_domain,
                               _external=_external,
                               **kwargs)

    def permissions(self, user, inherited=None):
        perms = super(JobPost, self).permissions(user, inherited)
        if self.state.PUBLIC:
            perms.add('view')
        if self.admin_is(user):
            if self.state.UNPUBLISHED:
                perms.add('view')
            perms.add('edit')
            perms.add('manage')
            perms.add('withdraw')
        return perms

    @property
    def from_webmail_domain(self):
        return is_public_email_domain(self.email_domain, default=False)

    @property
    def company_url_domain_zone(self):
        if not self.company_url:
            return u''
        else:
            r = tldextract.extract(self.company_url)
            return u'.'.join([r.domain, r.suffix])

    @property
    def pays_cash(self):
        if self.pay_type is None:  # Legacy record from before `pay_type` was mandatory
            return True
        return self.pay_type != PAY_TYPE.NOCASH and self.pay_cash_min is not None and self.pay_cash_max is not None

    @property
    def pays_equity(self):
        return self.pay_equity_min is not None and self.pay_equity_max is not None

    def pay_label(self):
        if self.pay_type is None:
            return u"NA"
        elif self.pay_type == PAY_TYPE.NOCASH:
            cash = None
            suffix = ""
        else:
            if self.pay_type == PAY_TYPE.RECURRING:
                suffix = "pa"
            else:
                suffix = ""

            indian = False
            if self.pay_currency == "INR":
                indian = True
                symbol = u"₹"
            elif self.pay_currency == "USD":
                symbol = u"$"
            elif self.pay_currency == "EUR":
                symbol = u"€"
            elif self.pay_currency == "GBP":
                symbol = u"£"
            else:
                symbol = u"¤"

            if self.pay_cash_min == self.pay_cash_max:
                cash = symbol + number_abbreviate(self.pay_cash_min, indian)
            else:
                cash = symbol + number_abbreviate(
                    self.pay_cash_min, indian) + "-" + number_abbreviate(
                        self.pay_cash_max, indian)

            if suffix:
                cash = cash + " " + suffix

        if self.pays_equity:
            if self.pay_equity_min == self.pay_equity_max:
                equity = str(self.pay_equity_min) + "%"
            else:
                equity = str(self.pay_equity_min) + "-" + str(
                    self.pay_equity_max) + "%"
        else:
            equity = None

        if cash:
            if equity:
                return ", ".join([cash, equity])
            else:
                return cash
        else:
            if equity:
                return equity
            else:
                return "No pay"

    def tag_content(self):
        return Markup('\n').join(
            (Markup('<div>') + Markup(escape(self.headline)) +
             Markup('</div>'),
             Markup('<div>') + Markup(self.description) + Markup('</div>'),
             Markup('<div>') + Markup(self.perks) + Markup('</div>')))

    @staticmethod
    def viewcounts_key(jobpost_id):
        if isinstance(jobpost_id, (list, tuple)):
            return ['hasjob/viewcounts/%d' % post_id for post_id in jobpost_id]
        return 'hasjob/viewcounts/%d' % jobpost_id

    def uncache_viewcounts(self, key=None):
        cache_key = JobPost.viewcounts_key(self.id)
        if not key:
            redis_store.delete(cache_key)
        else:
            redis_store.hdel(cache_key, key)

    @cached_property
    def ab_impressions(self):
        results = {'NA': 0, 'A': 0, 'B': 0}
        counts = db.session.query(JobImpression.bgroup.label('bgroup'),
                                  db.func.count('*').label('count')).filter(
                                      JobImpression.jobpost == self).group_by(
                                          JobImpression.bgroup)
        for row in counts:
            if row.bgroup is False:
                results['A'] = row.count
            elif row.bgroup is True:
                results['B'] = row.count
            else:
                results['NA'] = row.count
        return results

    @cached_property
    def ab_views(self):
        results = {
            'C_NA': 0,
            'C_A': 0,
            'C_B': 0,  # Conversions (cointoss=True, crosstoss=False)
            'E_NA': 0,
            'E_A': 0,
            'E_B':
            0,  # External (cointoss=False, crosstoss=True OR False [do sum])
            'X_NA': 0,
            'X_A': 0,
            'X_B': 0,  # Cross toss (cointoss=True, crosstoss=True)
        }
        counts = db.session.query(JobViewSession.bgroup.label('bgroup'),
                                  JobViewSession.cointoss.label('cointoss'),
                                  JobViewSession.crosstoss.label('crosstoss'),
                                  db.func.count('*').label('count')).filter(
                                      JobViewSession.jobpost == self).group_by(
                                          JobViewSession.bgroup,
                                          JobViewSession.cointoss,
                                          JobViewSession.crosstoss)

        for row in counts:
            if row.cointoss is True and row.crosstoss is False:
                prefix = 'C'
            elif row.cointoss is False:
                prefix = 'E'
            elif row.cointoss is True and row.crosstoss is True:
                prefix = 'X'
            if row.bgroup is False:
                results[prefix + '_A'] += row.count
            elif row.bgroup is True:
                results[prefix + '_B'] += row.count
            else:
                results[prefix + '_NA'] += row.count
        return results

    @property
    def sort_score(self):
        """
        Sort with a gravity of 1.8 using the HackerNews algorithm
        """
        viewcounts = self.viewcounts
        opened = int(viewcounts['opened'])
        applied = int(viewcounts['applied'])
        age = datetime.utcnow() - self.datetime
        hours = age.days * 24 + age.seconds / 3600

        return ((applied * 3) + (opened - applied)) / pow((hours + 2), 1.8)

    @cached_property  # For multiple accesses in a single request
    def viewstats(self):
        now = datetime.utcnow()
        delta = now - self.datetime
        hourly_stat_limit = 2  # days
        if delta.days < hourly_stat_limit:  # Less than {limit} days
            return 'h', viewstats_by_id_hour(self.id, hourly_stat_limit * 24)
        else:
            return 'd', viewstats_by_id_day(self.id, 30)

    def reports(self):
        if not self.flags:
            return []
        counts = {}
        for flag in self.flags:
            counts[flag.reportcode] = counts.setdefault(flag.reportcode, 0) + 1
        return [{
            'count': i[2],
            'title': i[1]
        } for i in sorted([(k.seq, k.title, v) for k, v in counts.items()])]
Exemple #20
0
 def validate_date_upto(self, field):
     if field.data and field.data.year < 1900:
         raise forms.ValidationError(__("Please enter a year after 1900"))
Exemple #21
0
            return render_redirect(url_for('.reset', expired=1, username=loginform.username.data))
    elif request.method == 'POST' and formid in service_forms:
        form = service_forms[formid]['form']
        if form.validate():
            return set_loginmethod_cookie(login_registry[formid].do(form=form), formid)
    elif request.method == 'POST':
        abort(500)
    iframe_block = {'X-Frame-Options': 'SAMEORIGIN'}
    if request.is_xhr and formid == 'passwordlogin':
        return render_template('loginform.html.jinja2', loginform=loginform, Markup=Markup), 200, iframe_block
    else:
        return render_template('login.html.jinja2', loginform=loginform, lastused=loginmethod,
            service_forms=service_forms, Markup=Markup, login_registry=login_registry), 200, iframe_block


logout_errormsg = __("We detected a possibly unauthorized attempt to log you out. "
    "If you really did intend to logout, please click on the logout link again")


def logout_user():
    """
    User-initiated logout
    """
    if not request.referrer or (urlparse.urlsplit(request.referrer).netloc != urlparse.urlsplit(request.url).netloc):
        # TODO: present a logout form
        flash(current_app.config.get('LOGOUT_UNAUTHORIZED_MESSAGE') or logout_errormsg, 'danger')
        return redirect(url_for('index'))
    else:
        logout_internal()
        db.session.commit()
        flash(_("You are now logged out"), category='info')
        return redirect(get_next_url())
Exemple #22
0
 def validate_end_at(self, field):
     if field.data <= self.start_at.data:
         raise forms.ValidationError(__(u"The campaign can’t end before it starts"))
Exemple #23
0
    if g.user:
        token = client.authtoken_for(g.user)
    else:
        token = None
    response = jsonify({
        'hastoken': True if token else False
        })
    response.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
    response.headers['Cache-Control'] = 'private, max-age=300'
    return response


# --- Token-based resource endpoints ------------------------------------------

@lastuser_oauth.route('/api/1/id')
@resource_registry.resource('id', __(u"Read your name and basic profile data"))
def resource_id(authtoken, args, files=None):
    """
    Return user's id
    """
    if 'all' in args and getbool(args['all']):
        return get_userinfo(authtoken.user, authtoken.client, scope=authtoken.scope, get_permissions=True)
    else:
        return get_userinfo(authtoken.user, authtoken.client, scope=['id'], get_permissions=False)


@csrf.exempt
@lastuser_oauth.route('/api/1/session/verify', methods=['POST'])
@resource_registry.resource('session/verify', __(u"Verify user session"), scope='id')
def session_verify(authtoken, args, files=None):
    sessionid = args['sessionid']
Exemple #24
0
class DISCOUNT_TYPE(LabeledEnum):  # NOQA: N801
    AUTOMATIC = (0, __("Automatic"))
    COUPON = (1, __("Coupon"))
Exemple #25
0
class MEMBERSHIP_RECORD_TYPE(LabeledEnum):  # NOQA: N801
    INVITE = (0, 'invite', __("Invite"))
    ACCEPT = (1, 'accept', __("Accept"))
    DIRECT_ADD = (2, 'direct_add', __("Direct add"))
    AMEND = (3, 'amend', __("Amend"))
Exemple #26
0
class AdminFiltersetView(UrlForView, ModelView):
    model = Filterset

    def loader(self, name=None):
        if name:
            return Filterset.get(g.board, name)

    @route('new', methods=['GET', 'POST'])
    @viewdata(title=__("New"))
    def new(self):
        if 'edit-filterset' not in g.board.current_permissions:
            abort(403)

        form = FiltersetForm(parent=g.board)
        if form.validate_on_submit():
            filterset = Filterset(board=g.board, title=form.title.data)
            form.populate_obj(filterset)
            try:
                db.session.add(filterset)
                db.session.commit()
                flash("Created a filterset", 'success')
                return render_redirect(filterset.url_for(), code=303)
            except ValueError:
                db.session.rollback()
                flash(
                    "There already exists a filterset with the selected criteria",
                    'interactive',
                )
        return render_form(
            form=form,
            title="Create a filterset…",
            submit="Create",
            formid="filterset_new",
            ajax=False,
        )

    @route('<name>/edit', methods=['GET', 'POST'])
    @viewdata(title=__("Edit"))
    def edit(self):
        if 'edit-filterset' not in g.board.current_permissions:
            abort(403)

        form = FiltersetForm(obj=self.obj)
        if form.validate_on_submit():
            form.populate_obj(self.obj)
            try:
                db.session.commit()
                flash("Updated filterset", 'success')
                return render_redirect(self.obj.url_for(), code=303)
            except ValueError:
                db.session.rollback()
                flash(
                    "There already exists a filterset with the selected criteria",
                    'interactive',
                )
        return render_form(
            form=form,
            title="Edit filterset…",
            submit="Update",
            formid="filterset_edit",
            ajax=False,
        )
Exemple #27
0
class CampaignForm(forms.Form):
    title = forms.StringField(
        __("Title"),
        description=__("A reference name for looking up this campaign again"),
        validators=[
            forms.validators.DataRequired(__("A title is required")),
            forms.validators.StripWhitespace()
        ])
    start_at = forms.DateTimeField(__("Start at"),
                                   timezone=lambda: g.user.timezone
                                   if g.user else None)
    end_at = forms.DateTimeField(__("End at"),
                                 timezone=lambda: g.user.timezone
                                 if g.user else None)
    public = forms.BooleanField(__("This campaign is live"))
    position = forms.RadioField(__("Display position"),
                                choices=CAMPAIGN_POSITION.items(),
                                coerce=int)
    priority = forms.IntegerField(
        __("Priority"),
        default=0,
        description=__(
            "A larger number is higher priority when multiple campaigns are running on the "
            "same dates. 0 implies lowest priority"))
    boards = QuerySelectMultipleField(
        __("Boards"),
        widget=ListWidget(),
        option_widget=CheckboxInput(),
        query_factory=lambda: Board.query.order_by('featured desc, title'),
        get_label='title_and_name',
        validators=[forms.validators.Optional()],
        description=__(u"Select the boards this campaign is active on"))
    geonameids = forms.GeonameSelectMultiField(
        "Locations",
        description=__(
            "This campaign will be targetted at users and jobs with matching locations"
        ))
    user_required = forms.RadioField(
        __("User is required"),
        coerce=getbool,
        choices=[(None, __(u"N/A – Don’t target by login status")),
                 (True, __(u"Yes – Show to logged in users only")),
                 (False, __(u"No – Show to anonymous users only"))])
    flags = forms.RadioMatrixField(
        "Flags",
        coerce=getbool,
        fields=Campaign.flag_choices,
        description=__(
            "All selected flags must match the logged in user for the campaign to be shown"
        ),
        choices=[('None', __(u"N/A")), ('True', __(u"True")),
                 ('False', __(u"False"))])
    content = forms.FormField(CampaignContentForm, __("Campaign content"))

    def validate_geonameids(self, field):
        field.data = [int(x) for x in field.data if x.isdigit()]

    def validate_end_at(self, field):
        if field.data <= self.start_at.data:
            raise forms.ValidationError(
                _(u"The campaign can’t end before it starts"))
Exemple #28
0
class Campaign(BaseNameMixin, db.Model):
    """
    A campaign runs in the header or sidebar of Hasjob's pages and prompts the user towards some action.
    Unlike announcements, campaigns sit outside the content area of listings.
    """

    __tablename__ = 'campaign'

    # Campaign metadata columns

    #: User who created this campaign
    user_id = db.Column(None, db.ForeignKey('user.id'), nullable=False)
    user = db.relationship(User, backref='campaigns')
    #: When does this campaign go on air?
    start_at = db.Column(db.TIMESTAMP(timezone=True), nullable=False, index=True)
    #: When does it go off air?
    end_at = db.Column(db.TIMESTAMP(timezone=True), nullable=False, index=True)
    #: Is this campaign live?
    public = db.Column(db.Boolean, nullable=False, default=False)
    #: StateManager for campaign's state
    state = StateManager('public', CAMPAIGN_STATE)
    #: Position to display campaign in
    position = db.Column(
        db.SmallInteger, nullable=False, default=CAMPAIGN_POSITION.HEADER
    )
    #: Boards to run this campaign on
    boards = db.relationship(Board, secondary=board_campaign_table)
    #: Quick lookup locations to run this campaign in
    geonameids = association_proxy(
        'locations', 'geonameid', creator=lambda l: CampaignLocation(geonameid=l)
    )
    #: Is this campaign location-based?
    geotargeted = db.Column(db.Boolean, nullable=False, default=False)
    #: Is a user required? None = don't care, True = user required, False = no user
    user_required = db.Column(db.Boolean, nullable=True, default=None)
    #: Priority, lower = less priority
    priority = db.Column(db.Integer, nullable=False, default=0)

    # Campaign content columns

    #: Subject (user-facing, unlike the title)
    subject = db.Column(db.Unicode(250), nullable=True)
    #: Call to action text (for header campaigns)
    blurb = db.Column(db.UnicodeText, nullable=False, default='')
    #: Full text (for read more click throughs)
    description = db.Column(db.UnicodeText, nullable=False, default='')
    #: Banner image
    banner_image = db.Column(db.Unicode(250), nullable=True)
    #: Banner location
    banner_location = db.Column(
        db.SmallInteger, nullable=False, default=BANNER_LOCATION.TOP
    )

    # Flags
    flag_is_new_since_day = db.Column(db.Boolean, nullable=True)
    flag_is_new_since_month = db.Column(db.Boolean, nullable=True)
    flag_is_not_new = db.Column(db.Boolean, nullable=True)

    flag_is_candidate_alltime = db.Column(db.Boolean, nullable=True)
    flag_is_candidate_day = db.Column(db.Boolean, nullable=True)
    flag_is_candidate_month = db.Column(db.Boolean, nullable=True)
    flag_is_candidate_past = db.Column(db.Boolean, nullable=True)

    flag_has_jobapplication_response_alltime = db.Column(db.Boolean, nullable=True)
    flag_has_jobapplication_response_day = db.Column(db.Boolean, nullable=True)
    flag_has_jobapplication_response_month = db.Column(db.Boolean, nullable=True)
    flag_has_jobapplication_response_past = db.Column(db.Boolean, nullable=True)

    flag_is_employer_alltime = db.Column(db.Boolean, nullable=True)
    flag_is_employer_day = db.Column(db.Boolean, nullable=True)
    flag_is_employer_month = db.Column(db.Boolean, nullable=True)
    flag_is_employer_past = db.Column(db.Boolean, nullable=True)

    flag_has_jobpost_unconfirmed_alltime = db.Column(db.Boolean, nullable=True)
    flag_has_jobpost_unconfirmed_day = db.Column(db.Boolean, nullable=True)
    flag_has_jobpost_unconfirmed_month = db.Column(db.Boolean, nullable=True)

    flag_has_responded_candidate_alltime = db.Column(db.Boolean, nullable=True)
    flag_has_responded_candidate_day = db.Column(db.Boolean, nullable=True)
    flag_has_responded_candidate_month = db.Column(db.Boolean, nullable=True)
    flag_has_responded_candidate_past = db.Column(db.Boolean, nullable=True)

    flag_is_new_lurker_within_day = db.Column(db.Boolean, nullable=True)
    flag_is_new_lurker_within_month = db.Column(db.Boolean, nullable=True)
    flag_is_lurker_since_past = db.Column(db.Boolean, nullable=True)
    flag_is_lurker_since_alltime = db.Column(db.Boolean, nullable=True)
    flag_is_inactive_since_day = db.Column(db.Boolean, nullable=True)
    flag_is_inactive_since_month = db.Column(db.Boolean, nullable=True)

    # Sessions this campaign has been viewed in
    session_views = db.relationship(
        EventSession,
        secondary=campaign_event_session_table,
        backref='campaign_views',
        order_by=campaign_event_session_table.c.created_at,
        lazy='dynamic',
    )

    __table_args__ = (
        db.CheckConstraint('end_at > start_at', name='campaign_start_at_end_at'),
    )

    # Campaign conditional states
    state.add_conditional_state(
        'LIVE',
        state.ENABLED,
        lambda obj: obj.start_at <= utcnow() < obj.end_at,
        lambda cls: db.and_(
            cls.start_at <= db.func.utcnow(), cls.end_at > db.func.utcnow()
        ),
        label=('live', __("Live")),
    )
    state.add_conditional_state(
        'CURRENT',
        state.ENABLED,
        lambda obj: obj.start_at
        <= obj.start_at
        <= utcnow()
        < obj.end_at
        <= utcnow() + timedelta(days=30),
        lambda cls: db.and_(
            cls.start_at <= db.func.utcnow(),
            cls.end_at > db.func.utcnow(),
            cls.end_at <= utcnow() + timedelta(days=30),
        ),
        label=('current', __("Current")),
    )
    state.add_conditional_state(
        'LONGTERM',
        state.ENABLED,
        lambda obj: obj.start_at
        <= obj.start_at
        <= utcnow()
        < utcnow() + timedelta(days=30)
        < obj.end_at,
        lambda cls: db.and_(
            cls.start_at <= utcnow(), cls.end_at > utcnow() + timedelta(days=30)
        ),
        label=('longterm', __("Long term")),
    )
    state.add_conditional_state(
        'OFFLINE',
        state.ENABLED,
        lambda obj: obj.start_at > utcnow() or obj.end_at <= utcnow(),
        lambda cls: db.or_(
            cls.start_at > db.func.utcnow(), cls.end_at <= db.func.utcnow()
        ),
        label=('offline', __("Offline")),
    )

    @property
    def content(self):
        """Form helper method"""
        return self

    @property
    def flags(self):
        """Form helper method"""
        return self

    def __repr__(self):
        return '<Campaign %s "%s" %s:%s>' % (
            self.name,
            self.title,
            self.start_at.strftime('%Y-%m-%d'),
            self.end_at.strftime('%Y-%m-%d'),
        )

    def useractions(self, user):
        if user is not None:
            return {
                cua.action.name: cua
                for cua in CampaignUserAction.query.filter_by(user=user)
                .filter(CampaignUserAction.action_id.in_([a.id for a in self.actions]))
                .all()
            }
        else:
            return {}

    def view_for(self, user=None, anon_user=None):
        if user:
            return CampaignView.get(campaign=self, user=user)
        elif anon_user:
            return CampaignAnonView.get(campaign=self, anon_user=anon_user)

    def subject_for(self, user):
        return self.subject.format(user=user)

    def blurb_for(self, user):
        return Markup(self.blurb).format(user=user)

    def description_for(self, user):
        return Markup(self.description).format(user=user)

    def estimated_reach(self):
        """
        Returns the number of users this campaign could potentially reach (assuming users are all active)
        """
        plus_userids = set()
        minus_userids = set()
        for flag in Campaign.supported_flags:
            setting = getattr(self, 'flag_' + flag)
            if setting is True or setting is False:
                query = getattr(UserFlags, flag).user_ids()
            if setting is True:
                userids = set(query.all())
                if plus_userids:
                    plus_userids = plus_userids.intersection(userids)
                else:
                    plus_userids = userids
            elif setting is False:
                userids = set(db.session.query(~User.id.in_(query)).all())
                if minus_userids:
                    minus_userids = minus_userids.union(userids)
                else:
                    minus_userids = userids
        if not plus_userids and not minus_userids:
            return None
        return len(plus_userids - minus_userids)

    def form(self):
        """Convenience method for action form CSRF"""
        return Form()

    @classmethod
    def for_context(
        cls, position, board=None, user=None, anon_user=None, geonameids=None
    ):
        """
        Return a campaign suitable for this board, user and locations (as geonameids).
        """
        basequery = cls.query.filter(cls.state.LIVE, cls.position == position)

        if board:
            basequery = basequery.filter(cls.boards.any(id=board.id))

        if user:
            # Look for campaigns that don't target by user or require a user
            basequery = basequery.filter(
                db.or_(cls.user_required.is_(None), cls.user_required.is_(True))
            )
        else:
            # Look for campaigns that don't target by user or require no user
            basequery = basequery.filter(
                db.or_(cls.user_required.is_(None), cls.user_required.is_(False))
            )

        if geonameids:
            # TODO: The query for CampaignLocation.campaign_id here does not consider
            # if the campaign id is currently live. This will become inefficient as the
            # number of location-targeted campaigns grows. This should be cached.
            basequery = basequery.filter(
                db.or_(
                    cls.geotargeted.is_(False),
                    db.and_(
                        cls.geotargeted.is_(True),
                        cls.id.in_(
                            db.session.query(CampaignLocation.campaign_id).filter(
                                CampaignLocation.geonameid.in_(geonameids)
                            )
                        ),
                    ),
                )
            )

            # In the following simpler version, a low priority geotargeted campaign returns above a high priority
            # non-geotargeted campaign, which isn't the intended behaviour. We've therefore commented it out and
            # left it here for reference.

            # basequery = basequery.join(CampaignLocation).filter(db.or_(
            #     cls.geotargeted.is_(False),
            #     db.and_(
            #         cls.geotargeted.is_(True),
            #         CampaignLocation.geonameid.in_(geonameids)
            #     )))
        else:
            basequery = basequery.filter(cls.geotargeted.is_(False))

        # Don't show campaigns that (a) the user has dismissed or (b) the user has encountered on >2 event sessions
        if user is not None:
            # TODO: The more campaigns we run, the more longer this list gets. Find something more efficient
            basequery = basequery.filter(
                ~cls.id.in_(
                    db.session.query(CampaignView.campaign_id).filter(
                        CampaignView.user == user,
                        db.or_(
                            CampaignView.dismissed.is_(True),
                            CampaignView.session_count > 2,
                        ),
                    )
                )
            )

            # Filter by user flags
            for flag, value in user.flags.items():
                if flag in cls.supported_flags:
                    col = getattr(cls, 'flag_' + flag)
                    basequery = basequery.filter(db.or_(col.is_(None), col == value))

        else:
            if anon_user:
                basequery = basequery.filter(
                    ~cls.id.in_(
                        db.session.query(CampaignAnonView.campaign_id).filter(
                            CampaignAnonView.anon_user == anon_user,
                            db.or_(
                                CampaignAnonView.dismissed.is_(True),
                                CampaignAnonView.session_count > 2,
                            ),
                        )
                    )
                )
            # Don't show user-targeted campaigns if there's no user
            basequery = basequery.filter_by(
                **{'flag_' + flag: None for flag in cls.supported_flags}
            )

        return basequery.order_by(cls.priority.desc()).first()

    @classmethod
    def get(cls, name):
        return cls.query.filter_by(name=name).one_or_none()
Exemple #29
0
class TransferProposal(forms.Form):
    userid = forms.UserSelectField(
        __("Transfer to"), validators=[forms.validators.DataRequired()])
Exemple #30
0
class VenueForm(Form):
    title = wtforms.TextField(__("Name"),
                              description=__("Name of the venue"),
                              validators=[
                                  wtforms.validators.Required(),
                                  wtforms.validators.length(max=250)
                              ])
    description = MarkdownField(
        __("Description"), description=__("An optional note about the venue"))
    address1 = wtforms.TextField(__("Address (line 1)"),
                                 validators=[
                                     wtforms.validators.Optional(),
                                     wtforms.validators.length(max=160)
                                 ])
    address2 = wtforms.TextField(__("Address (line 2)"),
                                 validators=[
                                     wtforms.validators.Optional(),
                                     wtforms.validators.length(max=160)
                                 ])
    city = wtforms.TextField(__("City"),
                             validators=[
                                 wtforms.validators.Optional(),
                                 wtforms.validators.length(max=30)
                             ])
    state = wtforms.TextField(__("State"),
                              validators=[
                                  wtforms.validators.Optional(),
                                  wtforms.validators.length(max=30)
                              ])
    postcode = wtforms.TextField(__("Post code"),
                                 validators=[
                                     wtforms.validators.Optional(),
                                     wtforms.validators.length(max=20)
                                 ])
    country = wtforms.SelectField(__("Country"),
                                  validators=[
                                      wtforms.validators.Optional(),
                                      wtforms.validators.length(max=2)
                                  ],
                                  choices=country_codes,
                                  default="IN")
    latitude = wtforms.DecimalField(__("Latitude"),
                                    places=None,
                                    validators=[
                                        wtforms.validators.Optional(),
                                        wtforms.validators.NumberRange(
                                            -90, 90)
                                    ])
    longitude = wtforms.DecimalField(__("Longitude"),
                                     places=None,
                                     validators=[
                                         wtforms.validators.Optional(),
                                         wtforms.validators.NumberRange(
                                             -180, 180)
                                     ])
Exemple #31
0
def validate_unique_discount_coupon_code(form, field):
    if DiscountCoupon.query.filter(DiscountCoupon.discount_policy == form.edit_parent, DiscountCoupon.code == field.data).notempty():
        raise StopValidation(__("This discount coupon code already exists. Please enter a different coupon code"))
Exemple #32
0
class DiscountPriceForm(forms.Form):
    title = forms.StringField(
        __("Discount price title"),
        validators=[
            forms.validators.DataRequired(
                __("Please specify a title for the discount price")),
            forms.validators.Length(max=250)
        ],
        filters=[forms.filters.strip()])
    amount = forms.IntegerField(__("Amount"),
                                validators=[
                                    forms.validators.DataRequired(
                                        __("Please specify an amount"))
                                ])
    currency = forms.RadioField(__("Currency"),
                                validators=[
                                    forms.validators.DataRequired(
                                        __("Please select the currency"))
                                ],
                                choices=list(CURRENCY.items()),
                                default=CURRENCY.INR)
    start_at = forms.DateTimeField(
        __("Price start date"),
        validators=[
            forms.validators.DataRequired(
                __("Please specify a start date and time"))
        ],
        naive=False)
    end_at = forms.DateTimeField(
        __("Price end date"),
        validators=[
            forms.validators.DataRequired(
                __("Please specify an end date and time")),
            forms.validators.GreaterThan(
                'start_at',
                __("Please specify an end date for the price that is greater than the start date"
                   ))
        ],
        naive=False)
    item = QuerySelectField(
        __("Item"),
        get_label='title',
        validators=[
            forms.validators.DataRequired(
                __("Please select a item for which the discount is to be applied"
                   ))
        ])

    def set_queries(self):
        self.item.query = Item.query.join(ItemCollection).filter(
            ItemCollection.organization ==
            self.edit_parent.organization).options(db.load_only('id', 'title'))
Exemple #33
0
    if request.is_xhr and formid == 'passwordlogin':
        return render_template('loginform.html.jinja2',
                               loginform=loginform,
                               Markup=Markup), 200, iframe_block
    else:
        return render_template(
            'login.html.jinja2',
            loginform=loginform,
            lastused=loginmethod,
            service_forms=service_forms,
            Markup=Markup,
            login_registry=login_registry), 200, iframe_block


logout_errormsg = __(
    "We detected a possibly unauthorized attempt to log you out. "
    "If you really did intend to logout, please click on the logout link again"
)


def logout_user():
    """
    User-initiated logout
    """
    if not request.referrer or (urlparse.urlsplit(request.referrer).netloc !=
                                urlparse.urlsplit(request.url).netloc):
        # TODO: present a logout form
        flash(
            current_app.config.get('LOGOUT_UNAUTHORIZED_MESSAGE')
            or logout_errormsg, 'danger')
        return redirect(url_for('index'))
    else:
Exemple #34
0
class JobApplication(BaseMixin, db.Model):
    __tablename__ = 'job_application'
    #: Hash id (to hide database ids)
    hashid = db.Column(db.String(40), nullable=False, unique=True)
    #: User who applied for this post
    user_id = db.Column(None,
                        db.ForeignKey('user.id'),
                        nullable=True,
                        index=True)  # TODO: add unique=True
    user = db.relationship(User, foreign_keys=user_id)
    #: Full name of the user (as it was at the time of the application)
    fullname = db.Column(db.Unicode(250), nullable=False)
    #: Job post they applied to
    jobpost_id = db.Column(None,
                           db.ForeignKey('jobpost.id'),
                           nullable=False,
                           index=True)
    # jobpost relationship is below, outside the class definition
    #: User's email address
    email = db.Column(db.Unicode(80), nullable=False)
    #: User's phone number
    phone = db.Column(db.Unicode(80), nullable=False)
    #: User's message
    message = db.Column(db.UnicodeText, nullable=False)
    #: User opted-in to experimental features
    optin = db.Column(db.Boolean, default=False, nullable=False)
    #: Employer's response code
    _response = db.Column('response',
                          db.Integer,
                          StateManager.check_constraint(
                              'response', EMPLOYER_RESPONSE),
                          nullable=False,
                          default=EMPLOYER_RESPONSE.NEW)
    response = StateManager('_response',
                            EMPLOYER_RESPONSE,
                            doc="Employer's response")
    #: Employer's response message
    response_message = db.Column(db.UnicodeText, nullable=True)
    #: Bag of words, for spam analysis
    words = db.Column(db.UnicodeText, nullable=True)
    #: Jobpost admin who replied to this candidate
    replied_by_id = db.Column(None,
                              db.ForeignKey('user.id'),
                              nullable=True,
                              index=True)
    replied_by = db.relationship(User, foreign_keys=replied_by_id)
    #: When they replied
    replied_at = db.Column(db.DateTime, nullable=True)

    candidate_feedback = db.Column(db.SmallInteger, nullable=True)

    def __init__(self, **kwargs):
        super(JobApplication, self).__init__(**kwargs)
        if self.hashid is None:
            self.hashid = unique_long_hash()

    @response.transition(response.NEW,
                         response.PENDING,
                         title=__("Mark read"),
                         message=__("This job application has been read"),
                         type='success')
    def mark_read(self):
        pass

    @response.transition(
        response.CAN_REPLY,
        response.REPLIED,
        title=__("Reply"),
        message=__("This job application has been replied to"),
        type='success')
    def reply(self, message, user):
        self.response_message = message
        self.replied_by = user
        self.replied_at = db.func.utcnow()

    @response.transition(response.CAN_REJECT,
                         response.REJECTED,
                         title=__("Reject"),
                         message=__("This job application has been rejected"),
                         type='danger')
    def reject(self, message, user):
        self.response_message = message
        self.replied_by = user
        self.replied_at = db.func.utcnow()

    @response.transition(response.CAN_IGNORE,
                         response.IGNORED,
                         title=__("Ignore"),
                         message=__("This job application has been ignored"),
                         type='danger')
    def ignore(self):
        pass

    @response.transition(response.CAN_REPORT,
                         response.FLAGGED,
                         title=__("Report"),
                         message=__("This job application has been reported"),
                         type='danger')
    def flag(self):
        pass

    @response.transition(response.FLAGGED,
                         response.PENDING,
                         title=__("Unflag"),
                         message=__("This job application has been unflagged"),
                         type='success')
    def unflag(self):
        pass

    def application_count(self):
        """Number of jobs candidate has applied to around this one"""
        if not self.user:
            # Kiosk submission, no data available
            return {
                'count': 0,
                'ignored': 0,
                'replied': 0,
                'flagged': 0,
                'spam': 0,
                'rejected': 0
            }
        date_min = self.created_at - timedelta(days=7)
        date_max = self.created_at + timedelta(days=7)
        grouped = JobApplication.response.group(
            JobApplication.query.filter(
                JobApplication.user == self.user).filter(
                    JobApplication.created_at > date_min,
                    JobApplication.created_at < date_max).options(
                        db.load_only('id')))
        counts = {k.label.name: len(v) for k, v in grouped.items()}
        counts['count'] = sum(counts.values())
        return counts

    def url_for(self, action='view', _external=False, **kwargs):
        domain = self.jobpost.email_domain
        if action == 'view':
            return url_for('view_application',
                           hashid=self.jobpost.hashid,
                           domain=domain,
                           application=self.hashid,
                           _external=_external,
                           **kwargs)
        elif action == 'process':
            return url_for('process_application',
                           hashid=self.jobpost.hashid,
                           domain=domain,
                           application=self.hashid,
                           _external=_external,
                           **kwargs)
        elif action == 'track-open':
            return url_for('view_application_email_gif',
                           hashid=self.jobpost.hashid,
                           domain=domain,
                           application=self.hashid,
                           _external=_external,
                           **kwargs)
Exemple #35
0
def validate_state_code(form, field):
    # Note: state_code is only a required field if the chosen country is India
    if form.country_code.data == "IN":
        if field.data.upper() not in indian_states_dict:
            raise forms.validators.StopValidation(__("Please select a state"))
Exemple #36
0
            'is_active',
        }
    }

    #: Make obj.user from a referring object falsy
    def __bool__(self):
        return False

    def __init__(self, representation):
        self.fullname = self.title = self.pickername = representation

    def __str__(self):
        return self.pickername


deleted_user = DuckTypeUser(__("[deleted]"))
removed_user = DuckTypeUser(__("[removed]"))

# --- Organizations and teams -------------------------------------------------

team_membership = db.Table(
    'team_membership',
    db.Model.metadata,
    db.Column('user_id',
              None,
              db.ForeignKey('user.id'),
              nullable=False,
              primary_key=True),
    db.Column('team_id',
              None,
              db.ForeignKey('team.id'),
Exemple #37
0
class ProposalSubspaceForm(ProposalSpaceForm):
    inherit_sections = forms.BooleanField(
        __("Inherit sections from parent space?"), default=True)
Exemple #38
0
class CampaignActionForm(forms.Form):
    title = forms.StringField(
        __("Title"),
        description=__("Contents of the call to action button"),
        validators=[
            forms.validators.DataRequired("You must provide some text"),
            forms.validators.StripWhitespace()
        ])
    icon = forms.NullTextField(
        __("Icon"),
        validators=[forms.validators.Optional()],
        description=__("Optional Font-Awesome icon name"))
    public = forms.BooleanField(__("This action is live"))
    type = forms.RadioField(
        __("Type"),
        choices=CAMPAIGN_ACTION.items(),
        validators=[forms.validators.DataRequired(__("This is required"))])
    group = forms.NullTextField(
        __("RSVP group"),
        validators=[forms.validators.Optional()],
        description=__(
            "If you have multiple RSVP actions, add an optional group name"))
    category = forms.RadioField(
        __("Category"),
        validators=[forms.validators.DataRequired(__("This is required"))],
        widget=forms.InlineListWidget(class_='button-bar',
                                      class_prefix='btn btn-'),
        choices=[
            (u'default', __(u"Default")),
            (u'primary', __(u"Primary")),
            (u'success', __(u"Success")),
            (u'info', __(u"Info")),
            (u'warning', __(u"Warning")),
            (u'danger', __(u"Danger")),
        ])
    message = forms.TinyMce4Field(
        __("Message"),
        description=__(
            "Message shown after the user has performed an action (for forms and RSVP type)"
        ),
        content_css=content_css,
        validators=[
            forms.validators.Optional(),
            forms.validators.AllUrlsValid()
        ])
    link = forms.URLField(
        __("Link"),
        description=__(u"URL to redirect to, if type is “follow link”"),
        validators=[
            forms.validators.StripWhitespace(), optional_url,
            forms.validators.Length(min=0,
                                    max=250,
                                    message=__("%%(max)d characters maximum")),
            forms.validators.ValidUrl()
        ])
    form = forms.TextAreaField(
        __("Form JSON"),
        description=__("Form definition (for form type)"),
        validators=[forms.validators.Optional()])
    seq = forms.IntegerField(
        __("Sequence #"),
        validators=[forms.validators.DataRequired(__("This is required"))],
        description=__(
            "Sequence number for displaying this action when multiple actions are available to the user"
        ))
Exemple #39
0
class EventForm(forms.Form):
    title = forms.StringField(__("Title"),
                              validators=[forms.validators.DataRequired()])
Exemple #40
0
def tag_locations(project_id):
    if app.config.get('HASCORE_SERVER'):
        with app.test_request_context():
            project = Project.query.get(project_id)
            if not project.location:
                return
            url = urljoin(app.config['HASCORE_SERVER'],
                          '/1/geo/parse_locations')
            response = requests.get(
                url,
                params={
                    'q':
                    project.location,
                    'bias': ['IN', 'US'],
                    'special':
                    ['Internet', 'Online',
                     __('Internet'),
                     __('Online')],
                },
            ).json()

            if response.get('status') == 'ok':
                results = response.get('result', [])
                geonames = defaultdict(dict)
                tokens = []
                for item in results:
                    geoname = item.get('geoname', {})
                    if geoname:
                        geonames[geoname['geonameid']]['geonameid'] = geoname[
                            'geonameid']
                        geonames[geoname['geonameid']]['primary'] = geonames[
                            geoname['geonameid']].get('primary', True)
                        for gtype, related in geoname.get('related',
                                                          {}).items():
                            if gtype in [
                                    'admin2', 'admin1', 'country', 'continent'
                            ]:
                                geonames[related['geonameid']][
                                    'geonameid'] = related['geonameid']
                                geonames[
                                    related['geonameid']]['primary'] = False

                        tokens.append({
                            'token': item.get('token', ''),
                            'geoname': {
                                'name': geoname['name'],
                                'geonameid': geoname['geonameid'],
                            },
                        })
                    else:
                        tokens.append({'token': item.get('token', '')})

                project.parsed_location = {'tokens': tokens}

                for locdata in geonames.values():
                    loc = ProjectLocation.query.get(
                        (project_id, locdata['geonameid']))
                    if loc is None:
                        loc = ProjectLocation(project=project,
                                              geonameid=locdata['geonameid'])
                        db.session.add(loc)
                        db.session.flush()
                    loc.primary = locdata['primary']
                for location in project.locations:
                    if location.geonameid not in geonames:
                        db.session.delete(location)
                db.session.commit()
Exemple #41
0
class ProposalSpaceForm(forms.Form):
    name = forms.StringField(__("URL name"),
                             validators=[
                                 forms.validators.DataRequired(),
                                 forms.ValidName(),
                                 AvailableName()
                             ])
    title = forms.StringField(__("Title"),
                              validators=[forms.validators.DataRequired()])
    datelocation = forms.StringField(__("Date and Location"),
                                     validators=[
                                         forms.validators.DataRequired(),
                                         forms.validators.Length(max=50)
                                     ])
    date = forms.DateField(
        __("Start date (for sorting)"),
        validators=[forms.validators.DataRequired(__("This is required"))])
    date_upto = forms.DateField(
        __("End date (for sorting)"),
        validators=[forms.validators.DataRequired(__("This is required"))])
    tagline = forms.StringField(
        __("Tagline"),
        validators=[forms.validators.DataRequired()],
        description=__("This is displayed on the card on the homepage"))
    website = forms.URLField(__("Website"),
                             validators=[forms.validators.Optional()])
    description = forms.MarkdownField(
        __("Description"),
        validators=[forms.validators.DataRequired()],
        description=__("About Event"))
    timezone = forms.SelectField(
        __("Timezone"),
        description=__("The timezone in which this event occurs"),
        validators=[forms.validators.DataRequired()],
        choices=sorted_timezones(),
        default=u'UTC')
    bg_image = forms.URLField(
        __("Background image URL"),
        description=u"Background image for the mobile app",
        validators=[forms.validators.Optional()])
    bg_color = forms.StringField(
        __("Background color"),
        description=__(
            "RGB color for the event, shown on the mobile app. Enter without the '#'. E.g. CCCCCC."
        ),
        validators=[
            forms.validators.Optional(),
            forms.validators.Length(max=6)
        ],
        default=u"CCCCCC")
    explore_url = forms.URLField(
        __("Explore tab URL"),
        description=__(
            u"Page containing the explore tab’s contents, for the mobile app"),
        validators=[forms.validators.Optional()])
    parent_space = QuerySelectField(__(u"Parent space"),
                                    get_label='title',
                                    allow_blank=True,
                                    blank_text=__(u"None"))

    status = forms.SelectField(
        __("Status"),
        coerce=int,
        choices=[
            (0, __("Draft")),
            (1, __("Open")),
            (2, __("Voting")),
            (3, __("Jury selection")),
            (4, __("Feedback")),
            (5, __("Closed")),
            (6, __("Withdrawn")),
        ],
        description=__(u"Proposals can only be submitted in the “Open” state. "
                       u"“Closed” and “Withdrawn” are hidden from homepage"))
    admin_team = QuerySelectField(
        u"Admin Team",
        validators=[
            forms.validators.DataRequired(__(u"Please select a team"))
        ],
        query_factory=profile_teams,
        get_label='title',
        allow_blank=False,
        description=__(u"The administrators of this proposal space"))
    review_team = QuerySelectField(
        u"Review Team",
        validators=[
            forms.validators.DataRequired(__(u"Please select a team"))
        ],
        query_factory=profile_teams,
        get_label='title',
        allow_blank=False,
        description=__(
            u"Reviewers can see contact details of proposers, but can’t change settings"
        ))
    allow_rsvp = forms.BooleanField(
        __("Allow site visitors to RSVP (login required)"))
    buy_tickets_url = forms.URLField(__("URL to buy tickets"),
                                     description=__(u"Eg: Explara, Instamojo"),
                                     validators=[forms.validators.Optional()])

    def validate_date_upto(self, date_upto):
        if self.date_upto.data < self.date.data:
            raise forms.ValidationError(
                _("End date cannot be before start date"))

    def validate_bg_color(self, field):
        if not valid_color_re.match(field.data):
            raise forms.ValidationError("Please enter a valid color code")
Exemple #42
0
class ProposalForm(forms.Form):
    speaking = forms.RadioField(
        __("Are you speaking?"),
        coerce=int,
        choices=[(1, __(u"I will be speaking")),
                 (0, __(u"I’m proposing a topic for someone to speak on"))])
    title = forms.StringField(__("Title"),
                              validators=[forms.validators.DataRequired()],
                              description=__("The title of your session"))
    section = QuerySelectField(__("Section"),
                               get_label='title',
                               validators=[forms.validators.DataRequired()],
                               widget=forms.ListWidget(prefix_label=False),
                               option_widget=forms.RadioInput())
    objective = forms.MarkdownField(
        __("Objective"),
        validators=[forms.validators.DataRequired()],
        description=__(
            "What is the expected benefit for someone attending this?"))
    session_type = forms.RadioField(
        __("Session type"),
        validators=[forms.validators.DataRequired()],
        choices=[
            ('Lecture', __("Lecture")),
            ('Demo', __("Demo")),
            ('Tutorial', __("Tutorial")),
            ('Workshop', __("Workshop")),
            ('Discussion', __("Discussion")),
            ('Panel', __("Panel")),
        ])
    technical_level = forms.RadioField(
        __("Technical level"),
        validators=[forms.validators.DataRequired()],
        choices=[
            ('Beginner', __("Beginner")),
            ('Intermediate', __("Intermediate")),
            ('Advanced', __("Advanced")),
        ])
    description = forms.MarkdownField(
        __("Description"),
        validators=[forms.validators.DataRequired()],
        description=__("A detailed description of the session"))
    requirements = forms.MarkdownField(
        __("Requirements"),
        description=__(
            "For workshops, what must participants bring to the session?"))
    slides = forms.URLField(
        __("Slides"),
        validators=[forms.validators.Optional(),
                    forms.validators.URL()],
        description=__(
            "Link to your slides. These can be just an outline initially. "
            "If you provide a Slideshare/Speakerdeck link, we'll embed slides in the page"
        ))
    preview_video = forms.URLField(
        __("Preview Video"),
        validators=[forms.validators.Optional(),
                    forms.validators.URL()],
        description=__(
            "Link to your preview video. Use a video to engage the community and give them a better idea about what you are planning to cover in your session and why they should attend. "
            "If you provide a YouTube/Vimeo link, we'll embed it in the page"))
    links = forms.TextAreaField(
        __("Links"),
        description=__(
            "Other links, one per line. Provide links to your profile and "
            "slides and videos from your previous sessions; anything that'll help "
            "folks decide if they want to attend your session"))
    bio = forms.MarkdownField(
        __("Speaker bio"),
        validators=[forms.validators.DataRequired()],
        description=__(
            "Tell us why you are the best person to be taking this session"))
    email = forms.EmailField(__("Your email address"),
                             validators=[
                                 forms.validators.DataRequired(),
                                 forms.validators.Length(max=80)
                             ],
                             description=__(
                                 "An email address we can contact you at. "
                                 "Not displayed anywhere"))
    phone = forms.StringField(
        __("Phone number"),
        validators=[
            forms.validators.DataRequired(),
            forms.validators.Length(max=80)
        ],
        description=__(
            "A phone number we can call you at to discuss your proposal, if required. "
            "Will not be displayed"))
    location = forms.StringField(
        __("Your location"),
        validators=[
            forms.validators.DataRequired(),
            forms.validators.Length(max=80)
        ],
        description=__(
            "Your location, to help plan for your travel if required"))
Exemple #43
0
        abort(404)
    if current_auth.is_authenticated:
        token = auth_client.authtoken_for(current_auth.user)
    else:
        token = None
    response = jsonify({'hastoken': True if token else False})
    response.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
    response.headers['Cache-Control'] = 'private, max-age=300'
    return response


# --- Token-based resource endpoints ------------------------------------------


@lastuser_oauth.route('/api/1/id')
@resource_registry.resource('id', __("Read your name and basic profile data"))
def resource_id(authtoken, args, files=None):
    """
    Return user's id
    """
    if 'all' in args and getbool(args['all']):
        return get_userinfo(
            authtoken.user,
            authtoken.auth_client,
            scope=authtoken.effective_scope,
            get_permissions=True,
        )
    else:
        return get_userinfo(authtoken.user,
                            authtoken.auth_client,
                            scope=['id'],