예제 #1
0
    def defaults(self):

        if not self.id or self.allocation_stale:
            return dict()

        allocation = self.allocation

        start, end = self.start, self.end
        if not all((start, end)):
            start = allocation.display_start(
                timezone=settings.timezone()
            )
            end = allocation.display_end(
                timezone=settings.timezone()
            )

        return {
            'id': self.id,
            'start_time': start.time(),
            'end_time': end.time(),
            'day': start.date(),
            'quota': allocation.quota,
            'approve_manually': allocation.approve_manually,
            'whole_day': allocation.whole_day,
            'reservation_quota_limit': allocation.quota_limit
        }
예제 #2
0
    def urls(self, allocation):
        """Returns the options for the js contextmenu for the given allocation
        as well as other links associated with the event.

        """

        items = utils.EventUrls(self.context, self.request, exposure)

        start = utils.utctimestamp(
            allocation.display_start(settings.timezone()))
        end = utils.utctimestamp(allocation.display_end(settings.timezone()))

        items.move_url('edit-allocation', dict(id=allocation.id))

        # Reservation
        res_add = lambda n, v, p, t: \
            items.menu_add(_(u'Reservations'), n, v, p, t)
        if allocation.is_separate:
            res_add(_(u'Reserve'), 'reserve',
                    dict(id=allocation.id, start=start, end=end), 'overlay')
            items.default_url('reserve',
                              dict(id=allocation.id, start=start, end=end))
        else:
            res_add(_(u'Reserve'), 'reserve-group',
                    dict(group=allocation.group), 'overlay')
            items.default_url('reserve', dict(group=allocation.group))

        res_add(_(u'Manage'), 'reservations', dict(group=allocation.group),
                'inpage')

        # menu entries for single items
        entry_add = lambda n, v, p, t: \
            items.menu_add(_('Entry'), n, v, p, t)

        entry_add(_(u'Edit'), 'edit-allocation', dict(id=allocation.id),
                  'overlay')

        entry_add(_(u'Remove'), 'remove-allocation', dict(id=allocation.id),
                  'overlay')

        if allocation.in_group:
            # menu entries for group items
            group_add = lambda n, v, p, t: \
                items.menu_add(_('Recurrences'), n, v, p, t)

            group_add(_(u'List'), 'group', dict(name=allocation.group),
                      'overlay')

            group_add(_(u'Remove'), 'remove-allocation',
                      dict(group=allocation.group), 'overlay')

        # view selections
        if 'agendaDay' in self.context.available_views:
            items.menu_add(
                _(u'Reservations'), _(u'Daily View'), 'view',
                dict(selected_view='agendaDay',
                     specific_date=allocation.start.strftime('%Y-%m-%d')),
                'window')

        return items
예제 #3
0
    def events(self):
        resource = self.context
        scheduler = resource.scheduler()
        translate = utils.translator(self.context, self.request)

        is_exposed = exposure.for_allocations([resource])

        # get an event for each exposed allocation
        events = []
        for alloc in scheduler.allocations_in_range(*self.range):

            if not is_exposed(alloc):
                continue

            start = alloc.display_start(settings.timezone())
            end = alloc.display_end(settings.timezone())

            # get the urls
            urls = self.urls(alloc)

            # calculate the availability for title and class
            availability, title, klass = utils.event_availability(
                resource, self.request, scheduler, alloc
            )

            if alloc.partly_available:
                partitions = alloc.availability_partitions()
            else:
                # if the allocation is not partly available there can only
                # be one partition meant to be shown as empty unless the
                # availability is zero
                partitions = [(100, availability == 0.0)]

            event_header = alloc.whole_day and translate(_(u'Whole Day'))

            events.append(dict(
                title=title,
                start=start.isoformat(),
                end=end.isoformat(),
                className=klass,
                url=urls.default,
                menu=urls.menu,
                menuorder=urls.order,
                allocation=alloc.id,
                partitions=partitions,
                group=alloc.group,
                allDay=False,
                moveurl=urls.move,
                header=event_header
            ))

        return events
