Beispiel #1
0
def create(username, password, name=None, **kwargs):
    """Create and return a new instance of
    :class:`pooldlib.postgresql.models.User`. Any unspecified
    kwargs will be assumed to be metadata to be associated with
    the new user. All errors are bubbled up.

    :param username: The username of the new user.
    :type username: string
    :param password: The password for the new user.
    :type password: string
    :param name: The full name of the new user (optional).
    :type password: string
    :param kwargs: Metadata to associate with the new user.
    :type kwargs: kwarg dictionary

    :raises: :class:`pooldlib.exceptions.InvalidPasswordError`
             :class:`pooldlib.exceptions.UsernameUnavailableError`
             :class:`pooldlib.exceptions.EmailUnavailableError`

    :returns: :class:`pooldlib.postgresql.models.User`
    """
    validate_password(password, exception_on_invalid=True)

    if 'email' in kwargs:
        if email_exists(kwargs['email']):
            msg = 'The email address %s is already assigned to another user.'
            msg %= kwargs['email']
            raise EmailUnavailableError(msg)
        # Only store lower-case emails in the system
        kwargs['email'] = kwargs['email'].lower()

    u = UserModel()
    u.username = username
    u.password = password
    if name:
        u.name = name

    with transaction_session() as session:
        try:
            session.add(u)
            session.commit()
        except SQLAlchemyIntegrityError:
            msg = "Username %s already in use." % username
            raise UsernameUnavailableError(msg)

    meta = list()
    for (k, v) in kwargs.items():
        um = UserMetaModel()
        um.key = k
        um.value = v
        um.user = u
        meta.append(um)

    with transaction_session(auto_commit=True) as session:
        for um in meta:
            session.add(um)
    return u
Beispiel #2
0
def add_goal(campaign,
             name,
             description,
             type,
             predecessor=None,
             start=None,
             end=None,
             **kwargs):
    """Add a goal to an existing campaign. ``name`` and ``description``
    are required.  Any key-value pair will be assumed to be metadata to be
    added to the goal instance.

    :param campaign: The campaign goal which the add a new goal.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param name: The name of the newly created campaign goal.
    :type name: string
    :param description: The description of the newly created campaign goal.
    :type description: string
    :param type: The type of goal to add (fund-raiser, project or group-purchase)
    :type type: string
    :param start: Active start datetime for the campaign in UTC, defaults to `datetime.utcnow`
    :type start: :class:`datetime.datetime`
    :param end: Active end datetime for the campaign in UTC, optional
    :type end: :class:`datetime.datetime` or `None`
    :param kwargs: Keyword arguments consisting of key-value pairs to be added to the
                   newly created goal as metadata.
    :type kwargs: unspecified keyword arguments to the function.

    :returns: :class:`pooldlib.postgresql.models.CommunitiyGoal`
    """
    goal = CampaignGoalModel()
    goal.name = name
    goal.description = description
    goal.start = start or pytz.UTC.localize(datetime.utcnow())
    goal.end = end
    goal.type = type
    if predecessor is not None:
        goal.predecessor = predecessor
    campaign.goals.append(goal)

    with transaction_session() as session:
        session.add(goal)
        session.commit()

    meta = list()
    for (k, v) in kwargs.items():
        goal_meta = CampaignGoalMetaModel()
        goal_meta.key = k
        goal_meta.value = v
        goal_meta.campaign_goal = goal
        meta.append(goal_meta)

    with transaction_session(auto_commit=True) as session:
        for goal_meta in meta:
            session.add(goal_meta)
    return goal
Beispiel #3
0
def add_goal(campaign, name, description, type, predecessor=None, start=None, end=None, **kwargs):
    """Add a goal to an existing campaign. ``name`` and ``description``
    are required.  Any key-value pair will be assumed to be metadata to be
    added to the goal instance.

    :param campaign: The campaign goal which the add a new goal.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param name: The name of the newly created campaign goal.
    :type name: string
    :param description: The description of the newly created campaign goal.
    :type description: string
    :param type: The type of goal to add (fund-raiser, project or group-purchase)
    :type type: string
    :param start: Active start datetime for the campaign in UTC, defaults to `datetime.utcnow`
    :type start: :class:`datetime.datetime`
    :param end: Active end datetime for the campaign in UTC, optional
    :type end: :class:`datetime.datetime` or `None`
    :param kwargs: Keyword arguments consisting of key-value pairs to be added to the
                   newly created goal as metadata.
    :type kwargs: unspecified keyword arguments to the function.

    :returns: :class:`pooldlib.postgresql.models.CommunitiyGoal`
    """
    goal = CampaignGoalModel()
    goal.name = name
    goal.description = description
    goal.start = start or pytz.UTC.localize(datetime.utcnow())
    goal.end = end
    goal.type = type
    if predecessor is not None:
        goal.predecessor = predecessor
    campaign.goals.append(goal)

    with transaction_session() as session:
        session.add(goal)
        session.commit()

    meta = list()
    for (k, v) in kwargs.items():
        goal_meta = CampaignGoalMetaModel()
        goal_meta.key = k
        goal_meta.value = v
        goal_meta.campaign_goal = goal
        meta.append(goal_meta)

    with transaction_session(auto_commit=True) as session:
        for goal_meta in meta:
            session.add(goal_meta)
    return goal
