Exemple #1
0
    def handle_request(self):
        """
        Reads data from self.request and return dict with data useful for response context.

        If self.request is None, then default values are used (access via GET probably).
        If self.request is set, then checks what button was pressed (access via POST probably).
        If start_date or end_date value is incorrect (not valid date), then defaults
        are used respectively.  
        """
        post = self.request
        result = {}
        date_range = self.default_date_range
        if post:
            btn_custom_hit = 'tb_btn_custom_date' in post
            btn_default_range_hit = 'tb_btn_default_range' in post
            btn_next_30_days_hit = 'tb_btn_next_30_days' in post
            btn_next_60_days_hit = 'tb_btn_next_60_days' in post
            btn_next_90_days_hit = 'tb_btn_next_90_days' in post

            # which button was pressed? What action should be triggered?
            if btn_custom_hit:
                date_range = [
                    str_to_date(post['tb_from_date']),
                    str_to_date(post['tb_to_date'])
                ]
            elif btn_default_range_hit:
                date_range = self.default_date_range
            elif btn_next_30_days_hit:
                date_range = [date.today(), date.today() + timedelta(30)]
            elif btn_next_60_days_hit:
                date_range = [date.today(), date.today() + timedelta(60)]
            elif btn_next_90_days_hit:
                date_range = [date.today(), date.today() + timedelta(90)]

        # if posted date is incorrect, then set default values
        date_range[0] = date_range[0] or self.default_date_range[
            0]  # override if None
        date_range[
            1] = date_range[1] or date_range[0] + self.default_date_offset

        # create result dict
        cut_date_range = self._cut_date_range_if_too_wide(date_range)
        result['date_range'] = cut_date_range
        return result
Exemple #2
0
    def handle_request(self):
        """
        Reads data from self.request and return dict with data useful for response context.

        If self.request is None, then default values are used (access via GET probably).
        If self.request is set, then checks what button was pressed (access via POST probably).
        If start_date or end_date value is incorrect (not valid date), then defaults
        are used respectively.  
        """ 
        post = self.request
        result = {}
        date_range = self.default_date_range
        if post:
            btn_custom_hit        = 'tb_btn_custom_date' in post
            btn_default_range_hit = 'tb_btn_default_range' in post
            btn_next_30_days_hit  = 'tb_btn_next_30_days' in post
            btn_next_60_days_hit  = 'tb_btn_next_60_days' in post
            btn_next_90_days_hit  = 'tb_btn_next_90_days' in post
            
            # which button was pressed? What action should be triggered?
            if btn_custom_hit:
                date_range = [str_to_date(post['tb_from_date']), str_to_date(post['tb_to_date'])]
            elif btn_default_range_hit:
                date_range = self.default_date_range
            elif btn_next_30_days_hit:
                date_range = [date.today(), date.today() + timedelta(30)]
            elif btn_next_60_days_hit:
                date_range = [date.today(), date.today() + timedelta(60)]
            elif btn_next_90_days_hit:
                date_range = [date.today(), date.today() + timedelta(90)]

        # if posted date is incorrect, then set default values
        date_range[0] = date_range[0] or self.default_date_range[0]    # override if None
        date_range[1] = date_range[1] or date_range[0] + self.default_date_offset

        # create result dict
        cut_date_range = self._cut_date_range_if_too_wide(date_range)
        result['date_range'] = cut_date_range
        return result