예제 #4
0
    def events(self):
        resource = self.context
        scheduler = resource.scheduler()
        translate = utils.translator(self.context, self.request)

        is_exposed = exposure.for_allocations([resource])

        # get an event for each exposed allocation
        events = []
        for alloc in scheduler.allocations_in_range(*self.range):

            if not is_exposed(alloc):
                continue

            start = alloc.display_start(settings.timezone())
            end = alloc.display_end(settings.timezone())

            # get the urls
            urls = self.urls(alloc)

            # calculate the availability for title and class
            availability, title, klass = utils.event_availability(
                resource, self.request, scheduler, alloc)

            if alloc.partly_available:
                partitions = alloc.availability_partitions()
            else:
                # if the allocation is not partly available there can only
                # be one partition meant to be shown as empty unless the
                # availability is zero
                partitions = [(100, availability == 0.0)]

            event_header = alloc.whole_day and translate(_(u'Whole Day'))

            events.append(
                dict(title=title,
                     start=start.isoformat(),
                     end=end.isoformat(),
                     className=klass,
                     url=urls.default,
                     menu=urls.menu,
                     menuorder=urls.order,
                     allocation=alloc.id,
                     partitions=partitions,
                     group=alloc.group,
                     allDay=False,
                     moveurl=urls.move,
                     header=event_header))

        return events
예제 #5
0
    def inject_missing_data(self, data, allocation=None):
        """ Adds the date and start-/end-time to the data if they are missing,
        which happens because those fields may be disabled and therefore are
        not transferred when submitting the form.

        The fields are injected into the passed dictionary, which may be
        the reservations defaults or the submitted form data.

        """
        extracted, errors = self.extractData(setErrors=False)

        # the extracted fields may contain field values which need to be
        # injected so the defaults are filled - otherwise no value is updated
        # on the disabled field
        for f in ("day", "start_time", "end_time"):
            if extracted.get(f) is not None:
                data[f] = extracted[f]

        # if the extracted data was not of any help the id of the allocation
        # is our last resort.
        try:
            allocation = allocation or self.allocation(data["id"])
        except DirtyReadOnlySession:
            return

        tz = settings.timezone()

        if data.get("day") is None:
            data["day"] = allocation.display_start(tz).date()

        if data.get("start_time") is None:
            data["start_time"] = allocation.display_start(tz).time()

        if data.get("end_time") is None:
            data["end_time"] = allocation.display_end(tz).time()
예제 #6
0
    def inject_missing_data(self, data, allocation=None):
        """ Adds the date and start-/end-time to the data if they are missing,
        which happens because those fields may be disabled and therefore are
        not transferred when submitting the form.

        The fields are injected into the passed dictionary, which may be
        the reservations defaults or the submitted form data.

        """
        extracted, errors = self.extractData(setErrors=False)

        # the extracted fields may contain field values which need to be
        # injected so the defaults are filled - otherwise no value is updated
        # on the disabled field
        for f in ('day', 'start_time', 'end_time'):
            if extracted.get(f) is not None:
                data[f] = extracted[f]

        # if the extracted data was not of any help the id of the allocation
        # is our last resort.
        try:
            allocation = allocation or self.allocation(data['id'])
        except DirtyReadOnlySession:
            return

        tz = settings.timezone()

        if data.get('day') is None:
            data['day'] = allocation.display_start(tz).date()

        if data.get('start_time') is None:
            data['start_time'] = allocation.display_start(tz).time()

        if data.get('end_time') is None:
            data['end_time'] = allocation.display_end(tz).time()
예제 #7
0
    def scheduler(self, language=None):
        uuid = utils.string_uuid(self.uuid())

        libres_util = getUtility(ILibresUtility)
        libres_util.context.set_service(
            'exposure',
            lambda ctx: LibresExposure(exposure.for_allocations([uuid])))

        return libres_util.scheduler(uuid, settings.timezone().zone)
예제 #8
0
    def scheduler(self, language=None):
        uuid = utils.string_uuid(self.uuid())

        libres_util = getUtility(ILibresUtility)
        libres_util.context.set_service(
            'exposure',
            lambda ctx: LibresExposure(exposure.for_allocations([uuid]))
        )

        return libres_util.scheduler(uuid, settings.timezone().zone)
예제 #9
0
    def defaults(self):
        parent = super(ReservationEditTimeForm, self).defaults()

        if self.reservation:
            tz = settings.timezone()

            parent.update(
                {
                    "start_time": self.reservation.display_start(tz).time(),
                    "end_time": self.reservation.display_end(tz).time(),
                }
            )

        return parent
