from bisect import insort from heapq import merge from collections import namedtuple class Library(object): def __init__(self): self._users = set() self._bookrecords = [] BookRecord = namedtuple("BookRecord", "name reservations") #reservations ordered by from_ Reservation = namedtuple("Reservation", "from_ to") def add_user(self, name): if name in self._users: return False self._users.add(name) return True def add_book(self, name): self._bookrecords.append(Library.BookRecord(name, [])) def reserve_book(self, user, book, date_from, date_to): if user not in self._users or date_from > date_to: return False disjoint_range = lambda rng: rng.to < date_from or date_to < rng.from_ is_free = lambda record: all((disjoint_range(range_) for range_ in record.reservations)) same_name = lambda record: record.name == book #it is important that we encounter the records always in the same order #this guarantees that reservations will not be fragmented for record in filter(is_free, filter(same_name, self._bookrecords)): insort(record.reservations, Library.Reservation(date_from, date_to)) return True return False def books(self): return (x.name for x in self._bookrecords) def reservations(self): triple_gen = lambda record: ((x.from_, x.to, record.name) for x in record.reservations) return merge(*[triple_gen(record) for record in self._bookrecords], key=lambda x: x[0]) def at_most_books(max_number_of_books): def decorator(cls): class NewBookCollection(cls): # pylint: disable=too-few-public-methods def add_book(self, name): #pouzitie _bookrecords je nebezpecne if sum([x.name == name for x in self._bookrecords]) >= max_number_of_books: return False cls.add_book(self, name) return True return NewBookCollection return decorator def one_book(bookcollection): return at_most_books(1)(bookcollection)