Exemple #3
0
def get_report_data(report_type, from_date, to_date, order_by=[]):
    """
    Desc:
        Returns data for creating selected report.

    Args:
        report_type - basestring. type of the report
        from_date, to_date - basestring. describe the time period for the report.
        order_by - iterable. Names of parameters by which result should be ordered by.
                   Currently only one item is supported, bo using order_by will more 
                   then one element will take no effect. 

    Return:
        dict { 'report'   : report data
               'template' : template to be rendered (only filename. Proper dir will be attached elsewhere)
               'error'    : error message if sth goes wrong
               'ordering' : list of names used to sort report data
             }
    """
    assert from_date
    assert to_date
    order_by = list(order_by)
    order_by = order_by[:1] if order_by else []         # only one name can be used to order data 
    
    # NOTE: `ob` stands from order by
    ob_rename_dict = {'title'        : order_asc_by_key('title'),
                      '-title'       : order_desc_by_key('title'),
                      'location'     : order_asc_by_key('location_str'),
                      '-location'    : order_desc_by_key('location_str'),
                      'shelf_mark'   : order_asc_by_key('shelf_mark'),
                      '-shelf_mark'  : order_desc_by_key('shelf_mark'),
                      'status'       : order_asc_by_key('status_str'),
                      '-status'      : order_desc_by_key('status_str'),
                      'for_whom'     : order_asc_by_key('for_whom'),
                      '-for_whom'    : order_desc_by_key('for_whom'),
                      'when'         : order_asc_by_key('when'),
                      '-when'        : order_desc_by_key('when'),
                      'librarian'    : order_asc_by_key('by_whom'),
                      '-librarian'   : order_desc_by_key('by_whom'),
                      'name'         : order_asc_by_key('name'),
                      '-name'        : order_desc_by_key('name'),
                      'num_cancels'  : order_asc_by_key('num_of_cancels'),
                      '-num_cancels' : order_desc_by_key('num_of_cancels'),
                      'num_overdues' : order_asc_by_key('num_of_overdues'),
                      '-num_overdues': order_desc_by_key('num_of_overdues'),
                      'len_overdues' : order_asc_by_key('duration_of_overdues'),
                      '-len_overdues': order_desc_by_key('duration_of_overdues'),
                      'num_rentals'  : order_asc_by_key('num_of_rentals'),
                      '-num_rentals' : order_desc_by_key('num_of_rentals'),
                      'num_rsvs'     : order_asc_by_key('num_of_reservations'),
                      '-num_rsvs'   : order_desc_by_key('num_of_reservations'),
                      }

    ob_in_status         = ['title', '-title', 'location', '-location', 'shelf_mark', '-shelf_mark', 
                            'status', '-status', 'for_whom', '-for_whom', 'when', '-when', 'librarian', '-librarian']
    ob_in_often_rented   = ['title', '-title', 'num_rentals', '-num_rentals']
    ob_in_often_reserved = ['title', '-title', 'num_rsvs', '-num_rsvs']
    ob_in_black_list     = ['name', '-name', 'num_cancels', '-num_cancels', 'num_overdues', '-num_overdues', 'len_overdues', '-len_overdues']
    ob_in_lost_books     = copy(ob_in_status)
    
    if report_type == u'status':
        book_infos = []
        copies = BookCopy.objects.select_related('id', 'state', 'book__title', 'location', 'location__building', 'shelf_mark')
        last_rentals = {}
        from datetime import timedelta, datetime
        status_day = utils.str_to_date(from_date, utils.today())
        status_day_start = datetime(status_day.year, status_day.month, status_day.day, 0, 0, 0)
        status_day_end = datetime(status_day.year, status_day.month, status_day.day, 23, 59, 59)
        