Beispiel #4
0
def update_goal(update_goal, name=None, predecessor=None, description=None, start=None, end=None, **kwargs):
    """Update an existing goal for a campaign. Only ``goal`` is required. All
    given goal properties will be updated. Any unspecified keyword arguments will
    be used to update the goal's metadata. To delete metadata for a campaign goal,
    pass ``None`` as the value for the to be deleted key in the kwarg key-value pair.

    :param update_goal: Identifier for the target campaign.
    :type update_goal: :class:`pooldlib.postgresql.models.Goal`
    :param name: The name of the newly created campaign goal.
    :type name: string
    :param description: The description of the newly created campaign goal.
    :type name: string
    :param kwargs: Keyword arguments consisting of key-value pairs to be added to the
                   newly created goal as metadata.
    :type kwargs: unspecified keyword arguments to the function.
    """
    if name is not None:
        update_goal.name = name
    if description is not None:
        update_goal.description = description
    if start is not None:
        update_goal.start = start
    if end is not None:
        update_goal.end = end
    if predecessor is not None:
        goal.predecessor_id = predecessor.id

    update_meta = [m for m in update_goal.metadata if m.key in kwargs]
    create_meta = [(k, v) for (k, v) in kwargs.items() if not hasattr(update_goal, k)]

    meta_delta = list()
    meta_remove = list()
    for goal_meta in update_meta:
        value = kwargs[m.key]
        if value is None:
            meta_remove.append(goal_meta)
        else:
            goal_meta.value = value
            meta_delta.append(goal_meta)

    for (k, v) in create_meta:
        goal_meta = CampaignGoalMetaModel()
        goal_meta.key = k
        goal_meta.value = v
        goal_meta.campaign_goal = update_goal
        meta_delta.append(goal_meta)

    with transaction_session() as session:
        session.add(update_goal)  # Technically not needed
        session.flush()

        for goal_meta in meta_delta:
            session.add(goal_meta)
        for goal_meta in meta_remove:
            session.delete(goal_meta)

        session.commit()

    return update_goal
Beispiel #5
0
def associate_user(campaign, user, role, goal_participation, pledge=None):
    """Associate a user with a campaign filling a specified role.

    :param campaign: The campaign with which to associate the user.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user for which to create the association.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param role: The role to assign the user (either `organizer` or `participant`)
    :type role: string
    :param goal_participation: The participation description for the campaign goals.
                               One of `participating`, `nonparticipating`, `opted-in` or `opted-out`
                               (See :func:`pooldlib.api.campaign.associate_user_with_goal`)
    :type goal_participation: string

    :raises: :class:`pooldlib.exceptions.InvalidUserRoleError`
             :class:`pooldlib.exceptions.DuplicateCampaignUserAssociationError`

    :return: :class:`pooldlib.postgresql.models.CampaignAssociation`
    """
    # NOTE :: We intentionally associate the user with all existing goals, not just
    # NOTE :: active ones. Date stamps can distinguish users who joined prior to goal
    # NOTE :: becoming inactive.
    campaign_goals = goals(campaign, filter_inactive=False)
    ca = CampaignAssociationModel()
    ca.enabled = True
    ca.campaign = campaign
    ca.user = user
    ca.role = role
    goal_pledge = None
    if pledge is not None:
        ca.pledge = pledge
        if campaign_goals:
            goal_pledge = pledge / len(campaign_goals)
    for goal in campaign_goals:
        associate_user_with_goal(goal,
                                 user,
                                 goal_participation,
                                 pledge=goal_pledge)

    # Check to see if this user was invited and mark them as accepted
    update_invitee = None
    for invitee in campaign.invitees:
        if invitee.user == user or invitee.email == user.email:
            invitee.accepted = pytz.UTC.localize(datetime.utcnow())
            invitee.user = user
            update_invitee = invitee

    with transaction_session() as session:
        session.add(ca)
        if update_invitee is not None:
            session.add(update_invitee)
        try:
            session.commit()
        except SQLAlchemyDataError:
            raise InvalidUserRoleError()
        except SQLAlchemyIntegrityError:
            raise DuplicateCampaignUserAssociationError()
    return ca