예제 #10
0
    def defaults(self):
        parent = super(ReservationEditTimeForm, self).defaults()

        if self.reservation:
            tz = settings.timezone()

            parent.update({
                'start_time':
                self.reservation.display_start(tz).time(),
                'end_time':
                self.reservation.display_end(tz).time()
            })

        return parent
예제 #11
0
def monthly_report(year, month, resources, reservations='*'):

    titles = dict()

    for uuid in resources.keys():
        titles[uuid] = utils.get_resource_title(resources[uuid])

    # timezone of the report
    timezone = settings.timezone()

    # this order is used for every day in the month
    ordered_uuids = [i[0] for i in sorted(titles.items(), key=lambda i: i[1])]

    # build the hierarchical structure of the report data
    report = utils.OrderedDict()
    last_day = 28

    for d in sorted((d for d in calendar.itermonthdates(year, month))):
        if not d.month == month:
            continue

        day = d.day
        last_day = max(last_day, day)
        report[day] = utils.OrderedDict()

        for uuid in ordered_uuids:
            report[day][uuid] = dict()
            report[day][uuid][u'title'] = titles[uuid]
            report[day][uuid][u'approved'] = list()
            report[day][uuid][u'pending'] = list()
            report[day][uuid][u'url'] = resources[uuid].absolute_url()
            report[day][uuid][u'lists'] = {
                u'approved': _(u'Approved'),
                u'pending': _(u'Pending'),
            }

    # gather the reservations with as much bulk loading as possible
    period_start = datetime(year, month, 1, tzinfo=timezone)
    period_end = datetime(year, month, last_day, tzinfo=timezone)
    period_end += timedelta(days=1, microseconds=-1)

    # get a list of relevant allocations in the given period
    query = Session().query(Allocation)
    query = query.filter(period_start <= Allocation._start)
    query = query.filter(Allocation._start <= period_end)
    query = query.filter(Allocation.resource == Allocation.mirror_of)
    query = query.filter(Allocation.resource.in_(resources.keys()))

    allocations = query.all()

    # quit if there are no allocations at this point
    if not allocations:
        return {}

    # store by group as it will be needed multiple times over later
    groups = dict()
    for allocation in allocations:
        groups.setdefault(allocation.group, list()).append(allocation)

    # using the groups get the relevant reservations
    query = Session().query(Reservation)
    query = query.filter(Reservation.target.in_(groups.keys()))

    if reservations != '*':
        query = query.filter(Reservation.token.in_(reservations))

    query = query.order_by(Reservation.status)

    reservations = query.all()

    @utils.memoize
    def json_timespans(start, end):
        return json.dumps([dict(start=start, end=end)])

    used_days = dict([(i, False) for i in range(1, 32)])
    timezone = settings.timezone()

    def add_reservation(start, end, reservation):
        day = start.day

        used_days[day] = True

        end += timedelta(microseconds=1)

        start = sedate.to_timezone(start, timezone=timezone)
        end = sedate.to_timezone(end, timezone=timezone)

        start = utils.localize_date(start, time_only=True)
        end = utils.localize_date(end, time_only=True)

        context = resources[utils.string_uuid(reservation.resource)]

        reservation_lists = report[day][utils.string_uuid(
            reservation.resource
        )]
        reservation_lists[reservation.status].append(
            dict(
                start=start,
                end=end,
                email=reservation.email,
                data=reservation.data,
                timespans=json_timespans(start, end),
                id=reservation.id,
                token=reservation.token,
                quota=utils.get_reservation_quota_statement(reservation.quota),
                resource=context,
            )
        )

    for reservation in reservations:
        if reservation.target_type == u'allocation':
            add_reservation(reservation.start, reservation.end, reservation)
        else:
            for allocation in groups[reservation.target]:
                add_reservation(allocation.start, allocation.end, reservation)

    # remove unused days
    for day in report:
        if not used_days[day]:
            del report[day]

    return report
