Example #1
0
class Checkin(Base):
    """checkin for event"""

    __tablename__ = 'checkin'

    authorizer_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    event_id = db.Column(db.Integer, db.ForeignKey('event.id'))

    @property
    def authorizer(self):
        """authorizing user"""
        return User.query.get(self.authorizer_id)
Example #2
0
class Membership(Base):
    """user membership in group"""

    __tablename__ = 'membership'

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    role_id = db.Column(db.Integer, db.ForeignKey('group_role.id'))

    def signups(self):
        """Returns signups for this membership"""
        return Signup.query.join(Event).filter(
            Event.group_id==self.group_id,
            Signup.user_id==self.user_id,
            Signup.is_active==True).all()

    def save(self):
        """save membership"""
        if not Membership.query.filter(
            Membership.user_id == self.user_id,
            Membership.group_id == self.group_id
        ).one_or_none():
            return super(Membership, self).save()
        return self

    @property
    def group(self):
        return Group.query.get(self.group_id)

    @property
    def user(self):
        return User.query.get(self.user_id)

    @property
    def role(self):
        return GroupRole.query.get(self.role_id)
Example #3
0
class Signup(Base):
    """user signup for event"""

    __tablename__ = 'signup'

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    event_id = db.Column(db.Integer, db.ForeignKey('event.id'))
    role_id = db.Column(db.Integer, db.ForeignKey('event_role.id'))
    category = db.Column(db.String(50))
    status = db.Column(db.String(50))
    preference = db.Column(db.Integer)
    comment = db.Column(db.Text)

    @property
    def event(self):
        return Event.query.get(self.event_id)

    @property
    def user(self):
        return User.query.get(self.user_id)

    @property
    def role(self):
        return EventRole.query.get(self.role_id)

    @property
    def is_checked_in(self):
        return Checkin.query.filter_by(
            user_id=self.user_id,
            event_id=self.event_id).count() > 0

    @property
    def num_check_ins(self):
        return Checkin.query.filter_by(
            user_id=self.user_id,
            event_id=self.event_id).count()

    @classmethod
    def from_csv_string(cls, string, override=False):
        """Import signups from csv"""
        try:
            reader = csv.reader(string.splitlines(), delimiter=',')
            headers = [s.strip() for s in next(reader)]
            for row in reader:
                if ''.join(row).strip() == '':
                    continue
                data = dict(zip(headers, [s.strip() for s in row]))
                user_data = {}
                for k in list(data.keys()):
                    if k.startswith('user_'):
                        user_data[k[5:]] = data.pop(k)
                user = User.get_or_create(email=user_data['email'], data=user_data)
                event_id = data.pop('event_id', None)
                event_ids = data.pop('event_ids', None)
                if event_ids:
                    event_ids = [int(s.strip()) for s in event_ids[1:-1].split('|') if s]
                elif event_id:
                    event_ids = [event_id]
                else:
                    raise UserWarning('Must specify an event_id or event_ids col.')
                for event_id in event_ids:
                    event = Event.query.get(event_id)
                    role = EventRole.query.filter_by(name=event.setting('role').value, event_id=event_id).one()
                    data['is_active'] = True
                    yield Signup.get_or_create(user_id=user.id, event_id=event_id, override=override, role_id=role.id, data=data)
        except sqlalchemy.exc.IntegrityError:
            raise UserWarning('Invalid event_id found. Check that all event_ids are associated with valid events.')