Beispiel #6
0
def create(organizer, name, description, start=None, end=None, **kwargs):
    """Create and return a new instance of
    :class:`pooldlib.postgresql.models.Campaign`.

    :param origanizer: The user to be classified as the campaign's organizing
                       member.
    :type origanizer: :class:`pooldlib.postgresql.models.User`
    :param name: The name of the campaign
    :type name: string
    :param description: The description of the campaign
    :type description: string
    :param start: Active start datetime for the campaign in UTC, defaults
                  to `datetime.utcnow`
    :type start: :class:`datetime.datetime`
    :param end: Active end datetime for the campaign in UTC, optional
    :type end: :class:`datetime.datetime` or `None`
    :param kwargs: Metadata to associate with the new campaign.
    :type kwargs: kwarg dictionary

    :returns: :class:`pooldlib.postgresql.models.Campaign`
    """
    campaign = CampaignModel()
    campaign.name = name
    campaign.description = description
    campaign.start = start or pytz.UTC.localize(datetime.utcnow())
    campaign.end = end

    with transaction_session() as session:
        session.add(campaign)
        session.commit()

    meta = list()
    for (k, v) in kwargs.items():
        cm = CampaignMetaModel()
        cm.key = k
        cm.value = v
        cm.campaign_id = campaign.id
        meta.append(cm)

    with transaction_session(auto_commit=True) as session:
        for cm in meta:
            session.add(cm)

    associate_user(campaign, organizer, 'organizer', 'participating')
    return campaign
Beispiel #7
0
def create(organizer, name, description, start=None, end=None, **kwargs):
    """Create and return a new instance of
    :class:`pooldlib.postgresql.models.Campaign`.

    :param origanizer: The user to be classified as the campaign's organizing
                       member.
    :type origanizer: :class:`pooldlib.postgresql.models.User`
    :param name: The name of the campaign
    :type name: string
    :param description: The description of the campaign
    :type description: string
    :param start: Active start datetime for the campaign in UTC, defaults
                  to `datetime.utcnow`
    :type start: :class:`datetime.datetime`
    :param end: Active end datetime for the campaign in UTC, optional
    :type end: :class:`datetime.datetime` or `None`
    :param kwargs: Metadata to associate with the new campaign.
    :type kwargs: kwarg dictionary

    :returns: :class:`pooldlib.postgresql.models.Campaign`
    """
    campaign = CampaignModel()
    campaign.name = name
    campaign.description = description
    campaign.start = start or pytz.UTC.localize(datetime.utcnow())
    campaign.end = end

    with transaction_session() as session:
        session.add(campaign)
        session.commit()

    meta = list()
    for (k, v) in kwargs.items():
        cm = CampaignMetaModel()
        cm.key = k
        cm.value = v
        cm.campaign_id = campaign.id
        meta.append(cm)

    with transaction_session(auto_commit=True) as session:
        for cm in meta:
            session.add(cm)

    associate_user(campaign, organizer, 'organizer', 'participating')
    return campaign
Beispiel #8
0
def update(campaign, name=None, description=None, **kwargs):
    """Update name and description of a specified campaign.

    :param campaign: Campaign to update.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param name: If specified, the new name for the campaign.
    :type name: string
    :param name: If specified, the new description for the campaign.
    :type name: string
    :param kwargs: key-value pairs to associate with the `User` data model
                   instance as metadata.
    :type kwargs: kwarg dictionary

    :returns: :class:`pooldlib.postgresql.models.Campaign`
    """
    if name is not None:
        campaign.name = name
    if description is not None:
        campaign.description = description

    # TODO :: This is really inefficient, fix it. <*****@*****.**>
    # TODO :: This methodology is mirrored in the user API as well.
    update_meta = [m for m in campaign.metadata if m.key in kwargs]
    create_meta = [(k, v) for (k, v) in kwargs.items()
                   if not hasattr(campaign, k)]

    meta_delta = list()
    meta_remove = list()
    for campaign_meta in update_meta:
        value = kwargs[campaign_meta.key]
        if value is None:
            meta_remove.append(campaign_meta)
        else:
            campaign_meta.value = value
            meta_delta.append(campaign_meta)

    for (k, v) in create_meta:
        m = CampaignMetaModel()
        m.key = k
        m.value = v
        m.campaign_id = campaign.id
        meta_delta.append(m)

    with transaction_session() as session:
        session.add(campaign)
        session.commit()

        for m in meta_delta:
            session.add(m)
            session.flush()
        for m in meta_remove:
            session.delete(m)

        session.commit()

    return campaign
