Skip to content

Commit

Permalink
feat(user): support for teacher yearbook photos (closes #1623)
Browse files Browse the repository at this point in the history
  • Loading branch information
NotFish232 committed Apr 10, 2024
1 parent 3feb200 commit 5bf22f5
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 31 deletions.
1 change: 1 addition & 0 deletions config/docker/initial_setup.sh
Expand Up @@ -23,6 +23,7 @@ for year in "freshman" "sophomore" "junior" "senior"; do
python3 create_users.py -t admin -nw -y $year -n admin admin1 admin2 admin3 admin4 admin5
done
python3 create_users.py -nw -ny -t admin -n admin
python3 create_users.py -nw -ny -t teacher -n teacher
python3 create_users.py -t admin -c 10
python3 create_users.py -t student -c 100
python3 create_users.py -t teacher -c 20
Expand Down
34 changes: 27 additions & 7 deletions intranet/apps/preferences/forms.py
Expand Up @@ -19,19 +19,39 @@ def __init__(self, *args, **kwargs):
self.fields["bus_route"] = forms.ChoiceField(choices=self.BUS_ROUTE_CHOICES, widget=forms.Select, required=False)


class UploadPictureForm(forms.Form):
uploaded_photo = forms.ImageField(required=False)


class DeletePictureForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)

picture_choices = []

for i in range(user.photos.count()):
picture_choices.append((i, f"Photo {i + 1}"))

self.fields["photos_to_delete"] = forms.MultipleChoiceField(choices=picture_choices, widget=forms.CheckboxSelectMultiple(), required=False)


class PreferredPictureForm(forms.Form):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)

self.PREFERRED_PICTURE_CHOICES = [("AUTO", "Auto-select the most recent photo")]

for i in range(4):
try:
grade = Grade.names[i]
user.photos.get(grade_number=i + 9) # Only display option if the photo exists
self.PREFERRED_PICTURE_CHOICES += [(i + 9, grade.title() + " Photo")]
except Exception:
pass
if user.is_student:
for i in range(4):
try:
grade = Grade.names[i]
user.photos.get(grade_number=i + 9) # Only display option if the photo exists
self.PREFERRED_PICTURE_CHOICES.append((i + 9, grade.title() + " Photo"))
except Exception:
pass
if user.is_teacher:
for i in range(user.photos.count()):
self.PREFERRED_PICTURE_CHOICES.append((i, f"Photo {i + 1}"))

self.fields["preferred_photo"] = forms.ChoiceField(choices=self.PREFERRED_PICTURE_CHOICES, widget=forms.RadioSelect(), required=True)

Expand Down
101 changes: 85 additions & 16 deletions intranet/apps/preferences/views.py
@@ -1,6 +1,8 @@
import io
import logging

from cacheops import invalidate_obj
from PIL import Image

from django.conf import settings
from django.contrib import messages
Expand All @@ -10,8 +12,9 @@

from ..auth.decorators import eighth_admin_required
from ..bus.models import Route
from ..users.models import Email
from .forms import BusRouteForm, DarkModeForm, EmailFormset, NotificationOptionsForm, PreferredPictureForm, PrivacyOptionsForm
from ..users.models import Email, Photo
from .forms import (BusRouteForm, DarkModeForm, DeletePictureForm, EmailFormset, NotificationOptionsForm, PreferredPictureForm, PrivacyOptionsForm,
UploadPictureForm)

# from .forms import (BusRouteForm, DarkModeForm, EmailFormset, NotificationOptionsForm, PhoneFormset, PreferredPictureForm, PrivacyOptionsForm,
# WebsiteFormset)
Expand Down Expand Up @@ -90,7 +93,16 @@ def get_preferred_pic(user):
# FIXME: remove this hardcoded junk
preferred_pic = {"preferred_photo": "AUTO"}
if user.preferred_photo:
preferred_pic["preferred_photo"] = user.preferred_photo.grade_number
if user.is_student:
preferred_pic["preferred_photo"] = user.preferred_photo.grade_number
else:
photo_idx = -1
for idx, photo in enumerate(user.photos.all()):
if photo.id == user.preferred_photo.id:
photo_idx = idx
break
if photo_idx != -1:
preferred_pic["preferred_photo"] = str(photo_idx)

return preferred_pic

