diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 433f026..a7e7bb5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,30 +2,6 @@ stages: - test - quality-assurance -py37-django22: - stage: test - image: debian:buster-backports - before_script: - - > - apt-get update && - apt-get install --no-install-recommends -t buster-backports -y - python3-django python3-django-casclient python3-django-reversion python3-djangorestframework - python3-docutils python3-pil python3-tz python3-six python3-sqlparse python3-stdnum python3-yaml python3-coreapi tox - script: tox -e py37 - -py38-django22: - stage: test - image: ubuntu:20.04 - before_script: - # Fix tzdata prompt - - ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime && echo Europe/Paris > /etc/timezone - - > - apt-get update && - apt-get install --no-install-recommends -y - python3-django python3-django-casclient python3-django-reversion python3-djangorestframework - python3-docutils python3-pil python3-tz python3-six python3-sqlparse python3-stdnum python3-yaml python3-coreapi tox - script: tox -e py38 - py39-django22: stage: test image: debian:bullseye @@ -33,13 +9,13 @@ py39-django22: - > apt-get update && apt-get install --no-install-recommends -y - python3-django python3-django-casclient python3-django-reversion python3-djangorestframework - python3-docutils python3-pil python3-tz python3-six python3-sqlparse python3-stdnum python3-yaml python3-coreapi tox + python3-django python3-django-reversion python3-djangorestframework + python3-docutils python3-requests tox script: tox -e py39 linters: stage: quality-assurance - image: debian:buster-backports + image: debian:bullseye before_script: - apt-get update && apt-get install -y tox script: tox -e linters diff --git a/README.md b/README.md index 09686d7..b53c789 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,6 @@ bureau media | Can add borrowed item media | Can change borrowed item media | Can delete borrowed item - users | Can view adhesion - users | Can add adhesion - users | Can change adhesion - users | Can delete adhesion users | Can view user users | Can add user users | Can change user diff --git a/med/settings.py b/med/settings.py index 0786c1b..69904ef 100644 --- a/med/settings.py +++ b/med/settings.py @@ -35,6 +35,7 @@ INSTALLED_APPS = [ # External apps 'reversion', 'rest_framework', + 'django_extensions', # Django contrib 'django.contrib.admin', diff --git a/med/urls.py b/med/urls.py index 32cee63..2ba3eeb 100644 --- a/med/urls.py +++ b/med/urls.py @@ -13,21 +13,21 @@ from .admin import admin_site # API router router = routers.DefaultRouter() -router.register(r'authors', media.views.AuteurViewSet) -router.register(r'media/bd', media.views.BDViewSet) +router.register(r'authors', media.views.AuthorViewSet) +router.register(r'media/comic', media.views.ComicViewSet) router.register(r'media/manga', media.views.MangaViewSet) router.register(r'media/cd', media.views.CDViewSet) -router.register(r'media/vinyle', media.views.VinyleViewSet) -router.register(r'media/roman', media.views.RomanViewSet) -router.register(r'media/revue', media.views.RevueViewSet) -router.register(r'media/future', media.views.FutureMediaViewSet) +router.register(r'media/vinyl', media.views.VinylViewSet) +router.register(r'media/novel', media.views.NovelViewSet) +router.register(r'media/review', media.views.ReviewViewSet) +router.register(r'media/future', media.views.FutureMediumViewSet) router.register(r'borrowed_items', media.views.EmpruntViewSet) -router.register(r'games', media.views.JeuViewSet) +router.register(r'games', media.views.GameViewSet) router.register(r'users', users.views.UserViewSet) router.register(r'groups', users.views.GroupViewSet) urlpatterns = [ - path('', media.views.index, name='index'), + path('', media.views.IndexView, name='index'), # Include project routers path('users/', include('users.urls')), diff --git a/media/admin.py b/media/admin.py index 6e53ebb..4ee4522 100644 --- a/media/admin.py +++ b/media/admin.py @@ -9,16 +9,16 @@ from med.admin import admin_site from reversion.admin import VersionAdmin from .forms import MediaAdminForm -from .models import Auteur, BD, CD, Emprunt, FutureMedia, Jeu, Manga,\ - Revue, Roman, Vinyle +from .models import Author, CD, Comic, Emprunt, FutureMedium, Game, Manga,\ + Novel, Review, Vinyl -class AuteurAdmin(VersionAdmin): +class AuthorAdmin(VersionAdmin): list_display = ('name',) search_fields = ('name',) -class MediaAdmin(VersionAdmin): +class MediumAdmin(VersionAdmin): list_display = ('__str__', 'authors_list', 'side_identifier', 'isbn', 'external_link') search_fields = ('title', 'authors__name', 'side_identifier', 'subtitle', @@ -61,7 +61,7 @@ class MediaAdmin(VersionAdmin): extra_context=extra_context) -class FutureMediaAdmin(VersionAdmin): +class FutureMediumAdmin(VersionAdmin): list_display = ('isbn',) search_fields = ('isbn',) @@ -88,7 +88,7 @@ class CDAdmin(VersionAdmin): authors_list.short_description = _('authors') -class VinyleAdmin(VersionAdmin): +class VinylAdmin(VersionAdmin): list_display = ('title', 'authors_list', 'side_identifier', 'rpm',) search_fields = ('title', 'authors__name', 'side_identifier', 'rpm',) autocomplete_fields = ('authors',) @@ -99,7 +99,7 @@ class VinyleAdmin(VersionAdmin): authors_list.short_description = _('authors') -class RevueAdmin(VersionAdmin): +class ReviewAdmin(VersionAdmin): list_display = ('__str__', 'number', 'year', 'month', 'day', 'double',) search_fields = ('title', 'number', 'year',) @@ -140,20 +140,20 @@ class EmpruntAdmin(VersionAdmin): return super().add_view(request, form_url, extra_context) -class JeuAdmin(VersionAdmin): - list_display = ('name', 'proprietaire', 'duree', 'nombre_joueurs_min', - 'nombre_joueurs_max', 'comment') - search_fields = ('name', 'proprietaire__username', 'duree', 'comment') - autocomplete_fields = ('proprietaire',) +class GameAdmin(VersionAdmin): + list_display = ('name', 'owner', 'duration', 'players_min', + 'players_max', 'comment') + search_fields = ('name', 'owner__username', 'duration', 'comment') + autocomplete_fields = ('owner',) -admin_site.register(Auteur, AuteurAdmin) -admin_site.register(BD, MediaAdmin) -admin_site.register(Manga, MediaAdmin) -admin_site.register(Roman, MediaAdmin) +admin_site.register(Author, AuthorAdmin) +admin_site.register(Comic, MediumAdmin) +admin_site.register(Manga, MediumAdmin) +admin_site.register(Novel, MediumAdmin) admin_site.register(CD, CDAdmin) -admin_site.register(Vinyle, VinyleAdmin) -admin_site.register(Revue, RevueAdmin) -admin_site.register(FutureMedia, FutureMediaAdmin) +admin_site.register(Vinyl, VinylAdmin) +admin_site.register(Review, ReviewAdmin) +admin_site.register(FutureMedium, FutureMediumAdmin) admin_site.register(Emprunt, EmpruntAdmin) -admin_site.register(Jeu, JeuAdmin) +admin_site.register(Game, GameAdmin) diff --git a/media/forms.py b/media/forms.py index f884597..04ed691 100644 --- a/media/forms.py +++ b/media/forms.py @@ -13,7 +13,7 @@ from django.db.models import QuerySet from django.forms import ModelForm from django.utils.translation import gettext_lazy as _ -from .models import Auteur, BD +from .models import Author, Comic from .scraper import BedetequeScraper @@ -123,7 +123,7 @@ class MediaAdminForm(ModelForm): self.cleaned_data["publish_date"] += "-01" self.cleaned_data["number_of_pages"] = data["pages"] self.cleaned_data["authors"] = \ - list(Auteur.objects.get_or_create(name=author_name)[0] + list(Author.objects.get_or_create(name=author_name)[0] for author_name in data["authors"]) self.cleaned_data["external_url"] = data["image"] return True @@ -138,7 +138,7 @@ class MediaAdminForm(ModelForm): if not r: return False # If results, then take the most accurate - data = scraper.scrap_bd_info(r[0]) + data = scraper.scrap_comic_info(r[0]) self.cleaned_data.update(data) return True @@ -195,7 +195,7 @@ class MediaAdminForm(ModelForm): if 'authors' in info: for author in info['authors']: - author_obj = Auteur.objects.get_or_create( + author_obj = Author.objects.get_or_create( name=author)[0] self.cleaned_data['authors'].append(author_obj) @@ -269,7 +269,7 @@ class MediaAdminForm(ModelForm): if 'authors' in data: for author in data['authors']: - author_obj = Auteur.objects.get_or_create( + author_obj = Author.objects.get_or_create( name=author['name'])[0] self.cleaned_data['authors'].append(author_obj) @@ -343,7 +343,7 @@ class MediaAdminForm(ModelForm): self.add_error(name, e) class Meta: - model = BD + model = Comic fields = ('isbn', 'title', 'subtitle', 'external_url', 'side_identifier', 'authors', 'number_of_pages', 'publish_date', 'present', ) diff --git a/media/locale/fr/LC_MESSAGES/django.po b/media/locale/fr/LC_MESSAGES/django.po index 901c200..4d4f603 100644 --- a/media/locale/fr/LC_MESSAGES/django.po +++ b/media/locale/fr/LC_MESSAGES/django.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-10-02 13:02+0200\n" +"POT-Creation-Date: 2021-10-23 18:27+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -13,20 +13,20 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: admin.py:34 admin.py:89 admin.py:100 models.py:29 models.py:65 models.py:130 +#: admin.py:33 admin.py:88 admin.py:99 models.py:29 models.py:65 models.py:130 #: models.py:192 models.py:243 models.py:274 msgid "authors" msgstr "auteurs" -#: admin.py:44 +#: admin.py:43 msgid "external url" msgstr "URL externe" -#: admin.py:127 +#: admin.py:126 msgid "Turn back" msgstr "Rendre" -#: admin.py:130 models.py:407 +#: admin.py:129 models.py:407 msgid "given back to" msgstr "rendu à" @@ -34,7 +34,7 @@ msgstr "rendu à" msgid "ISBN-10 or ISBN-13" msgstr "ISBN-10 ou ISBN-13" -#: forms.py:244 +#: forms.py:301 msgid "This ISBN is not found." msgstr "L'ISBN n'a pas été trouvé." @@ -94,11 +94,11 @@ msgid "Tell that the medium is present in the Mediatek." msgstr "Indique que le medium est présent à la Mediatek." #: models.py:93 models.py:355 -msgid "BD" +msgid "comic" msgstr "BD" #: models.py:94 -msgid "BDs" +msgid "comics" msgstr "BDs" #: models.py:155 @@ -110,11 +110,11 @@ msgid "mangas" msgstr "mangas" #: models.py:217 -msgid "roman" +msgid "novel" msgstr "roman" #: models.py:218 -msgid "romans" +msgid "novels" msgstr "romans" #: models.py:234 @@ -130,12 +130,12 @@ msgid "45 RPM" msgstr "45 TPM" #: models.py:256 -msgid "vinyle" +msgid "vinyl" msgstr "vinyle" #: models.py:257 -msgid "vinyles" -msgstr "vinyle" +msgid "vinyls" +msgstr "vinyles" #: models.py:287 msgid "CD" @@ -166,11 +166,11 @@ msgid "double" msgstr "double" #: models.py:338 -msgid "revue" +msgid "review" msgstr "revue" #: models.py:339 -msgid "revues" +msgid "reviews" msgstr "revues" #: models.py:353 @@ -277,12 +277,6 @@ msgstr "ISBN invalide : mauvaise longueur" msgid "Invalid ISBN: Only upper case allowed" msgstr "ISBN invalide : seulement les majuscules sont autorisées" -#: views.py:50 +#: views.py:51 msgid "Welcome to the Mediatek database" msgstr "Bienvenue sur la base de données de la Mediatek" - -#~ msgid "medium" -#~ msgstr "medium" - -#~ msgid "media" -#~ msgstr "media" diff --git a/media/management/commands/export_markdown_site.py b/media/management/commands/export_markdown_site.py index 0b3962a..93a87ce 100644 --- a/media/management/commands/export_markdown_site.py +++ b/media/management/commands/export_markdown_site.py @@ -1,5 +1,5 @@ from django.core.management import BaseCommand -from media.models import BD, CD, Manga, Revue, Roman, Vinyle, Jeu +from media.models import Comic, CD, Manga, Review, Novel, Vinyl, Game class Command(BaseCommand): @@ -18,9 +18,9 @@ class Command(BaseCommand): f.write("Ce site répertorie l'intégralité des media présents " "à la Mediatek de l'ENS Paris-Saclay.\n") - for model_class, file_name in [(BD, "bd.md"), (Manga, "mangas.md"), - (Roman, "romans.md"), - (CD, "cd.md"), (Vinyle, "vinyles.md")]: + for model_class, file_name in [(Comic, "bd.md"), (Manga, "mangas.md"), + (Novel, "romans.md"), + (CD, "cd.md"), (Vinyl, "vinyles.md")]: self.process_model_class(model_class, file_name, f, directory) # Traitement différent pour les revues @@ -28,13 +28,13 @@ class Command(BaseCommand): f.write("# Revues\n\n\n") titles = list(set(obj["title"] for obj in - Revue.objects.values("title").distinct().all())) + Review.objects.values("title").distinct().all())) titles.sort() for title in titles: f.write(f"## {title}\n\n\n") - for medium in Revue.objects.filter(title=title)\ + for medium in Review.objects.filter(title=title)\ .order_by("number").all(): f.write(f"### Numéro {medium.number}\n\n\n") if medium.double: @@ -51,13 +51,13 @@ class Command(BaseCommand): with open(directory + "/docs/jeux.md", "w") as f: f.write("# Jeux\n\n\n") - for game in Jeu.objects.order_by("name").all(): + for game in Game.objects.order_by("name").all(): f.write(f"## {game.name}\n\n\n") - f.write(f"Durée : {game.duree}\n\n") - f.write(f"Nombre de joueurs : {game.nombre_joueurs_min} " - f"- {game.nombre_joueurs_max}\n\n") - if game.proprietaire.username != "Med": - f.write(f"Propriétaire : {game.proprietaire.username}\n\n") + f.write(f"Durée : {game.duration}\n\n") + f.write(f"Nombre de joueurs : {game.players_min} " + f"- {game.players_max}\n\n") + if game.owner.username != "Med": + f.write(f"Propriétaire : {game.owner.username}\n\n") if game.comment: f.write(f"Commentaire : {game.comment}\n\n") f.write("\n\n\n") diff --git a/media/management/commands/import_cds.py b/media/management/commands/import_cds.py index 3498958..a125890 100644 --- a/media/management/commands/import_cds.py +++ b/media/management/commands/import_cds.py @@ -2,7 +2,7 @@ from argparse import FileType from sys import stdin from django.core.management import BaseCommand -from media.models import Auteur, CD +from media.models import Author, CD class Command(BaseCommand): @@ -29,7 +29,7 @@ class Command(BaseCommand): title = cd[0] side = cd[1] authors_str = cd[2].split('|') - authors = [Auteur.objects.get_or_create(name=author)[0] + authors = [Author.objects.get_or_create(name=author)[0] for author in authors_str] cd, created = CD.objects.get_or_create( title=title, diff --git a/media/management/commands/import_future_media.py b/media/management/commands/import_future_media.py index c0b4c4b..b21d8a4 100644 --- a/media/management/commands/import_future_media.py +++ b/media/management/commands/import_future_media.py @@ -3,20 +3,20 @@ from time import sleep from django.core.management import BaseCommand from media.forms import MediaAdminForm -from media.models import BD, FutureMedia, Manga, Roman +from media.models import Comic, FutureMedium, Manga, Novel class Command(BaseCommand): def handle(self, *args, **options): - for future_medium in FutureMedia.objects.all(): + for future_medium in FutureMedium.objects.all(): isbn = future_medium.isbn type_str = future_medium.type if type_str == 'bd': - cl = BD + cl = Comic elif type_str == 'manga': cl = Manga elif type_str == 'roman': - cl = Roman + cl = Novel else: self.stderr.write(self.style.WARNING( "Unknown medium type: {type}. Ignoring..." diff --git a/media/management/commands/import_isbn.py b/media/management/commands/import_isbn.py index c7bd893..1886fba 100644 --- a/media/management/commands/import_isbn.py +++ b/media/management/commands/import_isbn.py @@ -3,7 +3,7 @@ from sys import stdin from django.core.exceptions import ValidationError from django.core.management import BaseCommand -from media.models import BD, FutureMedia, Manga, Roman +from media.models import Comic, FutureMedium, Manga, Novel from media.validators import isbn_validator @@ -27,7 +27,7 @@ class Command(BaseCommand): def handle(self, *args, **options): type_str = options["media_type"] - media_classes = [BD, Manga, Roman, FutureMedia] + media_classes = [Comic, Manga, Novel, FutureMedium] file = options["input"] isbns = [] @@ -70,7 +70,7 @@ class Command(BaseCommand): if isbn_exists: continue - FutureMedia.objects.create(isbn=isbn, type=type_str) + FutureMedium.objects.create(isbn=isbn, type=type_str) self.stdout.write(self.style.SUCCESS("ISBN {isbn} imported" .format(isbn=isbn))) imported += 1 diff --git a/media/management/commands/import_marvel.py b/media/management/commands/import_marvel.py index 69d5b1c..9fdc775 100644 --- a/media/management/commands/import_marvel.py +++ b/media/management/commands/import_marvel.py @@ -2,7 +2,7 @@ from argparse import FileType from sys import stdin from django.core.management import BaseCommand -from media.models import Auteur, BD +from media.models import Author, Comic class Command(BaseCommand): @@ -28,9 +28,9 @@ class Command(BaseCommand): title = revue[0] number = revue[1] - authors = [Auteur.objects.get_or_create(name=n)[0] + authors = [Author.objects.get_or_create(name=n)[0] for n in revue[2].split('|')] - bd = BD.objects.create( + bd = Comic.objects.create( title=title, subtitle=number, side_identifier="{:.3} {:.3} {:0>2}" diff --git a/media/management/commands/import_no_isbn_roman.py b/media/management/commands/import_no_isbn_roman.py index e7adb0a..30683df 100644 --- a/media/management/commands/import_no_isbn_roman.py +++ b/media/management/commands/import_no_isbn_roman.py @@ -3,7 +3,7 @@ from sys import stdin from django.core.management import BaseCommand from media.forms import generate_side_identifier -from media.models import Roman, Auteur +from media.models import Novel, Author class Command(BaseCommand): @@ -28,10 +28,10 @@ class Command(BaseCommand): continue title = book[1] - authors = [Auteur.objects.get_or_create(name=n)[0] + authors = [Author.objects.get_or_create(name=n)[0] for n in book[0].split(';')] side_identifier = generate_side_identifier(title, authors) - roman = Roman.objects.create( + roman = Novel.objects.create( title=title, side_identifier=side_identifier, ) diff --git a/media/management/commands/import_revues.py b/media/management/commands/import_revues.py index 6df08b3..107923f 100644 --- a/media/management/commands/import_revues.py +++ b/media/management/commands/import_revues.py @@ -2,7 +2,7 @@ from argparse import FileType from sys import stdin from django.core.management import BaseCommand -from media.models import Revue +from media.models import Review class Command(BaseCommand): @@ -37,7 +37,7 @@ class Command(BaseCommand): year = revue[4] if not year: year = None - revue, created = Revue.objects.get_or_create( + revue, created = Review.objects.get_or_create( title=title, number=number.replace('*', ''), year=year, diff --git a/media/management/commands/import_vinyles.py b/media/management/commands/import_vinyles.py index 029fbff..3f710e1 100644 --- a/media/management/commands/import_vinyles.py +++ b/media/management/commands/import_vinyles.py @@ -2,7 +2,7 @@ from argparse import FileType from sys import stdin from django.core.management import BaseCommand -from media.models import Auteur, Vinyle +from media.models import Author, Vinyl class Command(BaseCommand): @@ -36,9 +36,9 @@ class Command(BaseCommand): title = vinyle[1 if rpm == 33 else 2] authors_str = vinyle[2 if rpm == 33 else 1]\ .split('|' if rpm == 33 else ';') - authors = [Auteur.objects.get_or_create(name=author)[0] + authors = [Author.objects.get_or_create(name=author)[0] for author in authors_str] - vinyle, created = Vinyle.objects.get_or_create( + vinyle, created = Vinyl.objects.get_or_create( title=title, side_identifier=side, rpm=rpm, diff --git a/media/management/commands/regenerate_side_identifiers.py b/media/management/commands/regenerate_side_identifiers.py index 35311ac..88c13d4 100644 --- a/media/management/commands/regenerate_side_identifiers.py +++ b/media/management/commands/regenerate_side_identifiers.py @@ -1,7 +1,7 @@ from django.core.management import BaseCommand from django.db import transaction from media.forms import generate_side_identifier -from media.models import BD, Manga, Roman +from media.models import Comic, Manga, Novel class Command(BaseCommand): @@ -24,11 +24,11 @@ class Command(BaseCommand): t = options["type"] medium_class = None if t == "bd": - medium_class = BD + medium_class = Comic elif t == "manga": medium_class = Manga elif t == "roman": - medium_class = Roman + medium_class = Novel interactive_mode = not options["noninteractivemode"] diff --git a/media/management/commands/split_media_types.py b/media/management/commands/split_media_types.py index fe7fd7d..1578bf5 100644 --- a/media/management/commands/split_media_types.py +++ b/media/management/commands/split_media_types.py @@ -2,7 +2,7 @@ from time import sleep from django.core.management import BaseCommand from media.forms import MediaAdminForm -from media.models import BD, Manga +from media.models import Comic, Manga class Command(BaseCommand): @@ -14,7 +14,7 @@ class Command(BaseCommand): def handle(self, *args, **options): converted = 0 - for media in BD.objects.all(): + for media in Comic.objects.all(): if media.pk < 3400: continue # We sleep 5 seconds to avoid a ban from Bedetheque diff --git a/media/migrations/0040_auto_20211023_1830.py b/media/migrations/0040_auto_20211023_1830.py new file mode 100644 index 0000000..3d9f57d --- /dev/null +++ b/media/migrations/0040_auto_20211023_1830.py @@ -0,0 +1,61 @@ +# Generated by Django 2.2.17 on 2021-10-23 16:30 + +from django.conf import settings +from django.db import migrations, models +import media.fields +import media.validators + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('media', '0039_mark_media_present'), + ] + + operations = [ + migrations.RenameModel( + old_name='Auteur', + new_name='Author', + ), + migrations.RenameModel( + old_name='Roman', + new_name='Comic', + ), + migrations.RenameModel( + old_name='Jeu', + new_name='Game', + ), + migrations.RenameModel( + old_name='BD', + new_name='Novel', + ), + migrations.RenameModel( + old_name='Revue', + new_name='Review', + ), + migrations.RenameModel( + old_name='Vinyle', + new_name='Vinyl', + ), + migrations.RenameModel( + old_name='FutureMedia', + new_name='FutureMedium', + ), + migrations.AlterModelOptions( + name='comic', + options={'ordering': ['title', 'subtitle'], 'verbose_name': 'comic', 'verbose_name_plural': 'comics'}, + ), + migrations.AlterModelOptions( + name='novel', + options={'ordering': ['title', 'subtitle'], 'verbose_name': 'novel', 'verbose_name_plural': 'novels'}, + ), + migrations.AlterModelOptions( + name='review', + options={'ordering': ['title', 'number'], 'verbose_name': 'review', 'verbose_name_plural': 'reviews'}, + ), + migrations.AlterModelOptions( + name='vinyl', + options={'ordering': ['title'], 'verbose_name': 'vinyl', 'verbose_name_plural': 'vinyls'}, + ), + ] diff --git a/media/migrations/0041_auto_20211023_1838.py b/media/migrations/0041_auto_20211023_1838.py new file mode 100644 index 0000000..37b05f2 --- /dev/null +++ b/media/migrations/0041_auto_20211023_1838.py @@ -0,0 +1,44 @@ +# Generated by Django 2.2.17 on 2021-10-23 16:38 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('media', '0040_auto_20211023_1830'), + ] + + operations = [ + migrations.RenameField( + model_name='game', + old_name='duree', + new_name='duration', + ), + migrations.RenameField( + model_name='game', + old_name='proprietaire', + new_name='owner', + ), + migrations.RenameField( + model_name='game', + old_name='nombre_joueurs_max', + new_name='players_max', + ), + migrations.RenameField( + model_name='game', + old_name='nombre_joueurs_min', + new_name='players_min', + ), + migrations.AlterField( + model_name='emprunt', + name='media', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='media.Comic'), + ), + migrations.AlterField( + model_name='futuremedium', + name='type', + field=models.CharField(choices=[('bd', 'Comic'), ('manga', 'Manga'), ('roman', 'Roman')], max_length=8, verbose_name='type'), + ), + ] diff --git a/media/models.py b/media/models.py index 5efc825..a797cff 100644 --- a/media/models.py +++ b/media/models.py @@ -9,7 +9,7 @@ from django.utils.translation import gettext_lazy as _ from .fields import ISBNField -class Auteur(models.Model): +class Author(models.Model): name = models.CharField( max_length=255, unique=True, @@ -30,7 +30,7 @@ class Auteur(models.Model): ordering = ['name'] -class BD(models.Model): +class Comic(models.Model): isbn = ISBNField( _('ISBN'), help_text=_('You may be able to scan it from a bar code.'), @@ -61,7 +61,7 @@ class BD(models.Model): ) authors = models.ManyToManyField( - 'Auteur', + 'Author', verbose_name=_('authors'), ) @@ -90,8 +90,8 @@ class BD(models.Model): return self.title class Meta: - verbose_name = _("BD") - verbose_name_plural = _("BDs") + verbose_name = _("comic") + verbose_name_plural = _("comics") ordering = ['title', 'subtitle'] @@ -126,7 +126,7 @@ class Manga(models.Model): ) authors = models.ManyToManyField( - 'Auteur', + 'Author', verbose_name=_('authors'), ) @@ -157,7 +157,7 @@ class Manga(models.Model): ordering = ['title'] -class Roman(models.Model): +class Novel(models.Model): isbn = ISBNField( _('ISBN'), help_text=_('You may be able to scan it from a bar code.'), @@ -188,7 +188,7 @@ class Roman(models.Model): ) authors = models.ManyToManyField( - 'Auteur', + 'Author', verbose_name=_('authors'), ) @@ -214,12 +214,12 @@ class Roman(models.Model): return self.title class Meta: - verbose_name = _("roman") - verbose_name_plural = _("romans") + verbose_name = _("novel") + verbose_name_plural = _("novels") ordering = ['title', 'subtitle'] -class Vinyle(models.Model): +class Vinyl(models.Model): title = models.CharField( verbose_name=_('title'), max_length=255, @@ -239,7 +239,7 @@ class Vinyle(models.Model): ) authors = models.ManyToManyField( - 'Auteur', + 'Author', verbose_name=_('authors'), ) @@ -253,8 +253,8 @@ class Vinyle(models.Model): return self.title class Meta: - verbose_name = _("vinyle") - verbose_name_plural = _("vinyles") + verbose_name = _("vinyl") + verbose_name_plural = _("vinyls") ordering = ['title'] @@ -270,7 +270,7 @@ class CD(models.Model): ) authors = models.ManyToManyField( - 'Auteur', + 'Author', verbose_name=_('authors'), ) @@ -289,7 +289,7 @@ class CD(models.Model): ordering = ['title'] -class Revue(models.Model): +class Review(models.Model): title = models.CharField( verbose_name=_('title'), max_length=255, @@ -335,12 +335,12 @@ class Revue(models.Model): return self.title + " n°" + str(self.number) class Meta: - verbose_name = _("revue") - verbose_name_plural = _("revues") + verbose_name = _("review") + verbose_name_plural = _("reviews") ordering = ['title', 'number'] -class FutureMedia(models.Model): +class FutureMedium(models.Model): isbn = ISBNField( _('ISBN'), help_text=_('You may be able to scan it from a bar code.'), @@ -352,7 +352,7 @@ class FutureMedia(models.Model): type = models.CharField( _('type'), choices=[ - ('bd', _('BD')), + ('bd', _('Comic')), ('manga', _('Manga')), ('roman', _('Roman')), ], @@ -375,7 +375,7 @@ class FutureMedia(models.Model): class Emprunt(models.Model): media = models.ForeignKey( - 'BD', + 'Comic', on_delete=models.PROTECT, ) user = models.ForeignKey( @@ -417,8 +417,8 @@ class Emprunt(models.Model): ordering = ['-date_emprunt'] -class Jeu(models.Model): - DUREE = ( +class Game(models.Model): + DURATIONS = ( ('-1h', '-1h'), ('1-2h', '1-2h'), ('2-3h', '2-3h'), @@ -430,21 +430,21 @@ class Jeu(models.Model): max_length=255, verbose_name=_("name"), ) - proprietaire = models.ForeignKey( + owner = models.ForeignKey( 'users.User', on_delete=models.PROTECT, verbose_name=_("owner"), ) - duree = models.CharField( - choices=DUREE, + duration = models.CharField( + choices=DURATIONS, max_length=255, verbose_name=_("duration"), ) - nombre_joueurs_min = models.IntegerField( + players_min = models.IntegerField( validators=[MinValueValidator(1)], verbose_name=_("minimum number of players"), ) - nombre_joueurs_max = models.IntegerField( + players_max = models.IntegerField( validators=[MinValueValidator(1)], verbose_name=_('maximum number of players'), ) diff --git a/media/scraper.py b/media/scraper.py index 35139a2..5b43c9a 100644 --- a/media/scraper.py +++ b/media/scraper.py @@ -4,7 +4,7 @@ import re import requests -from media.models import Auteur +from media.models import Author class BedetequeScraper: @@ -44,7 +44,7 @@ class BedetequeScraper: regex = r'href=\"(https://www\.bedetheque\.com/BD.*.html)\"' return re.findall(regex, content) - def scrap_bd_info(self, bd_url: str) -> dict: + def scrap_comic_info(self, bd_url: str) -> dict: """ Load BD web page and scrap data :param bd_url: URL where to find BD data @@ -99,12 +99,12 @@ class BedetequeScraper: if 'author' not in data: data['authors'] = list() if author: - author_obj = Auteur.objects.get_or_create( + author_obj = Author.objects.get_or_create( name=author.group(1))[0] data['authors'].append(author_obj) illustrator = re.search(regex_illustrator, content) if illustrator: - author_obj = Auteur.objects.get_or_create( + author_obj = Author.objects.get_or_create( name=illustrator.group(1))[0] data['authors'].append(author_obj) diff --git a/media/serializers.py b/media/serializers.py index a02b055..97c0ff5 100644 --- a/media/serializers.py +++ b/media/serializers.py @@ -1,18 +1,18 @@ from rest_framework import serializers -from .models import Auteur, BD, CD, FutureMedia, Manga, Emprunt, Jeu, Revue,\ - Roman, Vinyle +from .models import Author, CD, Comic, FutureMedium, Manga, Emprunt, Game, \ + Novel, Review, Vinyl -class AuteurSerializer(serializers.ModelSerializer): +class AuthorSerializer(serializers.ModelSerializer): class Meta: - model = Auteur + model = Author fields = ['url', 'name'] -class BDSerializer(serializers.ModelSerializer): +class ComicSerializer(serializers.ModelSerializer): class Meta: - model = BD + model = Comic fields = '__all__' @@ -28,27 +28,27 @@ class CDSerializer(serializers.ModelSerializer): fields = '__all__' -class VinyleSerializer(serializers.ModelSerializer): +class VinylSerializer(serializers.ModelSerializer): class Meta: - model = Vinyle + model = Vinyl fields = '__all__' -class RomanSerializer(serializers.ModelSerializer): +class NovelSerializer(serializers.ModelSerializer): class Meta: - model = Roman + model = Novel fields = '__all__' -class RevueSerializer(serializers.ModelSerializer): +class ReviewSerializer(serializers.ModelSerializer): class Meta: - model = Revue + model = Review fields = '__all__' -class FutureMediaSerializer(serializers.ModelSerializer): +class FutureMediumSerializer(serializers.ModelSerializer): class Meta: - model = FutureMedia + model = FutureMedium fields = '__all__' @@ -59,8 +59,8 @@ class EmpruntSerializer(serializers.HyperlinkedModelSerializer): 'permanencier_emprunt', 'permanencier_rendu'] -class JeuSerializer(serializers.HyperlinkedModelSerializer): +class GameSerializer(serializers.HyperlinkedModelSerializer): class Meta: - model = Jeu + model = Game fields = ['url', 'name', 'proprietaire', 'duree', 'nombre_joueurs_min', 'nombre_joueurs_max', 'comment'] diff --git a/media/tests/test_templates.py b/media/tests/test_templates.py index bdea46d..d6bf8b9 100644 --- a/media/tests/test_templates.py +++ b/media/tests/test_templates.py @@ -3,7 +3,7 @@ from django.test import TestCase from django.urls import reverse -from media.models import Auteur, BD +from media.models import Author, Comic from users.models import User """ @@ -21,44 +21,44 @@ class TemplateTests(TestCase): self.client.force_login(self.user) # Create an author - self.dummy_author = Auteur.objects.create(name="Test author") + self.dummy_author = Author.objects.create(name="Test author") # Create media - self.dummy_bd1 = BD.objects.create( + self.dummy_bd1 = Comic.objects.create( title="Test media", side_identifier="T M", ) self.dummy_bd1.authors.add(self.dummy_author) - self.dummy_bd2 = BD.objects.create( + self.dummy_bd2 = Comic.objects.create( title="Test media bis", side_identifier="T M 2", external_url="https://example.com/", ) self.dummy_bd2.authors.add(self.dummy_author) - def test_bd_bd_changelist(self): - response = self.client.get(reverse('admin:media_bd_changelist')) + def test_comic_bd_changelist(self): + response = self.client.get(reverse('admin:media_comic_changelist')) self.assertEqual(response.status_code, 200) - def test_bd_bd_add(self): - response = self.client.get(reverse('admin:media_bd_add')) + def test_comic_bd_add(self): + response = self.client.get(reverse('admin:media_comic_add')) self.assertEqual(response.status_code, 200) - def test_bd_isbn_download(self): + def test_comic_isbn_download(self): data = { '_isbn': True, 'isbn': "0316358525", } response = self.client.post(reverse( - 'admin:media_bd_change', + 'admin:media_comic_change', args=[self.dummy_bd1.id], ), data=data) self.assertEqual(response.status_code, 302) - def test_bd_emprunt_changelist(self): + def test_comic_emprunt_changelist(self): response = self.client.get(reverse('admin:media_emprunt_changelist')) self.assertEqual(response.status_code, 200) - def test_bd_emprunt_add(self): + def test_comic_emprunt_add(self): response = self.client.get(reverse('admin:media_emprunt_add')) self.assertEqual(response.status_code, 200) diff --git a/media/urls.py b/media/urls.py index ff61900..d11bbce 100644 --- a/media/urls.py +++ b/media/urls.py @@ -13,8 +13,8 @@ urlpatterns = [ name='retour-emprunt'), path('find/', views.FindMediumView.as_view(), name="find"), path('mark-as-present/bd//', - views.MarkBDAsPresent.as_view(), - name="mark_bd_as_present"), + views.MarkComicAsPresent.as_view(), + name="mark_comic_as_present"), path('mark-as-present/manga//', views.MarkMangaAsPresent.as_view(), name="mark_manga_as_present"), @@ -22,7 +22,7 @@ urlpatterns = [ views.MarkCDAsPresent.as_view(), name="mark_cd_as_present"), path('mark-as-present/vinyle//', - views.MarkVinyleAsPresent.as_view(), + views.MarkVinylAsPresent.as_view(), name="mark_vinyle_as_present"), path('mark-as-present/roman//', views.MarkRomanAsPresent.as_view(), diff --git a/media/views.py b/media/views.py index eb45dc4..08370c3 100644 --- a/media/views.py +++ b/media/views.py @@ -8,7 +8,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponse from django_filters.rest_framework import DjangoFilterBackend from django.db import transaction -from django.shortcuts import redirect, render +from django.shortcuts import redirect from django.utils import timezone from django.utils.translation import gettext_lazy as _ from django.views.generic import TemplateView, DetailView @@ -16,11 +16,11 @@ from rest_framework import viewsets from rest_framework.filters import SearchFilter from reversion import revisions as reversion -from .models import Auteur, BD, CD, Emprunt, FutureMedia, Jeu, Manga, Revue,\ - Roman, Vinyle -from .serializers import AuteurSerializer, BDSerializer, CDSerializer,\ - EmpruntSerializer, FutureMediaSerializer, JeuSerializer, MangaSerializer,\ - RevueSerializer, RomanSerializer, VinyleSerializer +from .models import Author, CD, Comic, Emprunt, FutureMedium, Game, Manga,\ + Novel, Review, Vinyl +from .serializers import AuthorSerializer, ComicSerializer, CDSerializer,\ + EmpruntSerializer, FutureMediumSerializer, GameSerializer, \ + MangaSerializer, NovelSerializer, ReviewSerializer, VinylSerializer @login_required @@ -40,16 +40,17 @@ def retour_emprunt(request, empruntid): return redirect("admin:media_emprunt_changelist") -def index(request): +class IndexView(TemplateView): """ Home page which redirect to admin when logged in """ - if request.user.is_authenticated: - return redirect('admin:index') - else: - return render(request, 'admin/index.html', { - 'title': _('Welcome to the Mediatek database'), - }) + extra_context = {'title': _('Welcome to the Mediatek database')} + template_name = 'admin/index.html' + + def dispatch(self, request, *args, **kwargs): + if request.user.is_authenticated: + return redirect('admin:index') + return super().dispatch(request, *args, **kwargs) class FindMediumView(LoginRequiredMixin, TemplateView): @@ -64,8 +65,8 @@ class MarkMediumAsPresent(LoginRequiredMixin, DetailView): return HttpResponse("", content_type=204) -class MarkBDAsPresent(MarkMediumAsPresent): - model = BD +class MarkComicAsPresent(MarkMediumAsPresent): + model = Comic class MarkMangaAsPresent(MarkMediumAsPresent): @@ -76,36 +77,36 @@ class MarkCDAsPresent(MarkMediumAsPresent): model = CD -class MarkVinyleAsPresent(MarkMediumAsPresent): - model = Vinyle +class MarkVinylAsPresent(MarkMediumAsPresent): + model = Vinyl class MarkRomanAsPresent(MarkMediumAsPresent): - model = Roman + model = Novel class MarkRevueAsPresent(MarkMediumAsPresent): - model = Revue + model = Review class MarkFutureAsPresent(MarkMediumAsPresent): - model = FutureMedia + model = FutureMedium -class AuteurViewSet(viewsets.ModelViewSet): +class AuthorViewSet(viewsets.ModelViewSet): """ API endpoint that allows authors to be viewed or edited. """ - queryset = Auteur.objects.all() - serializer_class = AuteurSerializer + queryset = Author.objects.all() + serializer_class = AuthorSerializer -class BDViewSet(viewsets.ModelViewSet): +class ComicViewSet(viewsets.ModelViewSet): """ API endpoint that allows media to be viewed or edited. """ - queryset = BD.objects.all() - serializer_class = BDSerializer + queryset = Comic.objects.all() + serializer_class = ComicSerializer filter_backends = [DjangoFilterBackend, SearchFilter] filterset_fields = ["isbn", "side_identifier"] search_fields = ["=isbn", "title", "subtitle", "side_identifier", @@ -135,46 +136,46 @@ class CDViewSet(viewsets.ModelViewSet): search_fields = ["title", "side_identifier", "authors__name"] -class VinyleViewSet(viewsets.ModelViewSet): +class VinylViewSet(viewsets.ModelViewSet): """ API endpoint that allows media to be viewed or edited. """ - queryset = Vinyle.objects.all() - serializer_class = VinyleSerializer + queryset = Vinyl.objects.all() + serializer_class = VinylSerializer filter_backends = [DjangoFilterBackend, SearchFilter] filterset_fields = ["side_identifier", "rpm"] search_fields = ["title", "side_identifier", "authors__name"] -class RomanViewSet(viewsets.ModelViewSet): +class NovelViewSet(viewsets.ModelViewSet): """ API endpoint that allows media to be viewed or edited. """ - queryset = Roman.objects.all() - serializer_class = RomanSerializer + queryset = Novel.objects.all() + serializer_class = NovelSerializer filter_backends = [DjangoFilterBackend, SearchFilter] filterset_fields = ["isbn", "side_identifier", "number_of_pages"] search_fields = ["=isbn", "title", "subtitle", "side_identifier", "authors__name"] -class RevueViewSet(viewsets.ModelViewSet): +class ReviewViewSet(viewsets.ModelViewSet): """ API endpoint that allows media to be viewed or edited. """ - queryset = Revue.objects.all() - serializer_class = RevueSerializer + queryset = Review.objects.all() + serializer_class = ReviewSerializer filter_backends = [DjangoFilterBackend, SearchFilter] filterset_fields = ["number", "year", "month", "day", "double"] search_fields = ["title"] -class FutureMediaViewSet(viewsets.ModelViewSet): +class FutureMediumViewSet(viewsets.ModelViewSet): """ API endpoint that allows media to be viewed or edited. """ - queryset = FutureMedia.objects.all() - serializer_class = FutureMediaSerializer + queryset = FutureMedium.objects.all() + serializer_class = FutureMediumSerializer filter_backends = [DjangoFilterBackend, SearchFilter] filterset_fields = ["isbn"] search_fields = ["=isbn"] @@ -188,9 +189,9 @@ class EmpruntViewSet(viewsets.ModelViewSet): serializer_class = EmpruntSerializer -class JeuViewSet(viewsets.ModelViewSet): +class GameViewSet(viewsets.ModelViewSet): """ API endpoint that allows games to be viewed or edited. """ - queryset = Jeu.objects.all() - serializer_class = JeuSerializer + queryset = Game.objects.all() + serializer_class = GameSerializer diff --git a/requirements.txt b/requirements.txt index fb744fb..3c3b10a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,7 @@ -Django~=2.2.10 -docutils~=0.14 -Pillow>=8.0.1 -pytz~=2020.4 -six~=1.15 -sqlparse~=0.3 +docutils~=0.16 # for Django-admin docs +Django~=2.2 django-filter~=2.4 django-reversion~=3.0 -python-stdnum~=1.14 -djangorestframework~=3.12.1 -pyyaml~=5.3.1 -coreapi~=2.3.3 -django_extensions~=3.1 \ No newline at end of file +djangorestframework~=3.12 +django_extensions~=3.0 +requests~=2.25 # for scrapping diff --git a/users/admin.py b/users/admin.py index e8d3a7c..6849ef6 100644 --- a/users/admin.py +++ b/users/admin.py @@ -13,12 +13,7 @@ from reversion.admin import VersionAdmin from med.admin import admin_site from .forms import UserCreationAdminForm -from .models import Adhesion, User - - -class AdhesionAdmin(VersionAdmin): - list_display = ('starting_in', 'ending_in') - autocomplete_fields = ('members',) +from .models import User class IsMemberFilter(admin.SimpleListFilter): @@ -31,12 +26,7 @@ class IsMemberFilter(admin.SimpleListFilter): ) def queryset(self, request, queryset): - value = self.value() - if value == 'Yes': - # Get current membership year and list all members - last_adh_year = Adhesion.objects.all().order_by('starting_in') \ - .reverse().first() - return last_adh_year.members + # FIXME Replace with imported Note Kfet memberships return queryset @@ -88,9 +78,8 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): """ Get current membership year and check if user is there """ - last_adh_year = Adhesion.objects.all().order_by('starting_in') \ - .reverse().first() - is_member = last_adh_year and obj in last_adh_year.members.all() + # FIXME Use NK20 + is_member = True if is_member: return format_html( 'True' @@ -108,4 +97,3 @@ class UserAdmin(VersionAdmin, BaseUserAdmin): admin_site.register(User, UserAdmin) -admin_site.register(Adhesion, AdhesionAdmin) diff --git a/users/migrations/0042_delete_adhesion.py b/users/migrations/0042_delete_adhesion.py new file mode 100644 index 0000000..a6a12c8 --- /dev/null +++ b/users/migrations/0042_delete_adhesion.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.17 on 2021-10-23 12:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0041_auto_20200923_2030'), + ] + + operations = [ + migrations.DeleteModel( + name='Adhesion', + ), + ] diff --git a/users/models.py b/users/models.py index acea90d..3b5cf51 100644 --- a/users/models.py +++ b/users/models.py @@ -42,31 +42,5 @@ class User(AbstractUser): @property def is_member(self): - last_year = Adhesion.objects.all().order_by( - 'starting_in').reverse().first() - return last_year and self in last_year.members.all() - - -class Adhesion(models.Model): - starting_in = models.IntegerField( - verbose_name=_('starting in'), - help_text=_('Year in which the membership year starts.'), - unique=True, - ) - ending_in = models.IntegerField( - verbose_name=_('ending in'), - help_text=_('Year in which the membership year ends.'), - unique=True, - ) - members = models.ManyToManyField( - 'User', - verbose_name=_('members'), - blank=True, - ) - - class Meta: - verbose_name = _('membership year') - verbose_name_plural = _('membership years') - - def __str__(self): - return f"{self.starting_in} - {self.ending_in}" + # FIXME Use NK20 + return True diff --git a/users/urls.py b/users/urls.py index 00fb9e5..457e218 100644 --- a/users/urls.py +++ b/users/urls.py @@ -9,5 +9,4 @@ from . import views app_name = 'users' urlpatterns = [ url(r'^edit_info/$', views.edit_info, name='edit-info'), - url(r'^adherer/(?P[0-9]+)$', views.adherer, name='adherer'), ] diff --git a/users/views.py b/users/views.py index 1673ce0..0f5b17a 100644 --- a/users/views.py +++ b/users/views.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later from django.contrib import messages -from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.db import transaction from django.shortcuts import redirect, render @@ -12,7 +12,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import viewsets from reversion import revisions as reversion from users.forms import BaseInfoForm -from users.models import Adhesion, User +from users.models import User from .serializers import GroupSerializer, UserSerializer @@ -44,27 +44,6 @@ def edit_info(request): }, 'users/user.html', request) -@login_required -@permission_required('users.add_adhesion') -def adherer(request, userid): - try: - users = User.objects.get(pk=userid) - except User.DoesNotExist: - messages.error(request, "Utilisateur inexistant") - return redirect("admin:users_user_changelist") - adh_year = Adhesion.objects.all().order_by('starting_in').reverse().first() - if not adh_year: - messages.error(request, "Année d'adhésion non définie") - return redirect("admin:users_user_changelist") - with transaction.atomic(), reversion.create_revision(): - reversion.set_user(request.user) - adh_year.members.add(users) - adh_year.save() - reversion.set_comment("Adhesion de %s" % users) - messages.success(request, "Adhesion effectuee") - return redirect("admin:users_user_changelist") - - class UserViewSet(viewsets.ModelViewSet): """ API endpoint that allows users to be viewed or edited.