Beispiel #9
0
def update(campaign, name=None, description=None, **kwargs):
    """Update name and description of a specified campaign.

    :param campaign: Campaign to update.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param name: If specified, the new name for the campaign.
    :type name: string
    :param name: If specified, the new description for the campaign.
    :type name: string
    :param kwargs: key-value pairs to associate with the `User` data model
                   instance as metadata.
    :type kwargs: kwarg dictionary

    :returns: :class:`pooldlib.postgresql.models.Campaign`
    """
    if name is not None:
        campaign.name = name
    if description is not None:
        campaign.description = description

    # TODO :: This is really inefficient, fix it. <*****@*****.**>
    # TODO :: This methodology is mirrored in the user API as well.
    update_meta = [m for m in campaign.metadata if m.key in kwargs]
    create_meta = [(k, v) for (k, v) in kwargs.items() if not hasattr(campaign, k)]

    meta_delta = list()
    meta_remove = list()
    for campaign_meta in update_meta:
        value = kwargs[campaign_meta.key]
        if value is None:
            meta_remove.append(campaign_meta)
        else:
            campaign_meta.value = value
            meta_delta.append(campaign_meta)

    for (k, v) in create_meta:
        m = CampaignMetaModel()
        m.key = k
        m.value = v
        m.campaign_id = campaign.id
        meta_delta.append(m)

    with transaction_session() as session:
        session.add(campaign)
        session.commit()

        for m in meta_delta:
            session.add(m)
            session.flush()
        for m in meta_remove:
            session.delete(m)

        session.commit()

    return campaign
Beispiel #10
0
def associate_user(campaign, user, role, goal_participation, pledge=None):
    """Associate a user with a campaign filling a specified role.

    :param campaign: The campaign with which to associate the user.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user for which to create the association.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param role: The role to assign the user (either `organizer` or `participant`)
    :type role: string
    :param goal_participation: The participation description for the campaign goals.
                               One of `participating`, `nonparticipating`, `opted-in` or `opted-out`
                               (See :func:`pooldlib.api.campaign.associate_user_with_goal`)
    :type goal_participation: string

    :raises: :class:`pooldlib.exceptions.InvalidUserRoleError`
             :class:`pooldlib.exceptions.DuplicateCampaignUserAssociationError`

    :return: :class:`pooldlib.postgresql.models.CampaignAssociation`
    """
    # NOTE :: We intentionally associate the user with all existing goals, not just
    # NOTE :: active ones. Date stamps can distinguish users who joined prior to goal
    # NOTE :: becoming inactive.
    campaign_goals = goals(campaign, filter_inactive=False)
    ca = CampaignAssociationModel()
    ca.enabled = True
    ca.campaign = campaign
    ca.user = user
    ca.role = role
    goal_pledge = None
    if pledge is not None:
        ca.pledge = pledge
        if campaign_goals:
            goal_pledge = pledge / len(campaign_goals)
    for goal in campaign_goals:
        associate_user_with_goal(goal, user, goal_participation, pledge=goal_pledge)

    # Check to see if this user was invited and mark them as accepted
    update_invitee = None
    for invitee in campaign.invitees:
        if invitee.user == user or invitee.email == user.email:
            invitee.accepted = pytz.UTC.localize(datetime.utcnow())
            invitee.user = user
            update_invitee = invitee

    with transaction_session() as session:
        session.add(ca)
        if update_invitee is not None:
            session.add(update_invitee)
        try:
            session.commit()
        except SQLAlchemyDataError:
            raise InvalidUserRoleError()
        except SQLAlchemyIntegrityError:
            raise DuplicateCampaignUserAssociationError()
    return ca
Beispiel #11
0
def disable(user):
    """Disable a specific instance of the User data model. This will prevent
    the user from being returned by calls to :func:`pooldlib.api.user.get`
    and any further updates to the user being allowed.

    :param user: User which to disable.
    :type user: :class:`pooldlib.postgresql.models.User`
    """
    user.update_field('enabled', False)
    with transaction_session(auto_commit=True) as session:
        session.add(user)
Beispiel #12
0
def disable(campaign):
    """Disable a specified campaign. This will prevent it from being returned
    by calls to :func:`pooldlib.api.campaign.get` and
    :func:`pooldlib.api.campaign.campaigns`

    :param campaign: The campaign to disable.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    """
    campaign.enabled = False
    with transaction_session() as session:
        session.add(campaign)
        session.commit()
Beispiel #13
0
def disable(campaign):
    """Disable a specified campaign. This will prevent it from being returned
    by calls to :func:`pooldlib.api.campaign.get` and
    :func:`pooldlib.api.campaign.campaigns`

    :param campaign: The campaign to disable.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    """
    campaign.enabled = False
    with transaction_session() as session:
        session.add(campaign)
        session.commit()
Beispiel #14
0
def create_for_campaign(campaign, currency):
    b = BalanceModel()
    b.enabled = True
    b.amount = Decimal('0.0000')
    b.currency = currency
    b.campaign = campaign
    b.type = 'campaign'

    with transaction_session() as session:
        session.add(b)
        session.commit()
    return b
Beispiel #15
0
def create_for_user(user, currency):
    b = BalanceModel()
    b.enabled = True
    b.amount = Decimal('0.0000')
    b.currency = currency
    b.user = user
    b.type = 'user'

    with transaction_session() as session:
        session.add(b)
        session.commit()
    return b
