diff --git a/CHANGELOG b/CHANGELOG index e447d42dd..fcf398559 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,65 @@ This changelog is viewable on the web at https://docs.funkwhale.audio/changelog. .. towncrier +1.2.8 (2022-09-12) +------------------ + +Upgrade instructions are available at +https://docs.funkwhale.audio/admin/upgrading.html + +Features: + +- Add Sentry SDK to collect errors at the backend + + +Bugfixes: + +- Fix exponentially growing database when using in-place-imports on a regular base #1676 +- Fix navigating to registration request not showing anything (#1836) +- Fix player cover image overlapping queue list +- Fixed metadata handling for Various Artists albums (#1201) +- Fixed search behaviour in radio builder's filters (#733) +- Fixed unpredictable subsonic search3 results (#1782) + +Committers: + +- Ciarán Ainsworth +- Georg Krause +- Marcos Peña +- Mathias Koehler +- wvffle + +Contributors to our Issues: + +- AMoonRabbit +- Agate +- Ciarán Ainsworth +- Georg Krause +- JuniorJPDJ +- Kasper Seweryn +- Kelvin Hammond +- Marcos Peña +- Meliurwen +- Micha Gläß-Stöcker +- Miv2nir +- Sam Birch +- Tolriq +- Tony Wasserka +- f1reflyyyylmao +- heyarne +- petitminion +- troll + +Contributors to our Merge Requests: + +- Ciarán Ainsworth +- Georg Krause +- JuniorJPDJ +- Kasper Seweryn +- Marcos Peña +- interru + + 1.2.7 (2022-07-14) ------------------ diff --git a/api/funkwhale_api/__init__.py b/api/funkwhale_api/__init__.py index 10d6f210f..7c7f3009f 100644 --- a/api/funkwhale_api/__init__.py +++ b/api/funkwhale_api/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = "1.2.7" +__version__ = "1.2.8" __version_info__ = tuple( [ int(num) if num.isdigit() else num diff --git a/api/funkwhale_api/common/utils.py b/api/funkwhale_api/common/utils.py index 061169ab8..f252aeac9 100644 --- a/api/funkwhale_api/common/utils.py +++ b/api/funkwhale_api/common/utils.py @@ -187,7 +187,7 @@ def order_for_search(qs, field): When searching, it's often more useful to have short results first, this function will order the given qs based on the length of the given field """ - return qs.annotate(__size=models.functions.Length(field)).order_by("__size") + return qs.annotate(__size=models.functions.Length(field)).order_by("__size", "pk") def recursive_getattr(obj, key, permissive=False): diff --git a/api/funkwhale_api/music/management/commands/prune_skipped_uploads.py b/api/funkwhale_api/music/management/commands/prune_skipped_uploads.py new file mode 100644 index 000000000..8dcf75380 --- /dev/null +++ b/api/funkwhale_api/music/management/commands/prune_skipped_uploads.py @@ -0,0 +1,35 @@ +from django.core.management.base import BaseCommand + +from django.db import transaction + +from funkwhale_api.music import models + + +class Command(BaseCommand): + help = """ + This command makes it easy to prune all skipped Uploads from the database. + Due to a bug they might caused the database to grow exponentially, + especially when using in-place-imports on a regular basis. This command + helps to clean up the database again. + """ + + def add_arguments(self, parser): + parser.add_argument( + "--force", + default=False, + help="Disable dry run mode and apply pruning for real on the database", + ) + + @transaction.atomic + def handle(self, *args, **options): + skipped = models.Uploads.objects.filter(import_status="skipped") + count = len(skipped) + if options["force"]: + skipped.delete() + print(f"Deleted {count} entries from the database.") + return + + print( + f"Would delete {count} entries from the database.\ + Run with --force to actually apply changes to the database" + ) diff --git a/api/funkwhale_api/music/metadata.py b/api/funkwhale_api/music/metadata.py index 9d19e1557..89f02a6ac 100644 --- a/api/funkwhale_api/music/metadata.py +++ b/api/funkwhale_api/music/metadata.py @@ -480,8 +480,8 @@ class ArtistField(serializers.Field): def get_value(self, data): if self.for_album: keys = [ - ("artists", "artists"), - ("names", "album_artist"), + ("artists", "album_artist"), + ("names", "artists"), ("mbids", "musicbrainz_albumartistid"), ] else: @@ -525,7 +525,14 @@ class ArtistField(serializers.Field): if separator in data["artists"]: names = [n.strip() for n in data["artists"].split(separator)] break - if not names: + # corner case: 'album artist' field with only one artist but multiple names in 'artits' field + if ( + not names + and data.get("names", None) + and any(separator in data["names"] for separator in separators) + ): + names = [n.strip() for n in data["names"].split(separators[0])] + elif not names: names = [data["artists"]] elif used_separator and mbids: names = [n.strip() for n in data["names"].split(used_separator)] diff --git a/api/funkwhale_api/music/tasks.py b/api/funkwhale_api/music/tasks.py index 51e17103a..42088d9e6 100644 --- a/api/funkwhale_api/music/tasks.py +++ b/api/funkwhale_api/music/tasks.py @@ -284,7 +284,9 @@ def process_upload(upload, update_denormalization=True): upload.import_status = "skipped" upload.import_details = { "code": "already_imported_in_owned_libraries", - "duplicates": list(owned_duplicates), + # In order to avoid exponential growth of the database, we only + # reference the first known upload which gets duplicated + "duplicates": owned_duplicates[0], } upload.import_date = timezone.now() upload.save( @@ -436,6 +438,7 @@ def get_owned_duplicates(upload, track): ) .exclude(pk=upload.pk) .values_list("uuid", flat=True) + .order_by("creation_date") ) diff --git a/api/funkwhale_api/radios/filters.py b/api/funkwhale_api/radios/filters.py index f3abe22e0..126701311 100644 --- a/api/funkwhale_api/radios/filters.py +++ b/api/funkwhale_api/radios/filters.py @@ -2,7 +2,7 @@ import collections import persisting_theory from django.core.exceptions import ValidationError -from django.db.models import Q +from django.db.models import Q, functions from django.urls import reverse_lazy from funkwhale_api.music import models @@ -132,9 +132,13 @@ class ArtistFilter(RadioFilter): "name": "ids", "type": "list", "subtype": "number", - "autocomplete": reverse_lazy("api:v1:artists-list"), + "autocomplete": reverse_lazy("api:v1:search"), "autocomplete_qs": "q={query}", - "autocomplete_fields": {"name": "name", "value": "id"}, + "autocomplete_fields": { + "remoteValues": "artists", + "name": "name", + "value": "id", + }, "label": "Artist", "placeholder": "Select artists", } @@ -145,7 +149,8 @@ class ArtistFilter(RadioFilter): filter_config["ids"] = sorted(filter_config["ids"]) names = ( models.Artist.objects.filter(pk__in=filter_config["ids"]) - .order_by("id") + .annotate(__size=functions.Length("name")) + .order_by("__size", "id") .values_list("name", flat=True) ) filter_config["names"] = list(names) @@ -176,13 +181,13 @@ class TagFilter(RadioFilter): "name": "names", "type": "list", "subtype": "string", - "autocomplete": reverse_lazy("api:v1:tags-list"), + "autocomplete": reverse_lazy("api:v1:search"), "autocomplete_fields": { - "remoteValues": "results", + "remoteValues": "tags", "name": "name", "value": "name", }, - "autocomplete_qs": "q={query}&ordering=length", + "autocomplete_qs": "q={query}", "label": "Tags", "placeholder": "Select tags", } @@ -196,3 +201,28 @@ class TagFilter(RadioFilter): | Q(artist__tagged_items__tag__name__in=names) | Q(album__tagged_items__tag__name__in=names) ) + + def clean_config(self, filter_config): + filter_config = super().clean_config(filter_config) + filter_config["names"] = sorted(filter_config["names"]) + names = ( + models.tags_models.Tag.objects.filter(name__in=filter_config["names"]) + .annotate(__size=functions.Length("name")) + .order_by("__size", "pk") + .values_list("name", flat=True) + ) + filter_config["names"] = list(names) + return filter_config + + def validate(self, config): + super().validate(config) + try: + names = models.tags_models.Tag.objects.filter( + name__in=config["names"] + ).values_list("name", flat=True) + diff = set(config["names"]) - set(names) + assert len(diff) == 0 + except KeyError: + raise ValidationError("You must provide a name") + except AssertionError: + raise ValidationError('No tag matching names "{}"'.format(diff)) diff --git a/api/funkwhale_api/subsonic/views.py b/api/funkwhale_api/subsonic/views.py index 8ea2fba5a..dd5ca3111 100644 --- a/api/funkwhale_api/subsonic/views.py +++ b/api/funkwhale_api/subsonic/views.py @@ -566,7 +566,7 @@ class SubsonicViewSet(viewsets.GenericViewSet): except (TypeError, KeyError, ValueError): size = 20 - size = min(size, 100) + size = min(size, 500) queryset = c["queryset"] if query: queryset = c["queryset"].filter( diff --git a/api/tests/music/test_tasks.py b/api/tests/music/test_tasks.py index 13522e338..15cecae33 100644 --- a/api/tests/music/test_tasks.py +++ b/api/tests/music/test_tasks.py @@ -452,7 +452,7 @@ def test_upload_import_skip_existing_track_in_own_library(factories, temp_signal assert duplicate.import_status == "skipped" assert duplicate.import_details == { "code": "already_imported_in_owned_libraries", - "duplicates": [str(existing.uuid)], + "duplicates": str(existing.uuid), } handler.assert_called_once_with( diff --git a/api/tests/radios/test_filters.py b/api/tests/radios/test_filters.py new file mode 100644 index 000000000..d63ef7940 --- /dev/null +++ b/api/tests/radios/test_filters.py @@ -0,0 +1,37 @@ +from funkwhale_api.radios import filters + + +def test_clean_config_artist_name_sorting(factories): + + artist3 = factories["music.Artist"](name="The Green Eyes") + artist2 = factories["music.Artist"](name="The Green Eyed Machine") + artist1 = factories["music.Artist"](name="The Green Seed") + factories["music.Artist"]() + filter_config = {"type": "artist", "ids": [artist3.pk, artist1.pk, artist2.pk]} + artist_filter = filters.ArtistFilter() + config = artist_filter.clean_config(filter_config) + # list of names whose artists have been sorted by name then by id + sorted_names = [ + a.name + for a in list( + sorted([artist2, artist1, artist3], key=lambda x: (len(x.name), x.id)) + ) + ] + assert config["names"] == sorted_names + + +def test_clean_config_tag_name_sorting(factories): + + tag3 = factories["tags.Tag"](name="Rock") + tag2 = factories["tags.Tag"](name="Classic") + tag1 = factories["tags.Tag"](name="Punk") + factories["tags.Tag"]() + filter_config = {"type": "tag", "names": [tag3.name, tag1.name, tag2.name]} + tag_filter = filters.TagFilter() + config = tag_filter.clean_config(filter_config) + # list of names whose tags have been sorted by name then by id + sorted_names = [ + a.name + for a in list(sorted([tag2, tag1, tag3], key=lambda x: (len(x.name), x.id))) + ] + assert config["names"] == sorted_names diff --git a/docs/poetry.lock b/docs/poetry.lock index 5e159ad82..b569d4562 100644 --- a/docs/poetry.lock +++ b/docs/poetry.lock @@ -15,10 +15,10 @@ optional = false python-versions = ">=3.7" [package.extras] -tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] [[package]] -name = "babel" +name = "Babel" version = "2.10.3" description = "Internationalization utilities" category = "main" @@ -30,7 +30,7 @@ pytz = ">=2015.7" [[package]] name = "certifi" -version = "2022.6.15" +version = "2022.6.15.1" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -38,7 +38,7 @@ python-versions = ">=3.6" [[package]] name = "charset-normalizer" -version = "2.1.0" +version = "2.1.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -67,8 +67,8 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] -name = "django" -version = "4.0.5" +name = "Django" +version = "4.0.6" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." category = "main" optional = false @@ -121,7 +121,7 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -name = "jinja2" +name = "Jinja2" version = "3.1.2" description = "A very fast and expressive template engine." category = "main" @@ -156,7 +156,7 @@ code_style = ["pre-commit (==2.6)"] benchmarking = ["pytest-benchmark (>=3.2,<4.0)", "pytest", "psutil"] [[package]] -name = "markupsafe" +name = "MarkupSafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" @@ -181,7 +181,7 @@ code_style = ["pre-commit (==2.6)"] [[package]] name = "mdurl" -version = "0.1.1" +version = "0.1.2" description = "Markdown URL utilities" category = "main" optional = false @@ -222,13 +222,16 @@ python-versions = ">=3.6" pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] -name = "pygments" -version = "2.12.0" +name = "Pygments" +version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false python-versions = ">=3.6" +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pyparsing" version = "3.0.9" @@ -238,18 +241,18 @@ optional = false python-versions = ">=3.6.8" [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytz" -version = "2022.1" +version = "2022.2.1" description = "World timezone definitions, modern and historical" category = "main" optional = false python-versions = "*" [[package]] -name = "pyyaml" +name = "PyYAML" version = "6.0" description = "YAML parser and emitter for Python" category = "main" @@ -444,7 +447,7 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -465,7 +468,7 @@ python-versions = ">=3.7" [[package]] name = "tzdata" -version = "2022.1" +version = "2022.2" description = "Provider of IANA time zone data" category = "main" optional = false @@ -477,11 +480,11 @@ version = "1.26.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.5.*, <4" [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] @@ -498,13 +501,13 @@ asgiref = [ {file = "asgiref-3.5.2-py3-none-any.whl", hash = "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4"}, {file = "asgiref-3.5.2.tar.gz", hash = "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"}, ] -babel = [ +Babel = [ {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, ] certifi = [ - {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, - {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, + {file = "certifi-2022.6.15.1-py3-none-any.whl", hash = "sha256:43dadad18a7f168740e66944e4fa82c6611848ff9056ad910f8f7a3e46ab89e0"}, + {file = "certifi-2022.6.15.1.tar.gz", hash = "sha256:cffdcd380919da6137f76633531a5817e3a9f268575c128249fb637e4f9e73fb"}, ] charset-normalizer = [ {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, @@ -518,9 +521,9 @@ colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] -django = [ - {file = "Django-4.0.5-py3-none-any.whl", hash = "sha256:502ae42b6ab1b612c933fb50d5ff850facf858a4c212f76946ecd8ea5b3bf2d9"}, - {file = "Django-4.0.5.tar.gz", hash = "sha256:f7431a5de7277966f3785557c3928433347d998c1e6459324501378a291e5aab"}, +Django = [ + {file = "Django-4.0.6-py3-none-any.whl", hash = "sha256:ca54ebedfcbc60d191391efbf02ba68fb52165b8bf6ccd6fe71f098cac1fe59e"}, + {file = "Django-4.0.6.tar.gz", hash = "sha256:a67a793ff6827fd373555537dca0da293a63a316fe34cb7f367f898ccca3c3ae"}, ] django-environ = [ {file = "django-environ-0.9.0.tar.gz", hash = "sha256:bff5381533056328c9ac02f71790bd5bf1cea81b1beeb648f28b81c9e83e0a21"}, @@ -538,7 +541,7 @@ imagesize = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] -jinja2 = [ +Jinja2 = [ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] @@ -546,7 +549,7 @@ markdown-it-py = [ {file = "markdown-it-py-2.1.0.tar.gz", hash = "sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"}, {file = "markdown_it_py-2.1.0-py3-none-any.whl", hash = "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27"}, ] -markupsafe = [ +MarkupSafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, @@ -593,8 +596,8 @@ mdit-py-plugins = [ {file = "mdit_py_plugins-0.3.0-py3-none-any.whl", hash = "sha256:b1279701cee2dbf50e188d3da5f51fee8d78d038cdf99be57c6b9d1aa93b4073"}, ] mdurl = [ - {file = "mdurl-0.1.1-py3-none-any.whl", hash = "sha256:6a8f6804087b7128040b2fb2ebe242bdc2affaeaa034d5fc9feeed30b443651b"}, - {file = "mdurl-0.1.1.tar.gz", hash = "sha256:f79c9709944df218a4cdb0fcc0b0c7ead2f44594e3e84dc566606f04ad749c20"}, + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] myst-parser = [ {file = "myst-parser-0.18.0.tar.gz", hash = "sha256:739a4d96773a8e55a2cacd3941ce46a446ee23dcd6b37e06f73f551ad7821d86"}, @@ -604,19 +607,19 @@ packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, +Pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, ] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pytz = [ - {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, - {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, + {file = "pytz-2022.2.1-py2.py3-none-any.whl", hash = "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197"}, + {file = "pytz-2022.2.1.tar.gz", hash = "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"}, ] -pyyaml = [ +PyYAML = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, @@ -712,8 +715,8 @@ typing-extensions = [ {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, ] tzdata = [ - {file = "tzdata-2022.1-py2.py3-none-any.whl", hash = "sha256:238e70234214138ed7b4e8a0fab0e5e13872edab3be586ab8198c407620e2ab9"}, - {file = "tzdata-2022.1.tar.gz", hash = "sha256:8b536a8ec63dc0751342b3984193a3118f8fca2afe25752bb9b7fffd398552d3"}, + {file = "tzdata-2022.2-py2.py3-none-any.whl", hash = "sha256:c3119520447d68ef3eb8187a55a4f44fa455f30eb1b4238fa5691ba094f2b05b"}, + {file = "tzdata-2022.2.tar.gz", hash = "sha256:21f4f0d7241572efa7f7a4fdabb052e61b55dc48274e6842697ccdf5253e5451"}, ] urllib3 = [ {file = "urllib3-1.26.11-py2.py3-none-any.whl", hash = "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc"}, diff --git a/front/src/style/components/_queue.scss b/front/src/style/components/_queue.scss index 31534ae84..03e7e1d65 100644 --- a/front/src/style/components/_queue.scss +++ b/front/src/style/components/_queue.scss @@ -123,8 +123,10 @@ top: 0; width: 32%; > img { - height: 50vh; - width: 50vh; + width: 100%; + height: auto; + max-height: 50vh; + max-width: 50vh; } @include media("