<commit_before># TODO: code review of whole module
from collections import defaultdict

from datetime import timedelta

from django.db.models import Q, Sum

from kitabu.search.utils import Timeline

from kitabu.exceptions import ReservationValidationError


class Subjects(object):
    """Searcher for subjects available in certain time period.

    Constructor is used to set subject model and possibly limit subjects on
    which search should be performed.

    Finite availablity means only certain number of reservations at a time is
    possible.

    """

    def __init__(self, subject_model, subject_manager=None):
        self.subject_model = subject_model
        self.reservation_model = subject_model.get_reservation_model()
        if subject_manager:
            self._subject_manager = subject_manager

    def _get_subject_manager(self):
        if not hasattr(self, '_subject_manager'):
            self._subject_manager = self.subject_model.objects
        return self._subject_manager

    def _set_subject_manager(self, value):
        self._subject_manager = value

    subject_manager = property(_get_subject_manager, _set_subject_manager)

    def search(self, start, end, required_size):
        colliding_reservations = self.reservation_model.colliding_reservations_in_subjects(
            start=start,
            end=end,
            subjects=self.subject_manager.all()
        ).select_related('subject')

        timelines = defaultdict(lambda: defaultdict(lambda: 0))

        for reservation in colliding_reservations:
            reservation_start = max(start, reservation.start)
            timelines[reservation.subject][reservation_start] += reservation.size
            if reservation.end < end:
                timelines[reservation.subject][reservation.end] -= reservation.size

        disqualified_subjects = []
        for subject, timeline in timelines.iteritems():
            reservations_cnt = 0
            max_reservations = 0
            for moment in sorted(timeline.keys()):
                reservations_cnt += timeline[moment]
                if reservations_cnt > max_reservations:
                    max_reservations = reservations_cnt
                    if reservations_cnt + required_size > subject.size:
                        disqualified_subjects.append(subject.id)
                        break
        return self.subject_manager.exclude(id__in=disqualified_subjects).filter(size__gte=required_size)

    def valid_search(self, *args, **kwargs):
        pre_results = self.search(*args, **kwargs)<commit_msg>add extra size param to available subjects searcher<commit_after># TODO: code review of whole module
from collections import defaultdict

from datetime import timedelta

from django.db.models import Q, Sum

from kitabu.search.utils import Timeline

from kitabu.exceptions import ReservationValidationError


class Subjects(object):
    """Searcher for subjects available in certain time period.

    Constructor is used to set subject model and possibly limit subjects on
    which search should be performed.

    Finite availablity means only certain number of reservations at a time is
    possible.

    """

    def __init__(self, subject_model, subject_manager=None):
        self.subject_model = subject_model
        self.reservation_model = subject_model.get_reservation_model()
        if subject_manager:
            self._subject_manager = subject_manager

    def _get_subject_manager(self):
        if not hasattr(self, '_subject_manager'):
            self._subject_manager = self.subject_model.objects
        return self._subject_manager

    def _set_subject_manager(self, value):
        self._subject_manager = value

    subject_manager = property(_get_subject_manager, _set_subject_manager)

    def search(self, start, end, required_size=None, size=None):
        if required_size is None:
            required_size = size

        if required_size is None:
            raise Exception('required_size or size must be provided')

        colliding_reservations = self.reservation_model.colliding_reservations_in_subjects(
            start=start,
            end=end,
            subjects=self.subject_manager.all()
        ).select_related('subject')

        timelines = defaultdict(lambda: defaultdict(lambda: 0))

        for reservation in colliding_reservations:
            reservation_start = max(start, reservation.start)
            timelines[reservation.subject][reservation_start] += reservation.size
            if reservation.end < end:
                timelines[reservation.subject][reservation.end] -= reservation.size

        disqualified_subjects = []
        for subject, timeline in timelines.iteritems():
            reservations_cnt = 0
            max_reservations = 0
            for moment in sorted(timeline.keys()):
                reservations_cnt += timeline[moment]
                if reservations_cnt > max_reservations:
                    max_reservations = reservations_cnt
                    if reservations_cnt + required_size > subject.size:
                        disqualified_subjects.append(subject.id)
                        break
        return self.subject_manager.exclude(id__in=disqualified_subjects).filter(size__gte=required_size)

    def valid_search(self, *args, **kwargs):
        pre_results = self.search(*args, **kwargs)
{'fraction_non_alphanumeric': 0.0677461380978969, 'fraction_numerical': 0.0011166945840312675, 'mean_word_length': 2.8140525195173884, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5494217', 'n_tokens_mistral': 1424, 'n_tokens_neox': 1345, 'n_words': 350}