예제 #12
0
    def build_allocations_table(
        self, allocations, start_time=None, end_time=None
    ):
        """ Prepares the given allocations for the found-allocations table.
        Only works on IResourceBase contexts.
        """

        if not allocations:
            return []

        scheduler = self.context.scheduler()
        whole_day_text = self.translate(_(u'Whole day'))

        def get_time_text(start, end):
            if utils.whole_day(start, end):
                return whole_day_text
            else:
                return ' - '.join((
                    utils.localize_date(start, time_only=True),
                    utils.localize_date(end, time_only=True),
                ))

        prev_date = None

        result = []

        tz = settings.timezone()

        for allocation in allocations:

            if start_time or end_time:
                s = start_time or allocation.display_start(tz).time()
                e = end_time or allocation.display_end(tz).time()

                s, e = allocation.limit_timespan(s, e)

                time_text = get_time_text(s, e)
            else:
                time_text = get_time_text(
                    allocation.display_start(tz), allocation.display_end(tz)
                )
                s, e = None, None

            availability, text, allocation_class = utils.event_availability(
                self.context, self.request, scheduler, allocation, s, e
            )

            date = ', '.join((
                self.translate(
                    self.short_days[allocation.display_start(tz).weekday()]
                ),
                utils.localize_date(
                    allocation.display_start(tz), long_format=False
                )
            ))

            result.append({
                'id': allocation.id,
                'group': allocation.group,
                'date': date,
                'time': time_text,
                'class': utils.event_class(availability),
                'is_first_of_date': prev_date != date,
                'text': ', '.join(text.split('\n')),
                'is_extra_result': getattr(
                    allocation, 'is_extra_result', False
                )
            })

            prev_date = date

        return result
예제 #13
0
def monthly_report(year, month, resources, reservations='*'):

    titles = dict()

    for uuid in resources.keys():
        titles[uuid] = utils.get_resource_title(resources[uuid])

    # timezone of the report
    timezone = settings.timezone()

    # this order is used for every day in the month
    ordered_uuids = [i[0] for i in sorted(titles.items(), key=lambda i: i[1])]

    # build the hierarchical structure of the report data
    report = utils.OrderedDict()
    last_day = 28

    for d in sorted((d for d in calendar.itermonthdates(year, month))):
        if not d.month == month:
            continue

        day = d.day
        last_day = max(last_day, day)
        report[day] = utils.OrderedDict()

        for uuid in ordered_uuids:
            report[day][uuid] = dict()
            report[day][uuid][u'title'] = titles[uuid]
            report[day][uuid][u'approved'] = list()
            report[day][uuid][u'pending'] = list()
            report[day][uuid][u'url'] = resources[uuid].absolute_url()
            report[day][uuid][u'lists'] = {
                u'approved': _(u'Approved'),
                u'pending': _(u'Pending'),
            }

    # gather the reservations with as much bulk loading as possible
    period_start = datetime(year, month, 1, tzinfo=timezone)
    period_end = datetime(year, month, last_day, tzinfo=timezone)
    period_end += timedelta(days=1, microseconds=-1)

    # get a list of relevant allocations in the given period
    query = Session().query(Allocation)
    query = query.filter(period_start <= Allocation._start)
    query = query.filter(Allocation._start <= period_end)
    query = query.filter(Allocation.resource == Allocation.mirror_of)
    query = query.filter(Allocation.resource.in_(resources.keys()))

    allocations = query.all()

    # quit if there are no allocations at this point
    if not allocations:
        return {}

    # store by group as it will be needed multiple times over later
    groups = dict()
    for allocation in allocations:
        groups.setdefault(allocation.group, list()).append(allocation)

    # using the groups get the relevant reservations
    query = Session().query(Reservation)
    query = query.filter(Reservation.target.in_(groups.keys()))

    if reservations != '*':
        query = query.filter(Reservation.token.in_(reservations))

    query = query.order_by(Reservation.status)

    reservations = query.all()

    @utils.memoize
    def json_timespans(start, end):
        return json.dumps([dict(start=start, end=end)])

    used_days = dict([(i, False) for i in range(1, 32)])
    timezone = settings.timezone()

    def add_reservation(start, end, reservation):
        day = start.day

        used_days[day] = True

        end += timedelta(microseconds=1)

        start = modules.calendar.to_timezone(start, timezone=timezone)
        end = modules.calendar.to_timezone(end, timezone=timezone)

        start = utils.localize_date(start, time_only=True)
        end = utils.localize_date(end, time_only=True)

        context = resources[utils.string_uuid(reservation.resource)]

        reservation_lists = report[day][utils.string_uuid(
            reservation.resource
        )]
        reservation_lists[reservation.status].append(
            dict(
                start=start,
                end=end,
                email=reservation.email,
                data=reservation.data,
                timespans=json_timespans(start, end),
                id=reservation.id,
                token=reservation.token,
                quota=utils.get_reservation_quota_statement(reservation.quota),
                resource=context,
            )
        )

    for reservation in reservations:
        if reservation.target_type == u'allocation':
            add_reservation(reservation.start, reservation.end, reservation)
        else:
            for allocation in groups[reservation.target]:
                add_reservation(allocation.start, allocation.end, reservation)

    # remove unused days
    for day in report:
        if not used_days[day]:
            del report[day]

    return report
