Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Logo Icons too big fix (Issue: 1290) #1306

Merged
merged 4 commits into from
Feb 9, 2024
Merged

Conversation

bbearce
Copy link
Collaborator

@bbearce bbearce commented Feb 3, 2024

A brief description of the purpose of the changes contained in this PR.

Logos on public challenge page are taking too long to load. In this fix I propose we change:

  • comp upload to make an icon_logo file upon saving original logo
  • logo upload to do the same however the difference is a new logo gets a new folder in minio and the icon_logo for this file is dropped in the same folder
    • Question: Delete old logo? We weren't before.
  • Attached is a file for performing this one challenge at a time to practice

Issues this PR resolves

A checklist for hand testing

# ...code to get comps_no_icon_logo
for comp in comps_no_icon_logo:
    pdb.set_trace(); # use 'c' to cycle through 1 at a time; comment to do all;
    comp.make_logo_icon()
    comp.save()

Any relevant files for testing

add_icon_sized_logo.md

the public competitions link wouldn't update and the only way I found to fix it was to clear cookies for this url:
image
image

Checklist

  • Code review by me
  • Hand tested by me
  • I'm proud of my work
  • Code review by reviewer
  • Hand tested by reviewer
  • CircleCi tests are passing
  • Ready to merge

@Didayolo
Copy link
Collaborator

Didayolo commented Feb 8, 2024

@bbearce Some questions:

  • Do we have commands to run in production to solve already existing competitions?
  • Is it possible to upload a competition without logo? What happens then?

@Didayolo
Copy link
Collaborator

Didayolo commented Feb 8, 2024

Benjamin's instructions:

Let's walk through doing this methodically to be safe

Shell into django

docker compose exec django bash
python manage.py shell_plus --plain

Get competitions that don't have logo icons

import io, os
from PIL import Image
from django.core.files.base import ContentFile
comps_no_icon_logo = Competition.objects.filter(logo_icon__isnull=True)
all = Competition.objects.all()
len(Competition.objects.all())
len(comps_no_icon_logo)

1 at a time to gain confidence

  1. Get minio up to check what is happening and get a test comp.
test = comps_no_icon_logo[0]
print(test.logo) # Ex: >>> logos/2024-01-29-1706501898/c0a9208bca26/logo.jpg
print(test.logo_icon) # Ex: >>> logos/2024-01-29-1706501898/c0a9208bca26/logo.jpg
  1. Click into public/logos copy and paste 2024-01-29-1706501898 into minio search and navigate to deepest level.

remember to delete from search bar once there as it is still searching for 2024-01-29-1706501898 inside 2024-01-29-1706501898.

  1. Verfiy there is only a logo.jpg | logo.png or other image file

What are we doing if it is some obscure image file type? Not sure how to test yet.

  1. Let's generate a logo_icon file.
icon_dirname_only = os.path.dirname(test.logo.name)  # Get just the path
icon_basename_only = os.path.basename(test.logo.name)  # Get filename plus ext
file_name = os.path.splitext(icon_basename_only)[0] # Get just the filename
ext = os.path.splitext(icon_basename_only)[1] # Get ext
new_path = os.path.join(icon_dirname_only, f"{file_name}_icon{ext}") # Craft new path
logo_content = test.logo.read()
original_logo = Image.open(io.BytesIO(logo_content))
# Resize the image to a smaller size for logo_icon
width, height = original_logo.size
new_width = 100  # Specify the desired width for the logo_icon
new_height = int((new_width / width) * height)
resized_logo = original_logo.resize((new_width, new_height))
# Create a BytesIO object to save the resized image
icon_content = io.BytesIO()
resized_logo.save(icon_content, format='PNG')
test.logo_icon.save(new_path, ContentFile(icon_content.getvalue()),save=False)
test.save()

There should be a logo_icon.jpg file or at least a _icon version of the previous logo

  1. Dangerous Loop through all comps with no logo_icon and create:
    The above code is actually baked into the Competition model object through method .make_logo_icon().
test2 = comps_no_icon_logo[1]

Loop over the rest

import pdb
for comp in comps_no_icon_logo:
    pdb.set_trace(); # use 'c' to cycle through 1 at a time
    comp.make_logo_icon()
    comp.save()

Notes:

src/utils/data.py has a PathWrapper class I edited. Without this change the logo_icon will be saved in a new folder on minio not associated with the original. I overwrite with a manual_override argument so that I can use the same directory as original logo.

@Didayolo
Copy link
Collaborator

Didayolo commented Feb 9, 2024

Note for me:

for comp in comps_no_icon_logo:
    comp.make_logo_icon()
    comp.save()

@Didayolo
Copy link
Collaborator

Didayolo commented Feb 9, 2024

@bbearce

Great job, I tested everything and it seems fine!

Maybe we should also load the icon logo in the front page of the platform?

