Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AlekSIS/onboarding/AlekSIS-App-Exlibris
1 result
Show changes
Commits on Source (1)
import pkg_resources
try:
__version__ = pkg_resources.get_distribution('BiscuIT-App-Exlibris').version
__version__ = pkg_resources.get_distribution("BiscuIT-App-Exlibris").version
except Exception:
__version__ = 'unknown'
__version__ = "unknown"
default_app_config = 'biscuit.apps.exlibris.apps.ExlibrisConfig'
default_app_config = "biscuit.apps.exlibris.apps.ExlibrisConfig"
# Additional apps to add to BiscuIT's INSTALLED_APPS on loading
INSTALLED_APPS = ['isbn_field']
INSTALLED_APPS = ["isbn_field"]
......@@ -2,5 +2,5 @@ from biscuit.core.util.apps import AppConfig
class ExlibrisConfig(AppConfig):
name = 'biscuit.apps.exlibris'
verbose_name = 'BiscuIT - Exlibris (School book management)'
name = "biscuit.apps.exlibris"
verbose_name = "BiscuIT - Exlibris (School book management)"
from django import forms
from django.utils.translation import ugettext_lazy as _
from django_select2.forms import Select2Widget
from biscuit.core.models import Person
......@@ -10,47 +11,47 @@ from .models import Book, BookCopy
class BookAddISBNForm(forms.ModelForm):
class Meta:
model = Book
fields = ['isbn']
fields = ["isbn"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['isbn'].widget.attrs.update({'autofocus': 'autofocus'})
self.fields["isbn"].widget.attrs.update({"autofocus": "autofocus"})
class BookGetCopyForm(forms.ModelForm):
class Meta:
model = BookCopy
fields = ['barcode']
fields = ["barcode"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['barcode'].widget.attrs.update({'autofocus': 'autofocus'})
self.fields["barcode"].widget.attrs.update({"autofocus": "autofocus"})
class BookEditForm(forms.ModelForm):
class Meta:
model = Book
fields = ['isbn', 'title', 'author',
'publisher', 'year', 'edition', 'cover']
fields = ["isbn", "title", "author", "publisher", "year", "edition", "cover"]
class BookCopyEditForm(forms.ModelForm):
class Meta:
model = BookCopy
fields = ['borrower', 'borrow_count',
'condition', 'remarks', 'barcode']
fields = ["borrower", "borrow_count", "condition", "remarks", "barcode"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['borrower'].disabled = True
self.fields["borrower"].disabled = True
class BookCopiesBulkAddForm(forms.Form):
count = forms.IntegerField(label=_('Number of copies'))
count = forms.IntegerField(label=_("Number of copies"))
class PersonBorrowForm(forms.Form):
borrower = forms.ModelChoiceField(label=_('Person'), queryset=Person.objects.all(), widget=Select2Widget)
barcodes = forms.CharField(label=_('Barcodes'), widget=forms.Textarea)
borrower = forms.ModelChoiceField(
label=_("Person"), queryset=Person.objects.all(), widget=Select2Widget
)
barcodes = forms.CharField(label=_("Barcodes"), widget=forms.Textarea)
from django.utils.translation import ugettext_lazy as _
MENUS = {
'NAV_MENU_CORE': [
"NAV_MENU_CORE": [
{
'name': _('Books'),
'url': '#',
'root': True,
'validators': ['menu_generator.validators.is_authenticated', 'biscuit.core.util.core_helpers.has_person'],
'submenu': [
"name": _("Books"),
"url": "#",
"root": True,
"validators": [
"menu_generator.validators.is_authenticated",
"biscuit.core.util.core_helpers.has_person",
],
"submenu": [
{
'name': _('List of books'),
'url': 'books',
'validators': ['menu_generator.validators.is_authenticated']
"name": _("List of books"),
"url": "books",
"validators": ["menu_generator.validators.is_authenticated"],
},
{
'name': _('Add book'),
'url': 'add_book',
'validators': ['menu_generator.validators.is_authenticated']
"name": _("Add book"),
"url": "add_book",
"validators": ["menu_generator.validators.is_authenticated"],
},
{
'name': _('Open copy'),
'url': 'get_copy',
'validators': ['menu_generator.validators.is_authenticated']
"name": _("Open copy"),
"url": "get_copy",
"validators": ["menu_generator.validators.is_authenticated"],
},
{
'name': _('Person borrowing'),
'url': 'person_borrow',
'validators': ['menu_generator.validators.is_authenticated']
}
]
"name": _("Person borrowing"),
"url": "person_borrow",
"validators": ["menu_generator.validators.is_authenticated"],
},
],
}
]
}
# Generated by Django 2.2.5 on 2019-09-03 18:30
import biscuit.core.util.core_helpers
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
import isbn_field.fields
import isbn_field.validators
import biscuit.core.util.core_helpers
class Migration(migrations.Migration):
initial = True
dependencies = [
('core', '0001_initial'),
("core", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='Book',
name="Book",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=50, verbose_name='Book title')),
('author', models.CharField(blank=True, max_length=50, verbose_name='Author name')),
('publisher', models.CharField(blank=True, max_length=50, verbose_name='Publishing company')),
('edition', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Number of edition')),
('year', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Publishing year')),
('isbn', isbn_field.fields.ISBNField(max_length=28, unique=True, validators=[isbn_field.validators.ISBNValidator], verbose_name='ISBN number')),
('cover', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Photo of cover')),
('school', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("title", models.CharField(max_length=50, verbose_name="Book title")),
(
"author",
models.CharField(
blank=True, max_length=50, verbose_name="Author name"
),
),
(
"publisher",
models.CharField(
blank=True, max_length=50, verbose_name="Publishing company"
),
),
(
"edition",
models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="Number of edition"
),
),
(
"year",
models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="Publishing year"
),
),
(
"isbn",
isbn_field.fields.ISBNField(
max_length=28,
unique=True,
validators=[isbn_field.validators.ISBNValidator],
verbose_name="ISBN number",
),
),
(
"cover",
models.ImageField(
blank=True,
null=True,
upload_to="",
verbose_name="Photo of cover",
),
),
(
"school",
models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="core.School",
),
),
],
options={
'ordering': ['title', 'year', 'edition'],
'unique_together': {('school', 'title', 'author', 'edition', 'year')},
"ordering": ["title", "year", "edition"],
"unique_together": {("school", "title", "author", "edition", "year")},
},
),
migrations.CreateModel(
name='BookCopy',
name="BookCopy",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('copy_num', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Current number of copy')),
('borrow_count', models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Number of borrowings')),
('condition', models.CharField(blank=True, choices=[('U', 'New, unused'), ('N', 'New, used'), ('S', 'Slightly worn'), ('H', 'Heavily worn')], max_length=1, verbose_name='Condition')),
('remarks', models.CharField(blank=True, max_length=100, verbose_name='Remarks')),
('barcode', models.CharField(max_length=20, unique=True, verbose_name='Barcode')),
('book', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='copies', to='exlibris.Book')),
('borrower', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='books', to='core.Person')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"copy_num",
models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="Current number of copy"
),
),
(
"borrow_count",
models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="Number of borrowings"
),
),
(
"condition",
models.CharField(
blank=True,
choices=[
("U", "New, unused"),
("N", "New, used"),
("S", "Slightly worn"),
("H", "Heavily worn"),
],
max_length=1,
verbose_name="Condition",
),
),
(
"remarks",
models.CharField(
blank=True, max_length=100, verbose_name="Remarks"
),
),
(
"barcode",
models.CharField(
max_length=20, unique=True, verbose_name="Barcode"
),
),
(
"book",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="copies",
to="exlibris.Book",
),
),
(
"borrower",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="books",
to="core.Person",
),
),
],
options={
'ordering': ['book__title', 'book__year', 'book__edition', 'copy_num'],
"ordering": ["book__title", "book__year", "book__edition", "copy_num"],
},
),
]
......@@ -6,13 +6,15 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
('exlibris', '0001_initial'),
("core", "0001_initial"),
("exlibris", "0001_initial"),
]
operations = [
migrations.AddIndex(
model_name='bookcopy',
index=models.Index(fields=['barcode'], name='exlibris_bo_co_barc_55ac33_idx'),
model_name="bookcopy",
index=models.Index(
fields=["barcode"], name="exlibris_bo_co_barc_55ac33_idx"
),
),
]
......@@ -7,24 +7,31 @@ from isbn_field import ISBNField
class Book(models.Model):
"""A book that is available for borrowing by persons."""
title = models.CharField(verbose_name=_('Book title'), max_length=50)
author = models.CharField(verbose_name=_(
'Author name'), max_length=50, blank=True)
publisher = models.CharField(verbose_name=_(
'Publishing company'), max_length=50, blank=True)
title = models.CharField(verbose_name=_("Book title"), max_length=50)
author = models.CharField(verbose_name=_("Author name"), max_length=50, blank=True)
publisher = models.CharField(
verbose_name=_("Publishing company"), max_length=50, blank=True
)
edition = models.PositiveSmallIntegerField(
verbose_name=_('Number of edition'), blank=True, null=True)
verbose_name=_("Number of edition"), blank=True, null=True
)
year = models.PositiveSmallIntegerField(
verbose_name=_('Publishing year'), blank=True, null=True)
verbose_name=_("Publishing year"), blank=True, null=True
)
isbn = ISBNField(verbose_name=_('ISBN number'), unique=True)
isbn = ISBNField(verbose_name=_("ISBN number"), unique=True)
cover = models.ImageField(verbose_name=_(
'Photo of cover'), blank=True, null=True)
cover = models.ImageField(verbose_name=_("Photo of cover"), blank=True, null=True)
def __str__(self) -> str:
return '%s (%s, %d. %d %d)' % (self.title, self.author, self.edition, _('edition'), self.year)
return "%s (%s, %d. %d %d)" % (
self.title,
self.author,
self.edition,
_("edition"),
self.year,
)
def is_deleteable(self) -> bool:
for book_copy in self.copies.all():
......@@ -34,8 +41,8 @@ class Book(models.Model):
return True
class Meta:
unique_together = [['title', 'author', 'edition', 'year']]
ordering = ['title', 'year', 'edition']
unique_together = [["title", "author", "edition", "year"]]
ordering = ["title", "year", "edition"]
class BookCopy(models.Model):
......@@ -44,40 +51,52 @@ class BookCopy(models.Model):
"""
CONDITION_CHOICES = [
('U', _('New, unused')),
('N', _('New, used')),
('S', _('Slightly worn')),
('H', _('Heavily worn'))
("U", _("New, unused")),
("N", _("New, used")),
("S", _("Slightly worn")),
("H", _("Heavily worn")),
]
book = models.ForeignKey(
'Book', blank=True, null=True, on_delete=models.CASCADE, related_name='copies')
"Book", blank=True, null=True, on_delete=models.CASCADE, related_name="copies"
)
borrower = models.ForeignKey(
'core.Person', blank=True, null=True, on_delete=models.CASCADE, related_name='books')
"core.Person",
blank=True,
null=True,
on_delete=models.CASCADE,
related_name="books",
)
copy_num = models.PositiveSmallIntegerField(
verbose_name=_('Current number of copy'), blank=True, null=True)
verbose_name=_("Current number of copy"), blank=True, null=True
)
borrow_count = models.PositiveSmallIntegerField(
verbose_name=_('Number of borrowings'), blank=True, null=True)
verbose_name=_("Number of borrowings"), blank=True, null=True
)
condition = models.CharField(
verbose_name=_('Condition'), choices=CONDITION_CHOICES, max_length=1, blank=True)
remarks = models.CharField(
verbose_name=_('Remarks'), max_length=100, blank=True)
verbose_name=_("Condition"), choices=CONDITION_CHOICES, max_length=1, blank=True
)
remarks = models.CharField(verbose_name=_("Remarks"), max_length=100, blank=True)
barcode = models.CharField(verbose_name=_(
'Barcode'), max_length=20, unique=True)
barcode = models.CharField(verbose_name=_("Barcode"), max_length=20, unique=True)
def save(self, *args, **kwargs) -> None:
if not self.copy_num:
if self.book.copies.exclude(copy_num__isnull=True).exists():
self.copy_num = self.book.copies.exclude(
copy_num__isnull=True).order_by('copy_num').last().copy_num + 1
self.copy_num = (
self.book.copies.exclude(copy_num__isnull=True)
.order_by("copy_num")
.last()
.copy_num
+ 1
)
else:
self.copy_num = 1
if not self.barcode:
self.barcode = '%s-%s' % (self.book.isbn, self.copy_num)
self.barcode = "%s-%s" % (self.book.isbn, self.copy_num)
super().save(*args, **kwargs)
......@@ -85,5 +104,5 @@ class BookCopy(models.Model):
return not self.borrow_count and not self.borrower
class Meta:
ordering = ['book__title', 'book__year', 'book__edition', 'copy_num']
indexes = [models.Index(fields=['barcode'])]
ordering = ["book__title", "book__year", "book__edition", "copy_num"]
indexes = [models.Index(fields=["barcode"])]
......@@ -8,34 +8,48 @@ from django_tables2.utils import A
class BooksTable(tables.Table):
class Meta:
attrs = {
'class': 'table table-striped table-bordered table-hover table-responsive-xl'}
isbn = tables.LinkColumn('book_by_id', args=[A('id')])
title = tables.LinkColumn('book_by_id', args=[A('id')], attrs={'td': {
'data-poload': lambda record: reverse('book_card_by_id', kwargs={'id_': record.id})}})
"class": "table table-striped table-bordered table-hover table-responsive-xl"
}
isbn = tables.LinkColumn("book_by_id", args=[A("id")])
title = tables.LinkColumn(
"book_by_id",
args=[A("id")],
attrs={
"td": {
"data-poload": lambda record: reverse(
"book_card_by_id", kwargs={"id_": record.id}
)
}
},
)
author = tables.Column()
num_copies = tables.Column(verbose_name=_(
'Copies'), accessor='copies.count')
num_copies = tables.Column(verbose_name=_("Copies"), accessor="copies.count")
edit = tables.LinkColumn('edit_book_by_id', args=[
A('id')], verbose_name='', text=_('Edit'))
delete = tables.LinkColumn('delete_book_by_id', args=[
A('id')], verbose_name='', text=_('Delete'))
edit = tables.LinkColumn(
"edit_book_by_id", args=[A("id")], verbose_name="", text=_("Edit")
)
delete = tables.LinkColumn(
"delete_book_by_id", args=[A("id")], verbose_name="", text=_("Delete")
)
class BookCopiesTable(tables.Table):
class Meta:
attrs = {
'class': 'table table-striped table-bordered table-hover table-responsive-xl'}
"class": "table table-striped table-bordered table-hover table-responsive-xl"
}
copy_num = tables.Column()
borrower = tables.Column()
condition = tables.Column()
borrow_count = tables.Column()
edit = tables.LinkColumn('edit_book_copy_by_id', args=[
A('id')], verbose_name='', text=_('Edit'))
delete = tables.LinkColumn('delete_book_copy_by_id', args=[
A('id')], verbose_name='', text=_('Delete'))
edit = tables.LinkColumn(
"edit_book_copy_by_id", args=[A("id")], verbose_name="", text=_("Edit")
)
delete = tables.LinkColumn(
"delete_book_copy_by_id", args=[A("id")], verbose_name="", text=_("Delete")
)
......@@ -2,27 +2,36 @@ from django.urls import path
from . import views
urlpatterns = [
path('book/<int:id_>', views.book,
{'template': 'full'}, name='book_by_id'),
path('book/<int:id_>/card', views.book,
{'template': 'card'}, name='book_card_by_id'),
path('book/<int:id_>/edit', views.edit_book, name='edit_book_by_id'),
path('book/<int:id_>/delete', views.delete_book, name='delete_book_by_id'),
path('book/get_copy', views.get_copy, name='get_copy'),
path('book/copy/<barcode>', views.book_copy,
{'template': 'full'}, name='book_copy_by_barcode'),
path('book/<int:id_>/copies/labels', views.book_copies_labels,
name='copies_labels_by_book_id'),
path('book/copy/<int:id_>/edit', views.edit_book_copy,
name='edit_book_copy_by_id'),
path('book/copy/<int:id_>/delete', views.delete_book_copy,
name='delete_book_copy_by_id'),
path('book/<int:id_>/copies/add', views.add_book_copies,
name='add_copies_by_book_id'),
path('book/add', views.add_book, name='add_book'),
path('book/isbn/<isbn>', views.edit_book, name='edit_book_by_isbn'),
path('books', views.books, name='books'),
path('books/borrowing/person', views.person_borrow, name='person_borrow')
path("book/<int:id_>", views.book, {"template": "full"}, name="book_by_id"),
path(
"book/<int:id_>/card", views.book, {"template": "card"}, name="book_card_by_id"
),
path("book/<int:id_>/edit", views.edit_book, name="edit_book_by_id"),
path("book/<int:id_>/delete", views.delete_book, name="delete_book_by_id"),
path("book/get_copy", views.get_copy, name="get_copy"),
path(
"book/copy/<barcode>",
views.book_copy,
{"template": "full"},
name="book_copy_by_barcode",
),
path(
"book/<int:id_>/copies/labels",
views.book_copies_labels,
name="copies_labels_by_book_id",
),
path("book/copy/<int:id_>/edit", views.edit_book_copy, name="edit_book_copy_by_id"),
path(
"book/copy/<int:id_>/delete",
views.delete_book_copy,
name="delete_book_copy_by_id",
),
path(
"book/<int:id_>/copies/add", views.add_book_copies, name="add_copies_by_book_id"
),
path("book/add", views.add_book, name="add_book"),
path("book/isbn/<isbn>", views.edit_book, name="edit_book_by_isbn"),
path("books", views.books, name="books"),
path("books/borrowing/person", views.person_borrow, name="person_borrow"),
]
......@@ -6,24 +6,24 @@ import isbnlib
def get_book_meta(isbn: str) -> Dict[str, str]:
meta_result = {}
for provider in 'dnb', 'openl', 'goob':
for provider in "dnb", "openl", "goob":
try:
meta_result.update(isbnlib.meta(isbn, provider))
except:
pass
meta_translated = {}
if 'Title' in meta_result:
meta_translated['title'] = meta_result['Title']
if 'Authors' in meta_result:
meta_translated['author'] = ', '.join(meta_result['Authors'])
if 'Publisher' in meta_result:
meta_translated['publisher'] = meta_result['Publisher']
if 'Year' in meta_result:
if "Title" in meta_result:
meta_translated["title"] = meta_result["Title"]
if "Authors" in meta_result:
meta_translated["author"] = ", ".join(meta_result["Authors"])
if "Publisher" in meta_result:
meta_translated["publisher"] = meta_result["Publisher"]
if "Year" in meta_result:
try:
meta_translated['year'] = int(meta_result['Year'])
meta_translated["year"] = int(meta_result["Year"])
except ValueError:
pass
meta_translated['isbn'] = isbn
meta_translated["isbn"] = isbn
return meta_translated
from io import BytesIO
import os
from io import BytesIO
from tempfile import TemporaryDirectory
from typing import Optional
......@@ -9,17 +9,24 @@ from django.shortcuts import get_object_or_404, redirect, render
from django.utils.text import slugify
from django.utils.translation import ugettext as _
from django_tables2 import RequestConfig
import labels
import PIL
from django_tables2 import RequestConfig
from reportlab.graphics import barcode, shapes
from biscuit.core.models import School
from biscuit.core.util import messages
from .forms import BookAddISBNForm, BookEditForm, BookCopiesBulkAddForm, BookCopyEditForm, BookGetCopyForm, PersonBorrowForm
from .forms import (
BookAddISBNForm,
BookCopiesBulkAddForm,
BookCopyEditForm,
BookEditForm,
BookGetCopyForm,
PersonBorrowForm,
)
from .models import Book, BookCopy
from .tables import BooksTable, BookCopiesTable
from .tables import BookCopiesTable, BooksTable
from .util import get_book_meta
......@@ -33,9 +40,9 @@ def books(request: HttpRequest) -> HttpResponse:
# Build table
books_table = BooksTable(books)
RequestConfig(request).configure(books_table)
context['books_table'] = books_table
context["books_table"] = books_table
return render(request, 'exlibris/books.html', context)
return render(request, "exlibris/books.html", context)
@login_required
......@@ -44,17 +51,21 @@ def add_book(request: HttpRequest) -> HttpResponse:
book_add_isbn_form = BookAddISBNForm(request.POST or None)
if request.method == 'POST':
if request.method == "POST":
if book_add_isbn_form.is_valid():
return redirect('edit_book_by_isbn', isbn=book_add_isbn_form.cleaned_data['isbn'])
return redirect(
"edit_book_by_isbn", isbn=book_add_isbn_form.cleaned_data["isbn"]
)
context['book_add_isbn_form'] = book_add_isbn_form
context["book_add_isbn_form"] = book_add_isbn_form
return render(request, 'exlibris/add_book.html', context)
return render(request, "exlibris/add_book.html", context)
@login_required
def edit_book(request: HttpRequest, id_: Optional[int] = None, isbn: Optional[str] = None) -> HttpResponse:
def edit_book(
request: HttpRequest, id_: Optional[int] = None, isbn: Optional[str] = None
) -> HttpResponse:
context = {}
if id_:
......@@ -62,9 +73,10 @@ def edit_book(request: HttpRequest, id_: Optional[int] = None, isbn: Optional[st
book = get_object_or_404(Book, pk=id_)
book_edit_form = BookEditForm(
request.POST or None, request.FILES or None, instance=book)
request.POST or None, request.FILES or None, instance=book
)
context['book'] = book
context["book"] = book
else:
# If no id was passed, create a new book
......@@ -73,24 +85,24 @@ def edit_book(request: HttpRequest, id_: Optional[int] = None, isbn: Optional[st
initial = get_book_meta(isbn)
if initial:
messages.info(request, _(
'Information found for this ISBN was pre-filled.'))
messages.info(request, _("Information found for this ISBN was pre-filled."))
else:
messages.warning(request, _('No information found for this ISBN.'))
messages.warning(request, _("No information found for this ISBN."))
book_edit_form = BookEditForm(
request.POST or None, request.FILES or None, initial=initial)
request.POST or None, request.FILES or None, initial=initial
)
if request.method == 'POST':
if request.method == "POST":
if book_edit_form.is_valid():
book_edit_form.save(commit=True)
messages.success(request, _('The book has been saved.'))
return redirect('books')
messages.success(request, _("The book has been saved."))
return redirect("books")
context['book_edit_form'] = book_edit_form
context["book_edit_form"] = book_edit_form
return render(request, 'exlibris/edit_book.html', context)
return render(request, "exlibris/edit_book.html", context)
@login_required
......@@ -100,12 +112,13 @@ def delete_book(request: HttpRequest, id_: int) -> HttpResponse:
if book.is_deleteable():
book.delete()
messages.success(request, _('Book %s has been deleted.') % title)
messages.success(request, _("Book %s has been deleted.") % title)
else:
messages.error(request,
_('Books with copies that have been borrowed cannot be deleted.'))
messages.error(
request, _("Books with copies that have been borrowed cannot be deleted.")
)
return redirect('books')
return redirect("books")
@login_required
......@@ -113,7 +126,7 @@ def book(request: HttpRequest, id_: int, template: str) -> HttpResponse:
context = {}
book = get_object_or_404(Book, pk=id_)
context['book'] = book
context["book"] = book
# Get all copies
copies = book.copies.all()
......@@ -121,9 +134,9 @@ def book(request: HttpRequest, id_: int, template: str) -> HttpResponse:
# Build table
copies_table = BookCopiesTable(copies)
RequestConfig(request).configure(copies_table)
context['copies_table'] = copies_table
context["copies_table"] = copies_table
return render(request, 'exlibris/book_%s.html' % template, context)
return render(request, "exlibris/book_%s.html" % template, context)
@login_required
......@@ -131,25 +144,28 @@ def add_book_copies(request: HttpRequest, id_: int) -> HttpResponse:
context = {}
book = get_object_or_404(Book, pk=id_)
context['book'] = book
context["book"] = book
bulk_add_form = BookCopiesBulkAddForm(request.POST or None)
if request.method == 'POST':
if request.method == "POST":
if bulk_add_form.is_valid():
new_copies = [BookCopy(book=book) for _ in range(
bulk_add_form.cleaned_data['count'])]
new_copies = [
BookCopy(book=book) for _ in range(bulk_add_form.cleaned_data["count"])
]
for new_copy in new_copies:
new_copy.save()
messages.success(request, _('%d copies have been created.') %
bulk_add_form.cleaned_data['count'])
return redirect('book_by_id', id_=book.pk)
messages.success(
request,
_("%d copies have been created.") % bulk_add_form.cleaned_data["count"],
)
return redirect("book_by_id", id_=book.pk)
context['book'] = book
context['bulk_add_form'] = bulk_add_form
context["book"] = book
context["bulk_add_form"] = bulk_add_form
return render(request, 'exlibris/add_book_copies.html', context)
return render(request, "exlibris/add_book_copies.html", context)
@login_required
......@@ -157,21 +173,20 @@ def edit_book_copy(request: HttpRequest, id_: int) -> HttpResponse:
context = {}
book_copy = get_object_or_404(BookCopy, pk=id_)
book_copy_edit_form = BookCopyEditForm(
request.POST or None, instance=book_copy)
book_copy_edit_form = BookCopyEditForm(request.POST or None, instance=book_copy)
context['book_copy'] = book_copy
context["book_copy"] = book_copy
if request.method == 'POST':
if request.method == "POST":
if book_copy_edit_form.is_valid():
book_copy_edit_form.save(commit=True)
messages.success(request, _('The book copy has been saved.'))
return redirect('book_by_id', id_=book_copy.book.pk)
messages.success(request, _("The book copy has been saved."))
return redirect("book_by_id", id_=book_copy.book.pk)
context['book_copy_edit_form'] = book_copy_edit_form
context["book_copy_edit_form"] = book_copy_edit_form
return render(request, 'exlibris/edit_book_copy.html', context)
return render(request, "exlibris/edit_book_copy.html", context)
@login_required
......@@ -181,13 +196,13 @@ def delete_book_copy(request: HttpRequest, id_: int) -> HttpResponse:
if book_copy.is_deleteable():
book_copy.delete()
messages.success(request, _(
'Book copy of %s has been deleted.') % book.title)
messages.success(request, _("Book copy of %s has been deleted.") % book.title)
else:
messages.error(request,
_('Book copies that have been borrowed cannot be deleted.'))
messages.error(
request, _("Book copies that have been borrowed cannot be deleted.")
)
return redirect('book_by_id', id_=book.id)
return redirect("book_by_id", id_=book.id)
@login_required
......@@ -196,32 +211,46 @@ def book_copies_labels(request: HttpRequest, id_: int) -> HttpResponse:
copies = book.copies.all()
with TemporaryDirectory() as temp_dir:
def draw_label(label, width, height, obj):
title = shapes.String(0, height - 10, obj.book.title, fontSize=10)
label.add(title)
copy_desc = shapes.String(
0, height - 20, _('Copy number: %d') % obj.copy_num, fontSize=8)
0, height - 20, _("Copy number: %d") % obj.copy_num, fontSize=8
)
label.add(copy_desc)
barcode_raw = barcode.createBarcodeImageInMemory(
'Code128', value=obj.barcode, width=400, height=120)
"Code128", value=obj.barcode, width=400, height=120
)
barcode_image = PIL.Image.open(BytesIO(barcode_raw))
barcode_file = os.path.join(temp_dir, "%s.png" % obj.barcode)
barcode_image.save(barcode_file, 'png')
label.add(shapes.Image(0, 30, width, 30,
os.path.join(temp_dir, barcode_file)))
barcode_image.save(barcode_file, "png")
label.add(
shapes.Image(0, 30, width, 30, os.path.join(temp_dir, barcode_file))
)
school = shapes.String(
0, 8, School.objects.first().name, fontSize=10)
school = shapes.String(0, 8, School.objects.first().name, fontSize=10)
label.add(school)
bottom_line = shapes.String(
0, 0, 'BiscuIT, the Free School Information System', fontSize=6)
0, 0, "BiscuIT, the Free School Information System", fontSize=6
)
label.add(bottom_line)
specs = labels.Specification(
210, 297, 3, 7, 63, 40, left_padding=3, top_padding=3, bottom_padding=3, right_padding=3)
210,
297,
3,
7,
63,
40,
left_padding=3,
top_padding=3,
bottom_padding=3,
right_padding=3,
)
sheet = labels.Sheet(specs, draw_label)
sheet.add_labels(copies)
......@@ -229,7 +258,7 @@ def book_copies_labels(request: HttpRequest, id_: int) -> HttpResponse:
pdf_file = os.path.join(temp_dir, "label_%s.pdf" % slugify(book.title))
sheet.save(pdf_file)
return FileResponse(open(pdf_file, 'rb'), as_attachment=True)
return FileResponse(open(pdf_file, "rb"), as_attachment=True)
@login_required
......@@ -238,13 +267,15 @@ def get_copy(request: HttpRequest) -> HttpResponse:
book_copy_form = BookGetCopyForm(request.POST or None)
if request.method == 'POST':
if request.method == "POST":
if book_copy_form.is_valid():
return redirect('book_copy_by_barcode', barcode=book_copy_form.cleaned_data['barcode'])
return redirect(
"book_copy_by_barcode", barcode=book_copy_form.cleaned_data["barcode"]
)
context['book_copy_form'] = book_copy_form
context["book_copy_form"] = book_copy_form
return render(request, 'exlibris/get_copy.html', context)
return render(request, "exlibris/get_copy.html", context)
@login_required
......@@ -252,9 +283,9 @@ def book_copy(request: HttpRequest, barcode: str, template: str) -> HttpResponse
context = {}
book_copy = get_object_or_404(BookCopy, barcode=barcode)
context['book_copy'] = book_copy
context["book_copy"] = book_copy
return render(request, 'exlibris/copy_%s.html' % template, context)
return render(request, "exlibris/copy_%s.html" % template, context)
@login_required
......@@ -263,27 +294,35 @@ def person_borrow(request: HttpRequest) -> HttpResponse:
person_borrow_form = PersonBorrowForm(request.POST or None)
if request.method == 'POST':
if request.method == "POST":
if person_borrow_form.is_valid():
person = person_borrow_form.cleaned_data['borrower']
person = person_borrow_form.cleaned_data["borrower"]
for barcode_line in person_borrow_form.cleaned_data['barcodes'].splitlines():
for barcode_line in person_borrow_form.cleaned_data[
"barcodes"
].splitlines():
try:
book_copy = BookCopy.objects.get(barcode=barcode_line)
except BookCopy.DoesNotExist:
messages.error(request, _('Barcode %(barcode)s is invalid.') %
{'barcode': barcode_line})
messages.error(
request,
_("Barcode %(barcode)s is invalid.")
% {"barcode": barcode_line},
)
continue
book_copy.borrower = person
book_copy.borrow_count = book_copy.borrow_count + 1
book_copy.save()
messages.success(request, _('Book %(title)s borrowed to %(person)s.') %
{'title': book_copy.book.title, 'person': person})
messages.success(
request,
_("Book %(title)s borrowed to %(person)s.")
% {"title": book_copy.book.title, "person": person},
)
person_borrow_form = PersonBorrowForm()
context['person_borrow_form'] = person_borrow_form
context["person_borrow_form"] = person_borrow_form
return render(request, 'exlibris/person_borrow.html', context)
return render(request, "exlibris/person_borrow.html", context)