예제 #14
0
    def urls(self, allocation):
        """Returns the options for the js contextmenu for the given allocation
        as well as other links associated with the event.

        """

        items = utils.EventUrls(self.context, self.request, exposure)

        start = utils.utctimestamp(
            allocation.display_start(settings.timezone()))
        end = utils.utctimestamp(
            allocation.display_end(settings.timezone()))

        items.move_url('edit-allocation', dict(id=allocation.id))

        # Reservation
        res_add = lambda n, v, p, t: \
            items.menu_add(_(u'Reservations'), n, v, p, t)
        if allocation.is_separate:
            res_add(
                _(u'Reserve'), 'reserve',
                dict(id=allocation.id, start=start, end=end), 'overlay'
            )
            items.default_url(
                'reserve', dict(id=allocation.id, start=start, end=end)
            )
        else:
            res_add(
                _(u'Reserve'), 'reserve-group', dict(group=allocation.group),
                'overlay'
            )
            items.default_url(
                'reserve', dict(group=allocation.group)
            )

        res_add(
            _(u'Manage'), 'reservations', dict(group=allocation.group),
            'inpage'
        )

        # menu entries for single items
        entry_add = lambda n, v, p, t: \
            items.menu_add(_('Entry'), n, v, p, t)

        entry_add(
            _(u'Edit'), 'edit-allocation', dict(id=allocation.id), 'overlay'
        )

        entry_add(
            _(u'Remove'), 'remove-allocation', dict(id=allocation.id),
            'overlay'
        )

        if allocation.in_group:
            # menu entries for group items
            group_add = lambda n, v, p, t: \
                items.menu_add(_('Recurrences'), n, v, p, t)

            group_add(
                _(u'List'), 'group', dict(name=allocation.group), 'overlay'
            )

            group_add(
                _(u'Remove'), 'remove-allocation',
                dict(group=allocation.group),
                'overlay'
            )

        # view selections
        if 'agendaDay' in self.context.available_views:
            items.menu_add(
                _(u'Reservations'), _(u'Daily View'), 'view',
                dict(
                    selected_view='agendaDay',
                    specific_date=allocation.start.strftime('%Y-%m-%d')
                ), 'window'
            )

        return items
예제 #15
0
    def build_allocations_table(self,
                                allocations,
                                start_time=None,
                                end_time=None):
        """ Prepares the given allocations for the found-allocations table.
        Only works on IResourceBase contexts.
        """

        if not allocations:
            return []

        scheduler = self.context.scheduler()
        whole_day_text = self.translate(_(u'Whole day'))

        def get_time_text(start, end):
            if utils.whole_day(start, end):
                return whole_day_text
            else:
                return ' - '.join((
                    utils.localize_date(start, time_only=True),
                    utils.localize_date(end, time_only=True),
                ))

        prev_date = None

        result = []

        tz = settings.timezone()

        for allocation in allocations:

            if start_time or end_time:
                s = start_time or allocation.display_start(tz).time()
                e = end_time or allocation.display_end(tz).time()

                s, e = allocation.limit_timespan(s, e)

                time_text = get_time_text(s, e)
            else:
                time_text = get_time_text(allocation.display_start(tz),
                                          allocation.display_end(tz))
                s, e = None, None

            availability, text, allocation_class = utils.event_availability(
                self.context, self.request, scheduler, allocation, s, e)

            date = ', '.join((self.translate(
                self.short_days[allocation.display_start(tz).weekday()]),
                              utils.localize_date(allocation.display_start(tz),
                                                  long_format=False)))

            result.append({
                'id':
                allocation.id,
                'group':
                allocation.group,
                'date':
                date,
                'time':
                time_text,
                'class':
                utils.event_class(availability),
                'is_first_of_date':
                prev_date != date,
                'text':
                ', '.join(text.split('\n')),
                'is_extra_result':
                getattr(allocation, 'is_extra_result', False)
            })

            prev_date = date

        return result