#         Q_start_date_filter =  Q(start_date__contains='')      # Q_all
#         if from_date:
#             Q_start_date_filter = Q(start_date__gte=from_date)
#         Q_end_date_filter = Q(end_date__isnull=True)
#         if to_date:
#             Q_end_date_filter = Q(end_date__lte=to_date) | Q(end_date__isnull=True)
#         rentals = Rental.objects.select_related('reservation', 'reservation__book_copy__id') \
#                                 .filter(Q_start_date_filter) \
#                                 .filter(Q_end_date_filter)
        Q_start_date_filter = Q(start_date__lte=status_day_end)
        Q_end_date_filter = (Q(end_date__isnull=True) | Q(end_date__gte=status_day_start))
        rentals = Rental.objects.select_related('reservation', 'reservation__book_copy__id') \
                                .filter(Q_start_date_filter) \
                                .filter(Q_end_date_filter)

        for rental in rentals:
            book_copy_id = rental.reservation.book_copy.id
            start_date = rental.start_date
            if book_copy_id not in last_rentals:
                last_rentals.update({book_copy_id: {'when'    : start_date,
                                                    'for_whom': rental.reservation.for_whom,
                                                    'by_whom' : rental.who_handed_out,
                                                                }})
            elif last_rentals[book_copy_id]['when'] < start_date:
                last_rentals.update({book_copy_id: {'when'    : start_date,
                                                    'for_whom': rental.reservation.for_whom,
                                                    'by_whom' : rental.who_handed_out,
                                                                }})

        book_infos = []
        statuses = book_copies_status(copies)
        for kopy in copies:
            copy_status = statuses[kopy.id]
            not_rented_yet = copy_status['copy'].id not in last_rentals
            if not_rented_yet:
                for_whom = when = by_whom = '- never -'
            else:
                last_rental = last_rentals[copy_status['copy'].id]
                (for_whom, when, by_whom) = (last_rental['for_whom'], last_rental['when'].date(), last_rental['by_whom'])            
            
            book_infos.append({ 'title'         : copy_status['copy'].book.title,
                                'shelf_mark'    : copy_status['copy'].shelf_mark,
                                'copy'          : kopy,
                                'location'      : copy_status['copy'].location,
                                'location_str'  : unicode(copy_status['copy'].location).lower(),     # field for ordering purpose
                                'status'        : copy_status['status'],
                                'status_str'    : unicode(copy_status['status']).lower(),
                                'for_whom'      : for_whom,
                                'when'          : unicode(when),
                                'by_whom'       : by_whom,
                                'not_rented_yet': not_rented_yet})
        
        sort_by = list(set(order_by) & set(ob_in_status))
        if sort_by:
            book_infos.sort(cmp=ob_rename_dict[sort_by[0]])
        return {'report'   : book_infos, 
                'template' : 'library_status.html', 
                'error'    : False,
                'ordering' : sort_by,
                }

    ###

    elif report_type == u'most_often_rented':
        book_infos = []
        books = Book.objects.all()
        rentals = Rental.objects.all()
        date_empty = False
        nums_of_rentals = {}

        if from_date == u'' or to_date == u'':
            date_empty = True
        else:
            try:
                [y, m, d] = map(int,from_date.split('-'))
                start_date = date(y, m, d)
                [y, m, d] = map(int,to_date.split('-'))
                end_date = date(y, m, d)
            except:
                return {'error': True, 'report': [], 'template': 'reports.html'}

        for book in books:
            if book.id not in nums_of_rentals:
                nums_of_rentals.update({book.id: 0})

        for rental in rentals:
            book_id = rental.reservation.book_copy.book.id
            if date_empty:
                nums_of_rentals[book_id] += 1
            else:
                if rental.start_date.date() >= start_date and rental.start_date.date() <= end_date:
                    nums_of_rentals[book_id] += 1

        for book in books:
            book_infos.append({'title': book.title, 'num_of_rentals': nums_of_rentals[book.id], 'id' : book.id, })

        sort_by = list(set(order_by) & set(ob_in_often_rented))
        if sort_by:
            book_infos.sort(cmp=ob_rename_dict[sort_by[0]])
        return {'report'   : book_infos, 
                'template' : 'most_often_rented.html', 
                'error'    : False,
                'ordering' : sort_by,
                }

    ###

    elif report_type == u'most_often_reserved':
        book_infos = []
        books = Book.objects.all()
        reservations = Reservation.objects.all()
        nums_of_reservations = {}
        date_empty = False

        if from_date == u'' or to_date == u'':
            date_empty = True
        else:
            try:
                [y, m, d] = map(int,from_date.split('-'))
                start_date = date(y, m, d)
                [y, m, d] = map(int,to_date.split('-'))
                end_date = date(y, m, d)
            except:
                return {'error': True, 'report': [], 'template': 'reports.html'}

        for book in books:
            if book.id not in nums_of_reservations:
                nums_of_reservations.update({book.id: 0})

        for reservation in reservations:
            book_id = reservation.book_copy.book.id
            if date_empty:
                nums_of_reservations[book_id] += 1
            else:
                if reservation.start_date >= start_date and reservation.start_date <= end_date:
                    nums_of_reservations[book_id] += 1

        for book in books:
            book_infos.append({'title': book.title, 'num_of_reservations': nums_of_reservations[book.id], 'id' : book.id, })

        sort_by = list(set(order_by) & set(ob_in_often_reserved))
        if sort_by:
            book_infos.sort(cmp=ob_rename_dict[sort_by[0]])
        return {'report'   : book_infos, 
                'template' : 'most_often_reserved.html', 
                'error'    : False,
                'ordering' : sort_by,
                }

    ###

    elif report_type == u'black_list':
        nums_of_cancels = {}
        nums_of_overdues = {}
        duration_of_overdues = {}
        user_infos = []
        users = User.objects.all()
        reservations = Reservation.objects.filter(when_cancelled__isnull=False)
        rentals = [ r for r in Rental.objects.all() if r.end_date and r.reservation.end_date < r.end_date.date() ]
        date_empty = False

        if from_date == u'' or to_date == u'':
            date_empty = True
        else:
            try:
                [y, m, d] = map(int,from_date.split('-'))
                start_date = date(y, m, d)
                [y, m, d] = map(int,to_date.split('-'))
                end_date = date(y, m, d)
            except:
                return {'error': True, 'report': [], 'template': 'reports.html'}

        for user in users:
            nums_of_cancels.update({user.id: 0})
            nums_of_overdues.update({user.id: 0})
            duration_of_overdues.update({user.id: 0})

        for rental in rentals:
            user_id = rental.reservation.for_whom.id
            if date_empty:
                nums_of_overdues[user_id] += 1
                duration_of_overdues[user_id] += (rental.end_date.date() - rental.reservation.end_date).days
            else:
                if rental.reservation.start_date >= start_date and rental.reservation.start_date <= end_date:
                    nums_of_overdues[user_id] += 1
                    duration_of_overdues[user_id] += (rental.end_date.date() - rental.reservation.end_date).days

        for reservation in reservations:
            user_id = reservation.for_whom.id
            if date_empty:
                nums_of_cancels[user_id] += 1
            else:
                if reservation.start_date >= start_date and reservation.start_date <= end_date:
                    nums_of_cancels[user_id] += 1

        for user in users:
            user_infos.append({'name': user.last_name + u', ' + user.first_name,
                               'id'  : user.id,
                               'num_of_cancels': nums_of_cancels[user.id],
                               'num_of_overdues' : nums_of_overdues[user.id],
                               'duration_of_overdues' : duration_of_overdues[user.id],
                               })

        sort_by = list(set(order_by) & set(ob_in_black_list))
        if sort_by:
            user_infos.sort(cmp=ob_rename_dict[sort_by[0]])

        return {'report'   : user_infos, 
                'template' : 'black_list.html', 
                'error'    : False,
                'ordering' : sort_by,
                }

    ###

    elif report_type == u'lost_books':
        reported_data = get_report_data(u'status', from_date, to_date)
        book_infos = reported_data['report']
        lost_books = filter(lambda b: not b['status'].is_available(), book_infos)
        sort_by = list(set(order_by) & set(ob_in_lost_books))
        if sort_by:
            lost_books.sort(cmp=ob_rename_dict[sort_by[0]])
        return {'report'   : lost_books, 
                'template' : 'unavailable_status.html', 
                'error'    : False,
                'ordering' : sort_by,
                }
    else:
        return {'report': [], 'template': 'reports.html', 'error': True}