Beispiel #16
0
def update_user_association(campaign,
                            user,
                            role=None,
                            pledge=None,
                            only_active_goals=True):
    """Update an existing User/Campaign association to change the
    specified user's role in the campaign.

    :param campaign: The campaign to which the user is associated.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user for which to update a campaign association.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param role: The role to assign the user, either `organizer` or `participant` (optional).
    :type role: string
    :param pledge: The amount the user has pledged to the campaign (optional).
    :type pledge: Decimal

    :raises: :class:`pooldlib.exceptions.InvalidUserRoleError`
             :class:`pooldlib.exceptions.UnknownCampaignAssociationError`

    :return: :class:`pooldlib.postgresql.models.CampaignAssociation`
    """
    ca = get_associations(campaign, user=user)
    if not ca:
        msg = "User %s is not associated with campaign %s. Please create "\
              "one with campaign.associate_user()."
        raise UnknownCampaignAssociationError(msg)
    ca = ca[0]
    updated = False
    if role is not None:
        updated = ca.update_field('role', role)
    if pledge is not None:
        if ca.pledge is not None:
            msg = "User %s has previously contributed to campaign '%s'"
            msg %= (user.username, campaign.name)
            raise PreviousUserContributionError(msg)
        updated = ca.update_field('pledge', pledge)
        goal_pledge = None
        campaign_goals = goals(campaign, filter_inactive=only_active_goals)
        if campaign_goals:
            goal_pledge = pledge / len(campaign_goals)
        for goal in campaign_goals:
            update_user_goal_association(goal, user, pledge=goal_pledge)

    if updated:
        with transaction_session() as session:
            try:
                session.commit()
            except SQLAlchemyIntegrityError:
                raise InvalidUserRoleError()
Beispiel #17
0
def reset_password(user):
    """Generate a new, random, password for a specific ``User`` data model
    instance.

    :param user: User for which to reset password.
    :type user: :class:`pooldlib.postgresql.models.User`

    :returns: The new password for the User data model instance as a ``string``
    """
    newpass = alphanumeric_string(alpha_count=8, number_count=2)
    user.password = newpass
    with transaction_session(auto_commit=True) as session:
        session.add(user)
    return newpass
Beispiel #18
0
def update_user_association(campaign, user, role=None, pledge=None, only_active_goals=True):
    """Update an existing User/Campaign association to change the
    specified user's role in the campaign.

    :param campaign: The campaign to which the user is associated.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user for which to update a campaign association.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param role: The role to assign the user, either `organizer` or `participant` (optional).
    :type role: string
    :param pledge: The amount the user has pledged to the campaign (optional).
    :type pledge: Decimal

    :raises: :class:`pooldlib.exceptions.InvalidUserRoleError`
             :class:`pooldlib.exceptions.UnknownCampaignAssociationError`

    :return: :class:`pooldlib.postgresql.models.CampaignAssociation`
    """
    ca = get_associations(campaign, user=user)
    if not ca:
        msg = "User %s is not associated with campaign %s. Please create "\
              "one with campaign.associate_user()."
        raise UnknownCampaignAssociationError(msg)
    ca = ca[0]
    updated = False
    if role is not None:
        updated = ca.update_field('role', role)
    if pledge is not None:
        if ca.pledge is not None:
            msg = "User %s has previously contributed to campaign '%s'"
            msg %= (user.username, campaign.name)
            raise PreviousUserContributionError(msg)
        updated = ca.update_field('pledge', pledge)
        goal_pledge = None
        campaign_goals = goals(campaign, filter_inactive=only_active_goals)
        if campaign_goals:
            goal_pledge = pledge / len(campaign_goals)
        for goal in campaign_goals:
            update_user_goal_association(goal, user, pledge=goal_pledge)

    if updated:
        with transaction_session() as session:
            try:
                session.commit()
            except SQLAlchemyIntegrityError:
                raise InvalidUserRoleError()
Beispiel #19
0
    def execute(self):
        """Atomically execute the full transact list.

        :raises: InsufficentFundsTransferError
        """
        if self._errors:
            exc, msg = self._errors.pop()
            self.reset()
            raise exc(msg)

        with transaction_session(auto_commit=True) as session:
            for xfer_class_values in self._transfers.values():
                for xfer in xfer_class_values.values():
                    session.add(xfer)
            session.flush()

            for txn_class_values in self._transactions.values():
                for txn in txn_class_values.values():
                    session.add(txn)
            session.flush()
