Пример #1
0
def update_classroom():
    """Update classroom list of students."""
    session = db_session()
    if request.method == 'GET':
        classrooms = session.query(Classroom).order_by(Classroom.name)
        return render_template('update_classroom.html', classrooms=classrooms)

    if request.method == 'POST':
        try:
            if 'classroom_file' not in request.files:
                raise FileNotFoundError("Could not find the file!")

            classroom_file = request.files['classroom_file']

            if classroom_file.filename == '':
                raise ValueError("No files have been selected.")

            if not allowed_file(classroom_file.filename):
                raise ValueError("This file type is not permitted.")

            if classroom_file:
                filename = secure_filename(classroom_file.filename)
                file_path = os.path.join(UPLOAD_DIR, filename)
                classroom_file.save(file_path)

                if update_db(file_path):
                    flash("Classroom details have been updated successfully!")

        except (FileNotFoundError, ValueError) as error:
            flash("<strong>Error! </strong> %s" % str(error), 'error')
            # pass
        return redirect('users/update')
Пример #2
0
def view_loans():
    """Displays loans of a book, or of all books."""
    session = db_session()
    if current_user.is_admin:
        found_books = session.query(Book).filter(
            Book.current_location == BookLocation.LOAN.value).all()
    else:
        found_books = session.query(Book).filter(
            Book.current_location == BookLocation.LOAN.value and
            Book.id in [l.book_id for l in Classroom.query(
                Classroom.user_id == current_user.id).open_loans]).all()

    # pagination
    total = len(found_books)
    page = request.args.get(get_page_parameter(), type=int, default=1)
    pagination = Pagination(page=page,
                            total=total,
                            per_page=PER_PAGE,
                            css_framework="bootstrap3")

    # initialise loan forms
    new_loan_form, loan_return_form = init_loan_forms()

    return render_template('loans.html',
                           books=found_books,
                           new_loan_form=new_loan_form,
                           loan_return_form=loan_return_form,
                           pagination=pagination,
                           thumbnails_dir=THUMBNAILS_DIR)
Пример #3
0
def record_loan():
    """Records loan of a book to a pupil."""
    session = db_session()
    new_loan_form = NewLoanForm()

    class_id = current_user.classroom.id
    new_loan_form.pupil_id.choices = \
        [(p[0], p[1]) for p in session.query(Pupil.id, Pupil.name).
         filter(Pupil.classroom_id == class_id)]
    try:
        user = User.query.filter(
            User.id == int(new_loan_form.user_id.data)
            ).first()
        pupil = Pupil.query.filter(
            Pupil.id == int(new_loan_form.pupil_id.data)
            ).first()
        book = Book.query.filter(
            Book.id == int(new_loan_form.book_id.data)
            ).first()

        if not user or not pupil or not book:
            raise ValueError(
                "Invalid entries! Book: %s; Pupil: %s; User: %s" %
                (str(book), str(pupil), str(user))
            )

        if not new_loan_form.barcode_img.data:
            raise ValueError(
                "No barcode image provided. Please scan the barcode."
            )

        isbnlist = scan_for_isbn(request.files[new_loan_form.barcode_img.name])

        if len(isbnlist) < 1:
            raise ValueError(
                "No ISBN found in provided image."
            )

        if book.isbn13 not in isbnlist:
            raise ValueError(
                "Barcode does not match selected book."
            )

        loan = Loan()
        loan.pupil = pupil
        loan.book = book
        loan.start_date = datetime.date(datetime.now())

        book.current_location = BookLocation.LOAN.value

        session.add(loan)
        session.commit()
        flash("Loan has been recorded for '%s' to %s" %
              (book.title, pupil.name))

    except ValueError as val_err:
        flash(str(val_err), 'error')

    return redirect_to_previous(True)
Пример #4
0
def edit_book():
    """Update a book in the library."""
    session = db_session()

    lookup_isbns = []
    lookup_titles = []
    books = session.query(Book).distinct().\
        values(Book.isbn10,
               Book.isbn13,
               Book.title)

    for book in books:
        if book[0]:
            lookup_isbns.append(book[0])
        if book[1]:
            lookup_isbns.append(book[1])
        if book[2]:
            lookup_titles.append(book[2])

    if lookup_isbns:
        lookup_isbns.sort()
    if lookup_titles:
        lookup_titles.sort()

    if request.method == 'GET':
        return render_template('edit_book.html',
                               lookup_isbns=lookup_isbns,
                               lookup_titles=lookup_titles)

    found_books = []

    if request.method == 'POST':
        search_isbn = request.form['search_isbn']
        search_title = request.form['search_title']

        if search_title and search_title.strip():
            search_term = '%' + search_title.strip() + '%'
            found_books = session.query(Book).\
                filter(Book.title.ilike(search_term)).all()

        if search_isbn and search_isbn.strip():
            search_term = '%' + search_isbn.strip() + '%'
            found_books = found_books + session.query(Book).\
                filter(or_(Book.isbn10.ilike(search_term),
                           Book.isbn13.ilike(search_term))).all()

    result = render_template('edit_book.html',
                             lookup_isbns=lookup_isbns,
                             lookup_titles=lookup_titles,
                             search_isbn=search_isbn,
                             search_title=search_title,
                             thumbnails_dir=THUMBNAILS_DIR,
                             found_books=sorted(found_books,
                                                key=lambda b: b.title))
    return result