Maybe it's not a big deal because the front page loads even if the logos are not loaded.

Capture d’écran 2024-02-09 à 20 42 31

@Didayolo Didayolo merged commit 61b0149 into develop Feb 9, 2024
1 check passed
@Didayolo Didayolo deleted the issue_1290_logo_sizes branch February 9, 2024 19:51
@Didayolo
Copy link
Collaborator

Didayolo commented Feb 9, 2024

@bbearce After deploying this on codabench-test server, I have an issue.

EDIT: IT'S ONLY WHEN NOT LOGGED IN

I ran the script to create a "logo icon" for all competitions. Now when I load the competition list, I get this error:

Capture d’écran 2024-02-09 à 21 03 42

Some logs:

codabench-django-1       | Internal Server Error: /api/competitions/public/
codabench-django-1       | Traceback (most recent call last):
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
codabench-django-1       |     response = get_response(request)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
codabench-django-1       |     response = self.process_exception_by_middleware(e, request)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
codabench-django-1       |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
codabench-django-1       |     return view_func(*args, **kwargs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/viewsets.py", line 116, in view
codabench-django-1       |     return self.dispatch(request, *args, **kwargs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 495, in dispatch
codabench-django-1       |     response = self.handle_exception(exc)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 455, in handle_exception
codabench-django-1       |     self.raise_uncaught_exception(exc)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 492, in dispatch
codabench-django-1       |     response = handler(request, *args, **kwargs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework_extensions/cache/decorators.py", line 44, in inner
codabench-django-1       |     return this.process_cache_response(
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework_extensions/cache/decorators.py", line 68, in process_cache_response
codabench-django-1       |     response = view_method(view_instance, request, *args, **kwargs)
codabench-django-1       |   File "/app/src/apps/api/views/competitions.py", line 547, in public
codabench-django-1       |     return self.get_paginated_response(serializer.data)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 768, in data
codabench-django-1       |     ret = super(ListSerializer, self).data
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 262, in data
codabench-django-1       |     self._data = self.to_representation(self.instance)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 685, in to_representation
codabench-django-1       |     return [
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 686, in <listcomp>
codabench-django-1       |     self.child.to_representation(item) for item in iterable
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 530, in to_representation
codabench-django-1       |     ret[field.field_name] = field.to_representation(attribute)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 530, in to_representation
codabench-django-1       |     ret[field.field_name] = field.to_representation(attribute)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/rest_framework/fields.py", line 1893, in to_representation
codabench-django-1       |     return method(value)
codabench-django-1       |   File "/app/src/apps/api/serializers/queues.py", line 105, in get_competitions
codabench-django-1       |     competitions = obj.competitions.filter(
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
codabench-django-1       |     return getattr(self.get_queryset(), name)(*args, **kwargs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 892, in filter
codabench-django-1       |     return self._filter_or_exclude(False, *args, **kwargs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 910, in _filter_or_exclude
codabench-django-1       |     clone.query.add_q(Q(*args, **kwargs))
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1290, in add_q
codabench-django-1       |     clause, _ = self._add_q(q_object, self.used_aliases)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1310, in _add_q
codabench-django-1       |     child_clause, needed_inner = self._add_q(
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1315, in _add_q
codabench-django-1       |     child_clause, needed_inner = self.build_filter(
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1251, in build_filter
codabench-django-1       |     condition = self.build_lookup(lookups, col, value)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1116, in build_lookup
codabench-django-1       |     lookup = lookup_class(lhs, rhs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/lookups.py", line 20, in __init__
codabench-django-1       |     self.rhs = self.get_prep_lookup()
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/fields/related_lookups.py", line 115, in get_prep_lookup
codabench-django-1       |     self.rhs = target_field.get_prep_value(self.rhs)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 972, in get_prep_value
codabench-django-1       |     return int(value)
codabench-django-1       |   File "/usr/local/lib/python3.8/site-packages/django/contrib/auth/models.py", line 388, in __int__
codabench-django-1       |     raise TypeError('Cannot cast AnonymousUser to int. Are you trying to use it in place of User?')
codabench-django-1       | TypeError: Cannot cast AnonymousUser to int. Are you trying to use it in place of User?

@Didayolo
Copy link
Collaborator

Didayolo commented Feb 9, 2024

TypeError: Cannot cast AnonymousUser to int. Are you trying to use it in place of User?

This error is raised because somewhere in your code (likely in a query or a model method that involves user filtering), there's an assumption that the user is a registered user with a valid ID. However, since the user is not logged in, Django substitutes a default AnonymousUser object, which does not have an integer ID that can be used in database queries.

@Didayolo
Copy link
Collaborator

Didayolo commented Feb 9, 2024

It seems that the bug does not come from this PR but from this one: #1317

In src/apps/api/serializers/queues.py line 102

@bbearce So all is good here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add thumbnail logo to competitions to increase loading speed of the list
3 participants