Example #4
0
class Event(Base):
    """PIAP event"""

    __tablename__ = 'event'
    __settingclass__ = EventSetting
    __defaultsettings__ = default_event_settings

    name = db.Column(db.Text, nullable=False)
    description = db.Column(db.Text)
    start = db.Column(ArrowType)
    end = db.Column(ArrowType)
    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
    parent_id = db.Column(db.Integer, db.ForeignKey('event.id'))
    google_id = db.Column(db.String(50), unique=True)
    settings = relationship("EventSetting", backref="event")
    checkins = relationship("Checkin", backref="event")
    frequency = db.Column(db.String(20))
    on_mondays = db.Column(db.Boolean)
    on_tuesdays = db.Column(db.Boolean)
    on_wednesdays = db.Column(db.Boolean)
    on_thursdays = db.Column(db.Boolean)
    on_fridays = db.Column(db.Boolean)
    on_saturdays = db.Column(db.Boolean)
    on_sundays = db.Column(db.Boolean)
    until = db.Column(ArrowType)

    DAYS_OF_THE_WEEK = tuple(calendar.day_name[i][:3] for i in range(7))

    def __contains__(self, user):
        """Check if user is in signups"""
        if not user.is_authenticated:
            return False
        return Signup.query.filter_by(
            user_id=user.id,
            event_id=self.id,
            is_active=True
        ).one_or_none() is not None

    @property
    def group(self):
        return Group.query.get(self.group_id)

    @property
    def signups(self):
        """Returns all participants"""
        return Signup.query.filter_by(
            event_id=self.id,
            is_active=True).all()

    @property
    def num_signups(self):
        """number of signups"""
        return int(Signup.query.filter_by(
            event_id=self.id,
            is_active=True).count())

    @property
    def categories(self):
        """all event categories"""
        return [s.strip().split('(')[0] for s in g.event.setting('categories').value.split(',')] + ['Accepted', 'Waitlisted']

    @property
    def category_defaults(self):
        """all event categories"""
        counts = [('Accepted', 0), ('Waitlisted', 0)]
        for s in g.event.setting('categories').value.split(','):
            data = s.strip().split('(')
            if len(data) > 1:
                counts.append((data[0], int(data[1][:-1])))
            else:
                counts.append((data[0], 0))
        return counts

    @property
    def days_of_the_week_booleans(self):
        """Returns boolean values for days of the week."""
        return tuple((
            self.on_mondays,
            self.on_tuesdays,
            self.on_wednesdays,
            self.on_thursdays,
            self.on_fridays,
            self.on_saturdays,
            self.on_sundays))

    @property
    def days_of_the_week(self):
        """Returns list of days of the week."""
        return tuple(day for day, value in zip(
            Event.DAYS_OF_THE_WEEK,
            self.days_of_the_week_booleans) if value)

    @property
    def category_counts(self):
        """all event categories"""
        counts = []
        for s in g.event.setting('categories').value.split(',') + ('Accepted', 'Waitlisted'):
            category = s.strip().split('(')[0]
            counts.append((category, Signup.query.filter_by(
                category=category,
                is_active=True,
                event_id=self.id
            )))
        return counts

    @property
    def num_non_waitlisted_signups(self):
        """Number of active signups"""
        return Event.query.join(Signup).filter(
            Event.id == self.id,
            Event.start >= arrow.now(),
            Signup.category != 'Waitlisted',
            Signup.is_active == True
        ).count()

    @classmethod
    def range(cls, event_start, event_end, shift_duration, shift_alignment):
        """Returns an iterable of time spans"""
        def start(i=None):
            """generates iterable of time spans in shift_duration from start"""
            i = i or event_start
            j = i.replace(minutes=shift_duration)
            while i < event_end:
                if i >= event_end:
                    raise StopIteration
                if j > event_end:
                    j = event_end
                yield i, j
                i = j
                j = j.replace(minutes=shift_duration)

        def hour():
            """generates iterable of time spans per hour from start"""
            i, j = event_start, event_start.replace(minutes=shift_duration)
            if i.floor('hour') != i:
                j = event_start.replace(hours=1).floor('hour')
            yield i, j
            for i, j in start(j):
                yield i, j

        return locals()[shift_alignment.lower()]()

    @classmethod
    def split(cls, event_data, shift_duration, shift_alignment):
        """
        Splits and saves event as a series of shifts
        :param event_data: dictionary of event attributes
        :param shift_duration: time in minutes
        :param shift_alignment: HOUR, START, END
        """
        if shift_duration == 0:
            return Event(**event_data).save()
        return [Event(**event_data).update(start=i, end=j).save()
            for i, j in Event.range(
                event_data['start'], event_data['end'],
                shift_duration, shift_alignment)]

    @classmethod
    def from_parent(cls, parent, date=None):
        """
        Create a new event from the parent object.

        All child events do NOT contain recurrence information. Only the parent
        event does.

        Args:
            parent: the parent event, containing recurrence information
            date: the date to create the new event on, transfer only the time
            of day from the old event

        Returns:
            new event object, on the new day but with old times
        """
        start = parent.start
        end = parent.end
        if date:
            start = date.replace(
                hour=parent.start.hour,
                minute=parent.start.minute)
            end = start.replace(
                hour=parent.end.hour,
                minute=parent.end.minute)
        return parent.copy(
            parent_id=parent.id,
            start=start,
            end=end)

    @validates('frequency')
    def validate_frequency(self, _, address):
        """Check that frequency is an integer."""
        return int(address[0])

    def create_shift(self, yyyymmdd):
        """
        Creates a child event.

        Note this child event does NOT contain recurrence information.

        Args:
            yyyymmdd: A date formatted as YYYYMMDD (e.g., 20160902)

        Returns:
            A child event
        """
        return Event.from_parent(self, arrow.get(yyyymmdd, 'YYYYMMDD'))

    def copy(self, **kwargs):
        """Makes copy for newly-created shifts."""
        data = dict(
            name=self.name,
            description=self.description,
            group_id=self.group_id)
        data.update(kwargs)
        return Event(**data)

    def get_shift_or_none(self, date):
        """Get shift for the provided date."""
        current_day = date.replace(hour=0, minutes=0)
        next_day = current_day.replace(days=+1, seconds=-1)
        return Event.query.filter(
            Event.parent_id == self.id,
            Event.start >= current_day,
            Event.start <= next_day).one_or_none()

    def update(self, **kwargs):
        """Intercept updates to object."""
        if 'days_of_the_week' in kwargs:
            dotw = kwargs.pop('days_of_the_week')
            self.set_byday(*[day in dotw for day in Event.DAYS_OF_THE_WEEK])
        return super().update(**kwargs)

    def set_byday(
            self,
            on_mondays: bool=False,
            on_tuesdays: bool=False,
            on_wednesdays: bool=False,
            on_thursdays: bool=False,
            on_fridays: bool=False,
            on_saturdays: bool=False,
            on_sundays: bool=False) -> int:
        """Pass in booleans representing the days for which an event happens.

        Converts list of booleans into a bit representation.
        """
        self.on_mondays = on_mondays
        self.on_tuesdays = on_tuesdays
        self.on_wednesdays = on_wednesdays
        self.on_thursdays = on_thursdays
        self.on_fridays = on_fridays
        self.on_saturdays = on_saturdays
        self.on_sundays = on_sundays

    def signups_by_category(self, **kwargs):
        """Returns all accepted participants"""
        if kwargs['category'] == '*':
            kwargs.pop('category')
        return Signup.query.filter_by(
            event_id=self.id,
            is_active=True,
            **kwargs).all()

    def split_existing(self, event_data, shift_duration, shift_alignment):
        """
        Splits and saves existing event
        :param event_data: dictionary of event attributes
        :param shift_duration: time in minutes
        :param shift_alignment: HOUR, START, END
        """
        if shift_duration == 0:
            return Event(**event_data).save()

        span_range = Event.range(
            event_data['start'], event_data['end'],
            shift_duration, shift_alignment)

        # modify existing event to match shift
        i, j = next(span_range)
        self.update(start=i, end=j).save()

        # create all other events
        event_data['parent_id'] = self.id
        event_data.pop('google_id', '')
        return [self] + [Event(**event_data).update(start=i, end=j).save()
            for i, j in span_range]
Example #5
0
class EventRole(Role):
    """roles for event membership"""

    __tablename__ = 'event_role'

    event_id = db.Column(db.Integer, db.ForeignKey('event.id'))
Example #6
0
class EventSetting(Setting):
    """settings for a PIAP event"""

    __tablename__ = 'event_setting'

    event_id = db.Column(db.Integer, db.ForeignKey('event.id'))
Example #7
0
class GroupRole(Role):
    """roles for group membership"""

    __tablename__ = 'group_role'

    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
Example #8
0
class GroupSetting(Setting):
    """settings for a PIAP group"""

    __tablename__ = 'group_setting'

    group_id = db.Column(db.Integer, db.ForeignKey('group.id'))
Example #9
0
class UserSetting(Setting):
    """settings for a PIAP user"""

    __tablename__ = 'user_setting'

    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))