Пример #5
0
def add_book():
    """Add a book to the library."""
    try:
        book = Book()
        # defaults
        book.is_available = True
        book.current_location = BookLocation.LIBRARY.value

        book.title = request.form['book_title'].strip()
        book.isbn10 = request.form['isbn10'].strip()
        book.isbn13 = request.form['isbn13'].strip()
        book.description = request.form['book_description'].strip()
        book.preview_url = request.form['preview_url'].strip()

        for author_name in request.form['book_authors'].split(','):
            author_name = author_name.strip()
            author = Author.query.filter(Author.name == author_name).first()
            if not author:
                author = Author(author_name)
            book.authors.append(author)

        for category_name in request.form['book_categories'].split(','):
            category_name = category_name.strip()
            category = Category.query.\
                filter(Category.name == category_name).first()
            if not category:
                category = Category(category_name)
            book.categories.append(category)

        book.thumbnail_url = request.form['thumbnail_url'].strip()
        if book.thumbnail_url:
            title = [
                c for c in book.title.replace(' ', '_') if re.match(r'\w', c)
            ]
            image_name = ''.join(title) + book.isbn13 + '.jpg'

            img = open(THUMBNAILS_ABSOLUTE_DIR + image_name, 'wb')
            img.write(urllib_request.urlopen(book.thumbnail_url).read())
            book.image_name = image_name

        session = db_session()
        session.add(book)
        session.commit()

        flash("The book has been added to the library successfully!")
    except RuntimeError as rte:
        error_message = "Something has gone wrong!"
        if isinstance(rte, exc.IntegrityError):
            error_message += "<br>It seems that the book '%s' "\
                "already exists in the library." % book.title

        flash(error_message, 'error')

    return render_template('add_book.html')
Пример #6
0
def add_user():
    """Give access to a classroom, pupil or admin."""
    session = db_session()
    new_access_form = NewAccessForm()
    new_access_form.classroom.choices = [(0, 'None')] + \
        [(cr[0], '{} ({})'.format(cr[1], cr[2]))
         for cr in session.query(Classroom.id,
                                 Classroom.name,
                                 Classroom.year).distinct()]

    if request.method == 'POST':
        if new_access_form.validate_on_submit():
            # check if it's an existing user
            user = User.query.filter(
                User.username.ilike(new_access_form.username.data)).first()
            # check if a classroom id has been passed
            classroom = Classroom.query.filter(
                Classroom.id == new_access_form.classroom.data).first()

            # for individual user; should be unique
            if user and not classroom:
                flash(
                    "This username is already in use, " +
                    "please chose a different username.", 'error')

            # for classroom username/password change, update db
            if user and classroom:
                user.username = new_access_form.username.data
                user.password = new_access_form.password.data
                user.is_admin = new_access_form.is_admin.data
                session.commit()
                flash("Login details have been updated!")

            # for new users, create
            elif not user:
                user = User()
                user.username = new_access_form.username.data
                user.password = new_access_form.password.data
                user.is_admin = new_access_form.is_admin.data

                # link to classroom if provided
                if classroom:
                    classroom.user = user

                session.add(user)
                session.commit()
                flash("Access has been created successfully!")
        else:
            flash("Invalid entries!", 'error')

    return render_template('access.html', new_access_form=new_access_form)
Пример #7
0
def init_loan_forms():
    """Initialise a new_loan and loan_return forms."""
    session = db_session()

    new_loan_form = None
    loan_return_form = None

    if current_user.is_authenticated:
        loan_return_form = LoanReturnForm()
        if current_user.classroom:
            new_loan_form = NewLoanForm()
            class_id = current_user.classroom.id
            new_loan_form.pupil_id.choices = \
                [(p[0], p[1]) for p in session.query(Pupil.id, Pupil.name).
                 filter(Pupil.classroom_id == class_id)]

    return new_loan_form, loan_return_form