Beispiel #20
0
    def execute(self):
        """Atomically execute the full transact list.

        :raises: InsufficentFundsTransferError
        """
        if self._errors:
            exc, msg = self._errors.pop()
            self.reset()
            raise exc(msg)

        with transaction_session(auto_commit=True) as session:
            for xfer_class_values in self._transfers.values():
                for xfer in xfer_class_values.values():
                    session.add(xfer)
            session.flush()

            for txn_class_values in self._transactions.values():
                for txn in txn_class_values.values():
                    session.add(txn)
            session.flush()
Beispiel #21
0
def disassociate_user(campaign, user):
    """Remove the association between a User and a Campaign.

    :param campaign: The campaign to which the user is associated.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user for which to remove a campaign association.
    :type user: :class:`pooldlib.postgresql.models.User`

    :raises: :class:`pooldlib.exceptions.UnknownCampaignAssociationError`

    :return: :class:`pooldlib.postgresql.models.CampaignAssociation`
    """
    ca = get_associations(campaign, user=user)
    if not ca:
        msg = "User %s is not associated with campaign %s. Please create "\
              "one with campaign.associate_user()."
        raise UnknownCampaignAssociationError(msg)
    ca = ca[0]
    with transaction_session() as session:
        session.delete(ca)
        session.commit()
Beispiel #22
0
def disassociate_user(campaign, user):
    """Remove the association between a User and a Campaign.

    :param campaign: The campaign to which the user is associated.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user for which to remove a campaign association.
    :type user: :class:`pooldlib.postgresql.models.User`

    :raises: :class:`pooldlib.exceptions.UnknownCampaignAssociationError`

    :return: :class:`pooldlib.postgresql.models.CampaignAssociation`
    """
    ca = get_associations(campaign, user=user)
    if not ca:
        msg = "User %s is not associated with campaign %s. Please create "\
              "one with campaign.associate_user()."
        raise UnknownCampaignAssociationError(msg)
    ca = ca[0]
    with transaction_session() as session:
        session.delete(ca)
        session.commit()
Beispiel #23
0
def update_user_goal_association(campaign_goal,
                                 user,
                                 participation=None,
                                 pledge=None):
    """Update a given user's association with ``campaign_goal``. The association will be
    described by ``participation``, which can be one of 'opted-in' and 'opted-out', 'participating',
    'nonparticipating'. The values 'participating' and 'nonparticipating' should only be used
    for default participation descriptions.  If a use opts to change their participation, use
    appropriate descriptor of 'opted-in' or 'opted-out'. These rules should be followed precisely
    for reporting purposes.

    :raises: :class:`pooldlib.exceptions.InvalidGoalParticipationNameError`
    """
    cga = CampaignGoalAssociationModel.query.filter_by(campaign=campaign_goal.campaign)\
                                            .filter_by(campaign_goal=campaign_goal)\
                                            .filter_by(user=user)\
                                            .first()
    if not cga:
        msg = "User %s is not associated with goals for campaign %s. Please create "\
              "one with campaign.associate_user()."
        raise UnknownCampaignGoalAssociationError(msg)

    updated = False
    if participation is not None:
        updated = cga.update_field('participation', participation)
    if pledge is not None:
        if cga.pledge is not None:
            msg = "User %s has previously contributed to campaign '%s'"
            msg %= (user.username, campaign_goal.campaign.name)
            raise PreviousUserContributionError(msg)
        updated = cga.update_field('pledge', pledge)

    if updated:
        with transaction_session() as session:
            session.add(cga)
            try:
                session.commit()
            except SQLAlchemyDataError:
                raise InvalidGoalParticipationNameError()
    return cga
Beispiel #24
0
def set_password(user, password):
    """Reset a user's password. The new password must conform to the user
    password requirements as defined in the doc string for
    :func:`pooldlib.api.user.validate_password`.


    :param user: User for which to update password.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param password: The new password to associate with the user.
    :type password: string

    :raises: :class:`pooldlib.exceptions.InvalidPasswordError`

    :returns: :class:`pooldlib.postgresql.models.User`
    """
    validate_password(password, exception_on_invalid=True)

    user.update_field('password', password)
    with transaction_session(auto_commit=True) as session:
        session.add(user)

    return user
Beispiel #25
0
def add_invite(campaign, email):
    from pooldlib.api import user
    usr = user.get_by_email(email)

    q = InviteeModel.query.filter_by(campaign_id=campaign.id)
    if usr is not None:
        q = q.filter_by(user_id=usr.id)
    else:
        q = q.filter_by(email=email)
    existing_invite = q.first()
    if existing_invite is not None:
        return None

    invite = InviteeModel()
    invite.email = email
    invite.campaign_id = campaign.id
    if usr is not None:
        invite.user_id = usr.id

    with transaction_session() as session:
        session.add(invite)
        session.commit()
    return invite