Expand All @@ -112,7 +124,11 @@ def save_preferred_pic(request, user):
if new_preferred_pic == "AUTO":
user.preferred_photo = None
else:
user.preferred_photo = user.photos.get(grade_number=new_preferred_pic)
if user.is_student:
photo = user.photos.get(grade_number=new_preferred_pic)
else:
photo = user.photos.all()[int(new_preferred_pic)]
user.preferred_photo = photo
user.save()
except Exception as e:
messages.error(request, "Unable to set field {} with value {}: {}".format("preferred_pic", new_preferred_pic, e))
Expand Down Expand Up @@ -291,6 +307,45 @@ def save_dark_mode_settings(request, user):
return dark_mode_form


def save_uploaded_picture(request, user):
upload_picture_form = UploadPictureForm(request.POST, request.FILES)
if upload_picture_form.is_valid():
try:
upload_photo = upload_picture_form.cleaned_data["uploaded_photo"]

img = Image.open(upload_photo)
current_size = img.size
new_size = (min(current_size[0], 200), min(current_size[1], 250))
img = img.resize(new_size)

img_arr = io.BytesIO()
img.save(img_arr, format="JPEG")

value = img_arr.getvalue()
Photo.objects.create(user=user, grade_number=13, _binary=value)
except Exception as e:
logger.error("Error saving uploaded image: %s", e)

return upload_picture_form


def save_deleted_picture(request, user):
delete_picture_form = DeletePictureForm(user, data=request.POST)
if delete_picture_form.is_valid():
photo_idxs_to_delete = delete_picture_form.cleaned_data["photos_to_delete"]
photo_idxs_to_delete = [int(p_idx) for p_idx in photo_idxs_to_delete]

photos_to_delete = []
for idx, photo in enumerate(user.photos.all()):
if idx in photo_idxs_to_delete:
photos_to_delete.append(photo)

for photo in photos_to_delete:
photo.delete()

return delete_picture_form


@login_required
def preferences_view(request):
"""View and process updates to the preferences page."""
Expand All @@ -300,19 +355,23 @@ def preferences_view(request):
logger.debug("Preparing to update user preferences for user %s", request.user.id)
# phone_formset, email_formset, website_formset, errors = save_personal_info(request, user)
_, email_formset, _, errors = save_personal_info(request, user)
if user.is_student:

if user.is_student or user.is_teacher:
preferred_pic_form = save_preferred_pic(request, user)

if user.is_teacher:
upload_picture_form = save_uploaded_picture(request, user)
delete_picture_form = save_deleted_picture(request, user)

if user.is_student:
bus_route_form = save_bus_route(request, user)
"""
The privacy options form is disabled due to the
permissions feature being unused and changes to school policy.
"""
# privacy_options_form = save_privacy_options(request, user)
privacy_options_form = None
else:
preferred_pic_form = None
bus_route_form = None
privacy_options_form = None

notification_options_form = save_notification_options(request, user)

dark_mode_form = save_dark_mode_settings(request, user)
Expand All @@ -332,10 +391,23 @@ def preferences_view(request):
email_formset = EmailFormset(instance=user, prefix="ef")
# website_formset = WebsiteFormset(instance=user, prefix="wf")

if user.is_student:
preferred_pic = None
preferred_pic_form = None
bus_route_form = None
privacy_options_form = None
upload_picture_form = None
delete_picture_form = None

if user.is_student or user.is_teacher:
preferred_pic = get_preferred_pic(user)
bus_route = get_bus_route(user)
preferred_pic_form = PreferredPictureForm(user, initial=preferred_pic)

if user.is_teacher:
upload_picture_form = UploadPictureForm()
delete_picture_form = DeletePictureForm(user)

if user.is_student:
bus_route = get_bus_route(user)
bus_route_form = BusRouteForm(initial=bus_route)

"""
Expand All @@ -347,11 +419,6 @@ def preferences_view(request):
privacy_options_form = PrivacyOptionsForm(user, initial=privacy_options)
"""
privacy_options_form = None
else:
bus_route_form = None
preferred_pic = None
preferred_pic_form = None
privacy_options_form = None

notification_options = get_notification_options(user)
notification_options_form = NotificationOptionsForm(user, initial=notification_options)
Expand All @@ -367,6 +434,8 @@ def preferences_view(request):
"notification_options_form": notification_options_form,
"bus_route_form": bus_route_form if settings.ENABLE_BUS_APP else None,
"dark_mode_form": dark_mode_form,
"upload_picture_form": upload_picture_form,
"delete_picture_form": delete_picture_form,
}
return render(request, "preferences/preferences.html", context)

Expand Down
2 changes: 1 addition & 1 deletion intranet/apps/users/urls.py
Expand Up @@ -4,5 +4,5 @@