Пример #8
0
def record_return():
    """Records return of a book."""
    session = db_session()
    loan_return_form = LoanReturnForm()

    try:
        book = Book.query.filter(
            Book.id == int(loan_return_form.book_id.data)
            ).first()

        if not book:
            raise ValueError("Invalid entries!")

        if not loan_return_form.barcode_img.data:
            raise ValueError(
                "No barcode image provided. Please scan the barcode.")

        isbnlist = scan_for_isbn(
            request.files[loan_return_form.barcode_img.name])

        if len(isbnlist) < 1:
            raise ValueError("No ISBN found in provided image.")

        if book.isbn13 not in isbnlist:
            raise ValueError("Barcode does not match selected book.")

        loan = book.current_loan

        if not loan:
            raise ValueError("No loans found for this book.")

        loan.end_date = datetime.date(datetime.now())
        book.current_location = BookLocation.LIBRARY.value

        session.commit()
        flash("Book return has been recorded for '%s' by %s" %
              (book.title, loan.pupil.name))

    except ValueError as val_err:
        flash(str(val_err), 'error')
    return redirect_to_previous(True)
Пример #9
0
def index():
    """Render the home page.

    Initialising the search box with books titles, authors names,
    and categories names.
    """
    session = db_session()
    search_terms = []

    for book_title in session.query(Book.title).distinct():
        search_terms.append(book_title[0])

    for category_name in session.query(Category.name).distinct():
        search_terms.append(category_name[0])

    for author_name in session.query(Author.name).distinct():
        search_terms.append(author_name[0])

    search_terms.sort()

    return render_template('home.html', search_terms=search_terms)
Пример #10
0
def update_db(classroom_file):
    """Persist the the details in the file into the database."""
    try:
        session = db_session()
        with open(classroom_file) as csv_file:
            reader = csv.reader(csv_file, delimiter=',', quotechar='"')

            current_classroom = None

            for row in reader:
                if not current_classroom or \
                   not row[0] == current_classroom.name:
                    current_classroom = Classroom(row[0])
                    current_classroom.year = row[1]
                    session.add(current_classroom)
                current_classroom.pupils.append(Pupil(row[2]))

            session.commit()
            return True
    except Exception as err:  # pylint: disable=W0703
        flash("Something has gone wrong!<br>" + str(err), 'error')
    return False
Пример #11
0
def auto_load_books():
    """Automated book loading.

    Combining the lookup and add functions, this function aims at bulk
    loading of books in the background.
    """
    session = db_session()
    lookup_limit = request.args.get('n')

    # lookup books
    succeeded = []
    failed = []
    counter = 1

    try:
        for isbn in execute_sql('fetch_isbn', lookup_limit):
            # ensure we don't exceed google or amazon api usage limit
            if counter % 3 == 0:
                sleep(random.uniform(60, 90))
            else:
                sleep(random.uniform(5, 10))
            counter += 1

            api_client = APIClient(isbn, None)
            found_books = api_client.find_books(direct_search_only=True)

            # to ensure only the right book is added, only search resulting
            # yielding 1 result is accepted.
            if len(found_books) != 1:
                failed.append(
                    (isbn[0], "search results %d" % len(found_books)))
                continue

            # now we add the book.
            book = found_books[0]

            # defaults
            book.is_available = True
            book.current_location = BookLocation.LIBRARY.value

            if book.thumbnail_url:
                image_name = ''.join([
                    c for c in book.title.replace(' ', '_')
                    if re.match(r'\w', c)
                ]) + book.isbn13 + '.jpg'
                img = open(THUMBNAILS_ABSOLUTE_DIR + image_name, 'wb')
                img.write(urllib_request.urlopen(book.thumbnail_url).read())
                book.image_name = image_name

            for i in range(len(book.authors)):
                lookup_author = Author.query.filter(
                    Author.name == book.authors[i].name).first()
                if lookup_author:
                    book.authors[i] = None
                    book.authors[i] = lookup_author

            for i in range(len(book.categories)):
                lookup_category = Category.query.filter(
                    Category.name == book.categories[i].name).first()
                if lookup_category:
                    book.categories[i] = None
                    book.categories[i] = lookup_category

            session.add(book)
            session.commit()
            succeeded.append(isbn[0])

        execute_sql('update_success', succeeded=succeeded)
        execute_sql('update_failed', failed=failed)

    except RuntimeError as rt_error:
        flash("Something has gone wrong! " + str(rt_error))

    flash('success: ' + ','.join(str(i) for i in succeeded))
    flash('failed: ' + ','.join(str(i) for i, e in failed))
    return redirect('books/view')