Beispiel #26
0
def add_invite(campaign, email):
    from pooldlib.api import user
    usr = user.get_by_email(email)

    q = InviteeModel.query.filter_by(campaign_id=campaign.id)
    if usr is not None:
        q = q.filter_by(user_id=usr.id)
    else:
        q = q.filter_by(email=email)
    existing_invite = q.first()
    if existing_invite is not None:
        return None

    invite = InviteeModel()
    invite.email = email
    invite.campaign_id = campaign.id
    if usr is not None:
        invite.user_id = usr.id

    with transaction_session() as session:
        session.add(invite)
        session.commit()
    return invite
Beispiel #27
0
def update_user_goal_association(campaign_goal, user, participation=None, pledge=None):
    """Update a given user's association with ``campaign_goal``. The association will be
    described by ``participation``, which can be one of 'opted-in' and 'opted-out', 'participating',
    'nonparticipating'. The values 'participating' and 'nonparticipating' should only be used
    for default participation descriptions.  If a use opts to change their participation, use
    appropriate descriptor of 'opted-in' or 'opted-out'. These rules should be followed precisely
    for reporting purposes.

    :raises: :class:`pooldlib.exceptions.InvalidGoalParticipationNameError`
    """
    cga = CampaignGoalAssociationModel.query.filter_by(campaign=campaign_goal.campaign)\
                                            .filter_by(campaign_goal=campaign_goal)\
                                            .filter_by(user=user)\
                                            .first()
    if not cga:
        msg = "User %s is not associated with goals for campaign %s. Please create "\
              "one with campaign.associate_user()."
        raise UnknownCampaignGoalAssociationError(msg)

    updated = False
    if participation is not None:
        updated = cga.update_field('participation', participation)
    if pledge is not None:
        if cga.pledge is not None:
            msg = "User %s has previously contributed to campaign '%s'"
            msg %= (user.username, campaign_goal.campaign.name)
            raise PreviousUserContributionError(msg)
        updated = cga.update_field('pledge', pledge)

    if updated:
        with transaction_session() as session:
            session.add(cga)
            try:
                session.commit()
            except SQLAlchemyDataError:
                raise InvalidGoalParticipationNameError()
    return cga
Beispiel #28
0
def associate_user_with_goal(campaign_goal, user, participation, pledge=None):
    """Associate given user with ``campaign_goal``. The association will be described by
    ``participation``, which can be one of 'opted-in', 'opted-out', 'participating',
    'nonparticipating'. The values 'participating' and 'nonparticipating' should only be used
    for default participation descriptions.  If a use opts to change their participation, use
    appropriate descriptor of 'opted-in' or 'opted-out'. These rules should be followed precisely
    for reporting purposes.

    :param campaign: The campaign which to associate the user.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user to associate.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param role: The role to assign the user (either `organizer` or `participant`)
    :type role: string
    :param pledge: The amount the user has pledged to the campaign (optional).
    :type pledge: Decimal

    :raises: :class:`pooldlib.exceptions.InvalidGoalParticipationNameError`
             :class:`pooldlib.exceptions.DuplicateCampaignGoalUserAssociationError`
    """
    cga = CampaignGoalAssociationModel()
    cga.campaign_id = campaign_goal.campaign.id
    cga.campaign_goal = campaign_goal
    cga.user = user
    cga.participation = participation
    cga.pledge = pledge

    with transaction_session() as session:
        session.add(cga)
        try:
            session.commit()
        except SQLAlchemyDataError:
            raise InvalidGoalParticipationNameError()
        except SQLAlchemyIntegrityError:
            raise DuplicateCampaignGoalUserAssociationError()
    return cga
Beispiel #29
0
def associate_user_with_goal(campaign_goal, user, participation, pledge=None):
    """Associate given user with ``campaign_goal``. The association will be described by
    ``participation``, which can be one of 'opted-in', 'opted-out', 'participating',
    'nonparticipating'. The values 'participating' and 'nonparticipating' should only be used
    for default participation descriptions.  If a use opts to change their participation, use
    appropriate descriptor of 'opted-in' or 'opted-out'. These rules should be followed precisely
    for reporting purposes.

    :param campaign: The campaign which to associate the user.
    :type campaign: :class:`pooldlib.postgresql.models.Campaign`
    :param user: The user to associate.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param role: The role to assign the user (either `organizer` or `participant`)
    :type role: string
    :param pledge: The amount the user has pledged to the campaign (optional).
    :type pledge: Decimal

    :raises: :class:`pooldlib.exceptions.InvalidGoalParticipationNameError`
             :class:`pooldlib.exceptions.DuplicateCampaignGoalUserAssociationError`
    """
    cga = CampaignGoalAssociationModel()
    cga.campaign_id = campaign_goal.campaign.id
    cga.campaign_goal = campaign_goal
    cga.user = user
    cga.participation = participation
    cga.pledge = pledge

    with transaction_session() as session:
        session.add(cga)
        try:
            session.commit()
        except SQLAlchemyDataError:
            raise InvalidGoalParticipationNameError()
        except SQLAlchemyIntegrityError:
            raise DuplicateCampaignGoalUserAssociationError()
    return cga