urlpatterns = [
re_path(r"^(?:/(?P<user_id>\d+))?$", views.profile_view, name="user_profile"),
re_path(r"^/picture/(?P<user_id>\d+)(?:/(?P<year>freshman|sophomore|junior|senior))?$", views.picture_view, name="profile_picture"),
re_path(r"^/picture/(?P<user_id>\d+)(?:/(?P<year>freshman|sophomore|junior|senior|teacher\d+))?$", views.picture_view, name="profile_picture"),
]
11 changes: 9 additions & 2 deletions intranet/apps/users/views.py
Expand Up @@ -123,18 +123,25 @@ def picture_view(request, user_id, year=None):
default_image_path = os.path.join(settings.PROJECT_ROOT, "static/img/default_profile_pic.png")

img = None
data = None

if year is None:
preferred = user.preferred_photo
if preferred is None:
data = user.default_photo
else:
data = preferred.binary
elif year.startswith("teacher"):
t_idx = year[len("teacher"):]
if t_idx.isdigit():
idx = int(t_idx)
photos = user.photos.all()
if idx < len(photos):
data = photos[idx].binary
else:
grade_number = Grade.number_from_name(year)
if user.photos.filter(grade_number=grade_number).exists():
data = user.photos.filter(grade_number=grade_number).first().binary
else:
data = None

if data is None:
img = io.open(default_image_path, mode="rb").read()
Expand Down
23 changes: 21 additions & 2 deletions intranet/templates/preferences/preferences.html
Expand Up @@ -79,7 +79,7 @@ <h2>Preferences</h2>
<h3>Mail Forwarding</h3>
<p>Set up email forwarding from your @tjhsst.edu email <a href="https://mailforwarding.tjhsst.edu" target="_blank">here</a>.</p>
{% endif %}
<form action="" method="post">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<br>
{% if bus_route_form %}
Expand Down Expand Up @@ -176,14 +176,33 @@ <h3>Personal Information</h3>
{% if preferred_pic_form %}
<br>
<h3>Preferred Picture</h3>
{% if request.user.is_student %}
<p>Since the eighth period office and TJ faculty can always see your pictures, it is recommended that you choose your preferred picture.</p>

{% endif %}
{% for choice in preferred_pic_form.preferred_photo %}
{{ choice.tag }} {{ choice.choice_label }}<br>
{% endfor %}
<br>
{% endif %}

{% if upload_picture_form %}
<h3>Upload Picture</h3>
{% for field in upload_picture_form %}
{{ field }}
{% endfor %}
<br>
<br>
{% endif %}

{% if delete_picture_form %}
<h3>Delete Picture(s)</h3>
{% for field in delete_picture_form %}
{{ field }}
{% endfor %}
<br>
<br>
{% endif %}

{% comment %}
The below privacy options form is disabled due to the
permissions feature being unused and changes to school policy.
Expand Down
20 changes: 17 additions & 3 deletions intranet/templates/users/profile.html
Expand Up @@ -32,7 +32,7 @@

{% block js %}
{{ block.super }}
{% if profile_user.is_student %}
{% if profile_user.is_student or profile_user.is_teacher %}
<script>
$(document).ready(function() {
$('.preferred-user-picture img, .preferred-user-picture span').click(function() {
Expand Down Expand Up @@ -124,13 +124,27 @@
</td>
</tr>
</table>
{% elif profile_user.is_teacher %}
<table>
<tr>
{% for photo in profile_user.photos.all %}
{% with forloop.counter0|stringformat:"s" as s_idx %}
<td>
<img data-src="{% url 'profile_picture' profile_user.id 'teacher'|add:s_idx %}" alt="Picture {{forloop.counter}}" title="Picture {{forloop.counter}}">
<br>
Picture {{forloop.counter}}
</td>
{% endwith %}
{% endfor %}
</tr>
</table>
{% else %}
No other pictures are available.
{% endif %}
</div>
<div class="{% if profile_user.is_student %}multiple-pics{% endif %} preferred-user-picture">
<div class="{% if profile_user.is_student or profile_user.is_teacher %}multiple-pics{% endif %} preferred-user-picture">
<img src="{% url 'profile_picture' profile_user.id %}" alt="Preferred Picture" title="View pictures" width="172">
{% if profile_user.is_student %}
{% if profile_user.is_student or profile_user.is_teacher %}
<span>
View all pictures
</span>
Expand Down

0 comments on commit 5bf22f5

Please sign in to comment.