Пример #12
0
def view_books(ready_books=None):
    """Display books in the library.

    Defaults to displaying available books only.
    If `include_unavailable` parameter is set in the `request`,
    it display all books; this is used in the admin view.
    """
    session = db_session()

    # get search cache
    search_cache = []

    for book_title in session.query(Book.title).distinct():
        search_cache.append(book_title[0])

    for category_name in session.query(Category.name).distinct():
        search_cache.append(category_name[0])

    for author_name in session.query(Author.name).distinct():
        search_cache.append(author_name[0])

    search_cache.sort()

    # initialise loan forms
    new_loan_form, loan_return_form = init_loan_forms()

    # requpest parameters
    page = request.args.get(get_page_parameter(), type=int, default=1)
    include_unavailable = request.args.get('include-unavailable')
    search_term = request.args.get('q')

    # if books have been provided, display them
    if ready_books:
        total = len(ready_books)
        books = ready_books
    # apply search criteria if provided
    elif search_term:
        search_term = '%' + search_term.strip() + '%'
        total = session.query(Book).\
            join(Author.books).\
            filter(or_(Book.title.ilike(search_term),
                       Author.name.ilike(search_term))).\
            union(
                session.query(Book).
                join(Category.books).
                filter(or_(Book.title.ilike(search_term),
                           Category.name.ilike(search_term)))
            ).count()
        books = session.query(Book).\
            join(Author.books).\
            filter(or_(Book.title.ilike(search_term),
                       Author.name.ilike(search_term))).\
            union(
                session.query(Book).
                join(Category.books).
                filter(or_(Book.title.ilike(search_term),
                           Category.name.ilike(search_term)))
            ).order_by(Book.title).\
            limit(PER_PAGE).offset((page - 1) * PER_PAGE)
    # include all books if specified
    elif include_unavailable:
        total = session.query(Book.id).count()
        books = session.query(Book).order_by(Book.title).\
            limit(PER_PAGE).offset((page - 1) * PER_PAGE)
    # in all other cases, display all vailable books
    else:
        total = session.query(Book).filter(Book.is_available == 1).count()
        books = session.query(Book).filter(Book.is_available == 1).\
            order_by(Book.title).limit(PER_PAGE).offset((page - 1) * PER_PAGE)

    pagination = Pagination(page=page,
                            total=total,
                            per_page=PER_PAGE,
                            css_framework="bootstrap3")

    return render_template('view_book.html',
                           books=books,
                           new_loan_form=new_loan_form,
                           loan_return_form=loan_return_form,
                           pagination=pagination,
                           search_cache=search_cache,
                           thumbnails_dir=THUMBNAILS_DIR)
Пример #13
0
def update_book():
    """Update a book with provided details."""
    try:
        session = db_session()

        update_id = request.form['book_id']
        update_status = int(request.form['book_status'].strip())
        update_title = request.form['book_title'].strip()
        update_description = request.form['book_description'].strip()
        update_thumbnail_url = request.form['book_thumbnail_url'].strip()
        update_preview_url = request.form['book_preview_url'].strip()
        update_categories = [
            c.strip() for c in request.form['book_categories'].split(',')
        ]
        update_authors = [
            a.strip() for a in request.form['book_authors'].split(',')
        ]

        book = session.query(Book).filter(Book.id == update_id).first()

        book.title = update_title
        book.description = update_description
        book.is_available = update_status

        # update categories
        # removed
        for category in book.categories:
            if category.name not in update_categories:
                book.categories.remove(category)
        # added
        for category_name in update_categories:
            if category_name not in [c.name for c in book.categories]:
                book.categories.append(Category(category_name))

        # update authors
        # removed
        for author in book.authors:
            if author.name not in update_authors:
                book.authors.remove(author)
        # added
        for author_name in update_authors:
            if author_name not in [a.name for a in book.authors]:
                book.authors.append(Author(author_name))

        # thumbnail
        if update_thumbnail_url and book.thumbnail_url != update_thumbnail_url:
            book.thumbnail_url = update_thumbnail_url
            title = [
                c for c in book.title.replace(' ', '_') if re.match(r'\w', c)
            ]
            image_name = ''.join(title) + book.isbn13 + '.jpg'

            img = open(THUMBNAILS_ABSOLUTE_DIR + image_name, 'wb')
            img.write(urllib_request.urlopen(book.thumbnail_url).read())
            book.image_name = image_name

        # preview url
        if update_preview_url and book.preview_url != update_preview_url:
            book.preview_url = update_preview_url

        session.commit()

        flash("The book has been updated successfully!")
    except RuntimeError as rte:
        flash("Something has gone wrong! <br>%s" % str(rte), 'error')

    return redirect('books/edit')