Beispiel #30
0
def update(user, username=None, name=None, password=None, **kwargs):
    """Update properties of a specific User data model instance.  Any
    unspecified keyword arguments will be assumed to be metadata. Existing
    metadata will be updated to the newly supplied value, and any new metadata
    keys will be associated with the user.
    :func:`pooldlib.api.user.reset_password` can be used to update
    a users password as well. To delete a user's metadata, pass ``None``
    as the value for the to be deleted key in the kwarg key-value pair.
    The ``name`` property of the User model can be cleared by passing
    an empty string ('') as the value for the name kwarg.

    :param user: User for which to update ``User`` model data.
    :type user: :class:`pooldlib.postgresql.models.User`
    :param username: New username to associate with `User` instance.
    :type username: string
    :param name: New name to associate with `User` data model instance.
    :type name: string
    :param kwargs: key-value pairs to associate with the `User` data model
                   instance as metadata.
    :type kwargs: kwarg dictionary

    :raises: :class:`pooldlib.exceptions.InvalidPasswordError`

    :returns: :class:`pooldlib.postgresql.models.User`
    """
    if username:
        user.update_field('username', username)
    if name is not None:
        name = None if name == '' else name
        user.update_field('name', name, nullable=True)
    if password:
        validate_password(password, exception_on_invalid=True)
        user.update_field('password', password)

    if 'email' in kwargs:
        if email_exists(kwargs['email'], user=user):
            msg = 'The email address %s is already assigned to another user.'
            msg %= kwargs['email']
            raise EmailUnavailableError(msg)
        kwargs['email'] = kwargs['email'].lower()

    update_meta = [m for m in user.metadata if m.key in kwargs]
    create_meta = [(k, v) for (k, v) in kwargs.items() if not hasattr(user, k)]

    meta_delta = list()
    meta_remove = list()
    for user_meta in update_meta:
        value = kwargs[user_meta.key]
        if value is None:
            meta_remove.append(user_meta)
        else:
            user_meta.value = value
            meta_delta.append(user_meta)

    for (k, v) in create_meta:
        m = UserMetaModel()
        m.key = k
        m.value = v
        m.user = user
        meta_delta.append(m)

    with transaction_session() as session:
        # Technically not needed, but gives the context content
        session.add(user)

        try:
            session.flush()
        except SQLAlchemyIntegrityError, e:
            if username is not None:
                msg = "Username %s already in use." % username
                raise UsernameUnavailableError(msg)
            raise e

        for m in meta_delta:
            session.add(m)
            session.flush()
        for m in meta_remove:
            session.delete(m)

        session.commit()
Beispiel #31
0
def update_goal(update_goal,
                name=None,
                predecessor=None,
                description=None,
                start=None,
                end=None,
                **kwargs):
    """Update an existing goal for a campaign. Only ``goal`` is required. All
    given goal properties will be updated. Any unspecified keyword arguments will
    be used to update the goal's metadata. To delete metadata for a campaign goal,
    pass ``None`` as the value for the to be deleted key in the kwarg key-value pair.

    :param update_goal: Identifier for the target campaign.
    :type update_goal: :class:`pooldlib.postgresql.models.Goal`
    :param name: The name of the newly created campaign goal.
    :type name: string
    :param description: The description of the newly created campaign goal.
    :type name: string
    :param kwargs: Keyword arguments consisting of key-value pairs to be added to the
                   newly created goal as metadata.
    :type kwargs: unspecified keyword arguments to the function.
    """
    if name is not None:
        update_goal.name = name
    if description is not None:
        update_goal.description = description
    if start is not None:
        update_goal.start = start
    if end is not None:
        update_goal.end = end
    if predecessor is not None:
        goal.predecessor_id = predecessor.id

    update_meta = [m for m in update_goal.metadata if m.key in kwargs]
    create_meta = [(k, v) for (k, v) in kwargs.items()
                   if not hasattr(update_goal, k)]

    meta_delta = list()
    meta_remove = list()
    for goal_meta in update_meta:
        value = kwargs[m.key]
        if value is None:
            meta_remove.append(goal_meta)
        else:
            goal_meta.value = value
            meta_delta.append(goal_meta)

    for (k, v) in create_meta:
        goal_meta = CampaignGoalMetaModel()
        goal_meta.key = k
        goal_meta.value = v
        goal_meta.campaign_goal = update_goal
        meta_delta.append(goal_meta)

    with transaction_session() as session:
        session.add(update_goal)  # Technically not needed
        session.flush()

        for goal_meta in meta_delta:
            session.add(goal_meta)
        for goal_meta in meta_remove:
            session.delete(goal_meta)

        session.commit()

    return update_goal