mirror of
https://gitlab.crans.org/mediatek/med.git
synced 2024-12-24 17:42:23 +00:00
Adapt media to ISBN
This commit is contained in:
parent
79ad58997a
commit
2f872eccce
@ -0,0 +1,5 @@
|
||||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'logs.apps.LogsConfig'
|
@ -1,3 +1,7 @@
|
||||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'media.apps.MediaConfig'
|
@ -17,18 +17,21 @@ class AuteurAdmin(VersionAdmin):
|
||||
|
||||
|
||||
class MediaAdmin(VersionAdmin):
|
||||
list_display = ('titre', 'authors', 'cote')
|
||||
search_fields = ('titre', 'auteur__nom', 'cote')
|
||||
autocomplete_fields = ('auteur',)
|
||||
list_display = ('title', 'authors_list', 'side_title', 'isbn')
|
||||
search_fields = ('title', 'authors__nom', 'side_title', 'subtitle', 'isbn')
|
||||
autocomplete_fields = ('authors',)
|
||||
date_hierarchy = 'publish_date'
|
||||
|
||||
def authors(self, obj):
|
||||
return ", ".join([a.nom for a in obj.auteur.all()])
|
||||
def authors_list(self, obj):
|
||||
return ", ".join([a.nom for a in obj.authors.all()])
|
||||
|
||||
authors_list.short_description = _('authors')
|
||||
|
||||
|
||||
class EmpruntAdmin(VersionAdmin):
|
||||
list_display = ('media', 'user', 'date_emprunt', 'date_rendu',
|
||||
'permanencier_emprunt', 'permanencier_rendu_custom')
|
||||
search_fields = ('media__titre', 'media__cote', 'user__username',
|
||||
search_fields = ('media__title', 'media__side_title', 'user__username',
|
||||
'date_emprunt', 'date_rendu')
|
||||
date_hierarchy = 'date_emprunt'
|
||||
autocomplete_fields = ('media', 'user', 'permanencier_emprunt',
|
||||
|
@ -1,3 +1,7 @@
|
||||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
55
media/fields.py
Normal file
55
media/fields.py
Normal file
@ -0,0 +1,55 @@
|
||||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
Based on https://github.com/secnot/django-isbn-field
|
||||
"""
|
||||
|
||||
from django.core.validators import EMPTY_VALUES
|
||||
from django.db.models import CharField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .validators import isbn_validator
|
||||
|
||||
|
||||
class ISBNField(CharField):
|
||||
description = _("ISBN-10 or ISBN-13")
|
||||
|
||||
def __init__(self, clean_isbn=True, *args, **kwargs):
|
||||
self.clean_isbn = clean_isbn
|
||||
kwargs['max_length'] = kwargs[
|
||||
'max_length'] if 'max_length' in kwargs else 28
|
||||
kwargs['verbose_name'] = kwargs[
|
||||
'verbose_name'] if 'verbose_name' in kwargs else u'ISBN'
|
||||
kwargs['validators'] = [isbn_validator]
|
||||
super(ISBNField, self).__init__(*args, **kwargs)
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'min_length': 10,
|
||||
'validators': [isbn_validator],
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
return super(ISBNField, self).formfield(**defaults)
|
||||
|
||||
def deconstruct(self):
|
||||
name, path, args, kwargs = super(ISBNField, self).deconstruct()
|
||||
# Only include clean_isbn in kwarg if it's not the default value
|
||||
if not self.clean_isbn:
|
||||
kwargs['clean_isbn'] = self.clean_isbn
|
||||
return name, path, args, kwargs
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
"""
|
||||
Remove dashes, spaces, and convert isbn to uppercase before saving
|
||||
when clean_isbn is enabled
|
||||
"""
|
||||
value = getattr(model_instance, self.attname)
|
||||
if self.clean_isbn and value not in EMPTY_VALUES:
|
||||
cleaned_isbn = value.replace(' ', '').replace('-', '').upper()
|
||||
setattr(model_instance, self.attname, cleaned_isbn)
|
||||
return super(ISBNField, self).pre_save(model_instance, add)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.value
|
18
media/migrations/0010_auto_20190811_0901.py
Normal file
18
media/migrations/0010_auto_20190811_0901.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0009_auto_20190802_1455'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='media',
|
||||
old_name='titre',
|
||||
new_name='title',
|
||||
),
|
||||
]
|
23
media/migrations/0011_auto_20190811_0903.py
Normal file
23
media/migrations/0011_auto_20190811_0903.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0010_auto_20190811_0901'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='media',
|
||||
name='subtitle',
|
||||
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='subtitle'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='media',
|
||||
name='title',
|
||||
field=models.CharField(max_length=255, verbose_name='title'),
|
||||
),
|
||||
]
|
18
media/migrations/0012_media_external_url.py
Normal file
18
media/migrations/0012_media_external_url.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0011_auto_20190811_0903'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='media',
|
||||
name='external_url',
|
||||
field=models.URLField(blank=True, null=True, verbose_name='external URL'),
|
||||
),
|
||||
]
|
18
media/migrations/0013_auto_20190811_0907.py
Normal file
18
media/migrations/0013_auto_20190811_0907.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:07
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0012_media_external_url'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='media',
|
||||
old_name='auteur',
|
||||
new_name='authors',
|
||||
),
|
||||
]
|
23
media/migrations/0014_auto_20190811_0908.py
Normal file
23
media/migrations/0014_auto_20190811_0908.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:08
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0013_auto_20190811_0907'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='media',
|
||||
name='number_of_pages',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='number of pages'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='media',
|
||||
name='authors',
|
||||
field=models.ManyToManyField(to='media.Auteur', verbose_name='authors'),
|
||||
),
|
||||
]
|
18
media/migrations/0015_media_publish_date.py
Normal file
18
media/migrations/0015_media_publish_date.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0014_auto_20190811_0908'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='media',
|
||||
name='publish_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='publish date'),
|
||||
),
|
||||
]
|
20
media/migrations/0016_media_isbn.py
Normal file
20
media/migrations/0016_media_isbn.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:17
|
||||
|
||||
from django.db import migrations
|
||||
import media.fields
|
||||
import media.validators
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0015_media_publish_date'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='media',
|
||||
name='isbn',
|
||||
field=media.fields.ISBNField(blank=True, max_length=28, null=True, validators=[media.validators.isbn_validator], verbose_name='ISBN'),
|
||||
),
|
||||
]
|
20
media/migrations/0017_auto_20190811_0918.py
Normal file
20
media/migrations/0017_auto_20190811_0918.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:18
|
||||
|
||||
from django.db import migrations
|
||||
import media.fields
|
||||
import media.validators
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0016_media_isbn'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='media',
|
||||
name='isbn',
|
||||
field=media.fields.ISBNField(blank=True, help_text='You may be able to scan it from a bar code.', max_length=28, null=True, validators=[media.validators.isbn_validator], verbose_name='ISBN'),
|
||||
),
|
||||
]
|
18
media/migrations/0018_auto_20190811_0918.py
Normal file
18
media/migrations/0018_auto_20190811_0918.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:18
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0017_auto_20190811_0918'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='media',
|
||||
old_name='cote',
|
||||
new_name='side_title',
|
||||
),
|
||||
]
|
18
media/migrations/0019_auto_20190811_0919.py
Normal file
18
media/migrations/0019_auto_20190811_0919.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.2.4 on 2019-08-11 07:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('media', '0018_auto_20190811_0918'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='media',
|
||||
name='side_title',
|
||||
field=models.CharField(max_length=255, verbose_name='side title'),
|
||||
),
|
||||
]
|
@ -6,6 +6,8 @@ from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .fields import ISBNField
|
||||
|
||||
|
||||
class Auteur(models.Model):
|
||||
nom = models.CharField(max_length=255, unique=True)
|
||||
@ -19,12 +21,48 @@ class Auteur(models.Model):
|
||||
|
||||
|
||||
class Media(models.Model):
|
||||
titre = models.CharField(max_length=255)
|
||||
cote = models.CharField(max_length=31)
|
||||
auteur = models.ManyToManyField('Auteur')
|
||||
isbn = ISBNField(
|
||||
_('ISBN'),
|
||||
help_text=_('You may be able to scan it from a bar code.'),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
title = models.CharField(
|
||||
verbose_name=_('title'),
|
||||
max_length=255,
|
||||
)
|
||||
subtitle = models.CharField(
|
||||
verbose_name=_('subtitle'),
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
external_url = models.URLField(
|
||||
verbose_name=_('external URL'),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
side_title = models.CharField(
|
||||
verbose_name=_('side title'),
|
||||
max_length=255,
|
||||
)
|
||||
authors = models.ManyToManyField(
|
||||
'Auteur',
|
||||
verbose_name=_('authors'),
|
||||
)
|
||||
number_of_pages = models.PositiveIntegerField(
|
||||
verbose_name=_('number of pages'),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
publish_date = models.DateField(
|
||||
verbose_name=_('publish date'),
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.titre) + ' - ' + str(self.auteur.all().first())
|
||||
return str(self.title) + ' - ' + str(self.authors.all().first())
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("medium")
|
||||
|
46
media/static/media/isbn_fetcher.js
Normal file
46
media/static/media/isbn_fetcher.js
Normal file
@ -0,0 +1,46 @@
|
||||
// curl 'https://openlibrary.org/api/books?bibkeys=ISBN:0201558025&format=json&jscmd=data'
|
||||
a = {
|
||||
"ISBN:0201558025": {
|
||||
"publishers": [{"name": "Addison-Wesley"}],
|
||||
"pagination": "xiii, 657 p. :",
|
||||
"identifiers": {
|
||||
"lccn": ["93040325"],
|
||||
"openlibrary": ["OL1429049M"],
|
||||
"isbn_10": ["0201558025"],
|
||||
"wikidata": ["Q15303722"],
|
||||
"goodreads": ["112243"],
|
||||
"librarything": ["45844"]
|
||||
},
|
||||
//"subtitle": "a foundation for computer science",
|
||||
//"title": "Concrete mathematics",
|
||||
//"url": "https://openlibrary.org/books/OL1429049M/Concrete_mathematics",
|
||||
"classifications": {"dewey_decimal_class": ["510"], "lc_classifications": ["QA39.2 .G733 1994"]},
|
||||
"notes": "Includes bibliographical references (p. 604-631) and index.",
|
||||
"number_of_pages": 657,
|
||||
"cover": {
|
||||
"small": "https://covers.openlibrary.org/b/id/135182-S.jpg",
|
||||
"large": "https://covers.openlibrary.org/b/id/135182-L.jpg",
|
||||
"medium": "https://covers.openlibrary.org/b/id/135182-M.jpg"
|
||||
},
|
||||
"subjects": [{
|
||||
"url": "https://openlibrary.org/subjects/computer_science",
|
||||
"name": "Computer science"
|
||||
}, {"url": "https://openlibrary.org/subjects/mathematics", "name": "Mathematics"}],
|
||||
"publish_date": "1994",
|
||||
"key": "/books/OL1429049M",
|
||||
"authors": [{
|
||||
"url": "https://openlibrary.org/authors/OL720958A/Ronald_L._Graham",
|
||||
"name": "Ronald L. Graham"
|
||||
}, {
|
||||
"url": "https://openlibrary.org/authors/OL229501A/Donald_Knuth",
|
||||
"name": "Donald Knuth"
|
||||
}, {"url": "https://openlibrary.org/authors/OL2669938A/Oren_Patashnik", "name": "Oren Patashnik"}],
|
||||
"by_statement": "Ronald L. Graham, Donald E. Knuth, Oren Patashnik.",
|
||||
"publish_places": [{"name": "Reading, Mass"}],
|
||||
"ebooks": [{
|
||||
"formats": {},
|
||||
"preview_url": "https://archive.org/details/concretemathemat00grah_444",
|
||||
"availability": "restricted"
|
||||
}]
|
||||
}
|
||||
}
|
31
media/validators.py
Normal file
31
media/validators.py
Normal file
@ -0,0 +1,31 @@
|
||||
# -*- mode: python; coding: utf-8 -*-
|
||||
# Copyright (C) 2017-2019 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
Based on https://github.com/secnot/django-isbn-field
|
||||
"""
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from six import string_types
|
||||
from stdnum import isbn
|
||||
|
||||
|
||||
def isbn_validator(raw_isbn):
|
||||
"""Check string is a valid ISBN number"""
|
||||
isbn_to_check = raw_isbn.replace('-', '').replace(' ', '')
|
||||
|
||||
if not isinstance(isbn_to_check, string_types):
|
||||
raise ValidationError(_(u'Invalid ISBN: Not a string'))
|
||||
|
||||
if len(isbn_to_check) != 10 and len(isbn_to_check) != 13:
|
||||
raise ValidationError(_(u'Invalid ISBN: Wrong length'))
|
||||
|
||||
if not isbn.is_valid(isbn_to_check):
|
||||
raise ValidationError(_(u'Invalid ISBN: Failed checksum'))
|
||||
|
||||
if isbn_to_check != isbn_to_check.upper():
|
||||
raise ValidationError(_(u'Invalid ISBN: Only upper case allowed'))
|
||||
|
||||
return True
|
@ -4,4 +4,5 @@ Pillow==5.4.1
|
||||
pytz==2019.1
|
||||
six==1.12.0
|
||||
sqlparse==0.2.4
|
||||
django-reversion==3.0.3
|
||||
django-reversion==3.0.3
|
||||
python-stdnum==1.10
|
Loading…
Reference in New Issue
Block a user