Commit ac9810e0 authored by Daniel W Bond's avatar Daniel W Bond
Browse files

Merge branch 'double_majors' into amherst

parents fea94a9c f858d982
......@@ -7,13 +7,22 @@ from .models import Student, Major, Confirmation
class StudentAdmin(admin.ModelAdmin):
list_display = ("get_name", "room", "privacy", "major", "created")
list_display = ("get_name", "room", "privacy", "get_first_major", "created")
def get_name(self, student):
return student.get_full_name_or_uname()
get_name.short_description = 'Name'
get_name.admin_order_field = 'user__username' # ordering by callables is hard
# We cannot use a manytomanyfield as a field in the list, so we're getting the first
# major. If we need to see a student's second major, we can just click the student.
# This covers nearly all students, who will have only one major.
def get_first_major(self, student):
return student.major.first()
get_first_major.short_description = 'Major'
# we're not going to give the option to sort by major for now
class MajorAdmin(admin.ModelAdmin):
list_display = ("name", "get_major_num", )
......
......@@ -80,7 +80,7 @@ class StudentUpdateForm(forms.Form):
room = SelectRoomField(queryset=Room.objects.all(), required=False)
privacy = forms.TypedChoiceField(choices=Student.PRIVACY_CHOICES)
major = forms.ModelChoiceField(queryset=Major.objects.all(), required=False)
major = forms.ModelMultipleChoiceField(queryset=Major.objects.all(), required=False)
graduating_year = forms.IntegerField()
def clean(self):
......
......@@ -168,7 +168,7 @@ class Student(TimeStampedModel):
on_campus = models.BooleanField(default=True)
room = models.ForeignKey(Room, null=True, blank=True)
major = models.ForeignKey('Major', related_name='major', null=True, blank=True)
major = models.ManyToManyField(Major, related_name='majors', blank=True)
times_changed_room = models.PositiveIntegerField(default=0)
......
......@@ -60,12 +60,13 @@
<table class="table table-hover text-center">
<tbody>
<tr>
<td><h4><strong>Major</strong>:
{% if student.major %}
<a href="{% url 'detail_major' student.major.slug %}">{{ student.major.name }}</a>
{% else %}
&nbsp;&mdash;
{% endif %}
{% if student.major.all|length == 2 %}
<td><h4><strong>Majors</strong>:
{% else %}
<td><h4><strong>Major</strong>:
{% endif %}
{% include 'double_major.html' %}
</h4></td>
</tr>
......
......@@ -29,6 +29,7 @@
<div class="col-sm-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
<h5>{{ student.graduating_year }}</h5>
{% if student.get_flag_count > 4 %}
<p><em>* a number of other floormates say this info is incorrect</em></p>
{% endif %}
......@@ -36,7 +37,7 @@
{% endfor %}
</div>
{% empty %}
No visible students in the database. :'-(
No visible students.
{% endfor %}
</div>
</div>
......@@ -55,6 +56,7 @@
<div class="col-sm-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
<h5>{{ student.graduating_year }}</h5>
{% if student.get_flag_count > 4 %}
<p><em>* a number of other floormates say this info is incorrect</em></p>
{% endif %}
......@@ -62,7 +64,7 @@
{% endfor %}
</div>
{% empty %}
No visible students in the database. :'-(
No visible students.
{% endfor %}
</div>
</div>
......@@ -81,6 +83,7 @@
<div class="col-sm-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
<h5>{{ student.graduating_year }}</h5>
{% if student.get_flag_count > 4 %}
<p><em>* a number of other floormates say this info is incorrect</em></p>
{% endif %}
......@@ -88,7 +91,7 @@
{% endfor %}
</div>
{% empty %}
No visible students in the database. :'-(
No visible students.
{% endfor %}
</div>
</div>
......@@ -103,6 +106,7 @@
<div class="col-sm-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
<h5>{{ student.graduating_year }}</h5>
</div>
{% endfor %}
</div>
......
{% if student.major.all|length > 0 %}
<a href="{{ student.major.all.0.get_absolute_url }}">
{{ student.major.all.0.name }}</a>
{% if student.major.all|length == 2 %}
&<a href="{{ student.major.all.1.get_absolute_url }}">
{{ student.major.all.1.name }}</a>
{% endif %}
{% else %}
&nbsp;&mdash;
{% endif %}
......@@ -49,11 +49,9 @@ SRCT Roomlist &bull; Search Students
<a href="{{ result.object.get_absolute_url }}"><h4><strong>{{ result.object.get_full_name_or_uname }}</strong></h4></a>
</td>
<td>
{% if result.object.major %}
<a href="{{ result.object.major.get_absolute_url }}"><h4>{{ result.object.major }}</h4></a>
{% else %}
&nbsp;&mdash;
{% endif %}
{% with result.object as student %}
<h4>{% include 'double_major.html' %}</h4>
{% endwith %}
</td>
<td>
{% if result.object.graduating_year %}
......
......@@ -147,11 +147,13 @@
</div>
<div class="panel-body">
<p class="text-center">A select number of locations <a href="{% url 'list_buildings'%}#Unsupported">on campus</a> aren't currently supported. You are still welcome to use Roomlist as a limited directory until we add support.</p>
<p class="text-center">
<p>
If you have a <a href="https://en.gravatar.com/">Gravatar profile</a> associated
with your <a href="https://masonlive.gmu.edu/">Masonlive email</a> address, your
default profile picture on this service will that Gravatar profile picture.
profile picture on Roomlist will use Gravatar.
</p>
<p>
If you connect your Facebook account, we will use your Facebook profile picture.
</p>
<hr />
{% if my_form.non_field_errors %}
......@@ -212,7 +214,7 @@
</div>
<div class="form-group">
<label for="{{ my_form.major.id_for_label }}" class="col-md-2">
Major<br />(select one)
Major(s)
</label>
<div class="col-md-4">
{{ my_form.major }}
......@@ -237,6 +239,9 @@
{% endfor %}
</div>
<div id="lives-on-campus">
<hr />
<p class="help-block">The Global Center, the Townhouses, and Beacon Hall are not currently supported.<br />
We'll let everyone know when we add them.</p>
<div class="form-group">
<div class="col-md-12">
{{ my_form.room }}
......@@ -268,7 +273,8 @@
<script type="text/javascript" src="/static/js/chained.min.js"></script>
<script type="text/javascript" src="/static/js/chosen.min.js"></script>
<script>
$(".chosen-select").chosen()
$(".chosen-select").chosen({max_selected_options: 2,
placeholder_text_multiple: '(select up to two)'});
</script>
{% include 'room_selection_script.html' %}
{% endblock javascript %}
......@@ -154,6 +154,7 @@ class UpdateStudent(LoginRequiredMixin, FormValidMessageMixin, FormView):
context = super(UpdateStudent, self).get_context_data(**kwargs)
me = Student.objects.get(user=self.request.user)
majors = [pk_or_none(me, major) for major in me.major.all()]
form = StudentUpdateForm(initial={'first_name': me.user.first_name,
'last_name': me.user.last_name,
......@@ -161,7 +162,7 @@ class UpdateStudent(LoginRequiredMixin, FormValidMessageMixin, FormView):
'show_gender': me.show_gender,
'room': pk_or_none(me, me.room),
'privacy': me.privacy,
'major': pk_or_none(me, me.major),
'major': majors,
'graduating_year': me.graduating_year,
'on_campus': me.on_campus, })
......@@ -188,7 +189,7 @@ class UpdateStudent(LoginRequiredMixin, FormValidMessageMixin, FormView):
@ratelimit(key='user', rate='10/d', method='POST', block=True)
def post(self, request, *args, **kwargs):
# for key, value in request.POST.iteritems():
# print(key, value)
# print(key, value)
return super(UpdateStudent, self).post(request, *args, **kwargs)
def form_valid(self, form):
......@@ -197,7 +198,7 @@ class UpdateStudent(LoginRequiredMixin, FormValidMessageMixin, FormView):
# print("In form valid method!")
# for key, value in form.data.iteritems():
# print(key, value)
# print(key, value)
current_room = me.room
......@@ -226,9 +227,27 @@ class UpdateStudent(LoginRequiredMixin, FormValidMessageMixin, FormView):
me.room = form_room
try:
me.major = Major.objects.get(pk=form.data['major'])
# in case someone disabled the js, limit processing to only the first
# two majors passed by the user
# we also eliminate the potential a student manipulates the form to
# pass in two majors of the same type by casting to a set
form_major_pks = set(form.data.getlist('major')[:2])
# retrieve the major objects from the list of pk strings
form_majors = [Major.objects.get(pk=pk) for pk in form_major_pks]
# print(form_majors)
# iterate over a student's current majors
for current_major in me.major.all():
# remove m2m relationship if not in majors from form
if current_major not in form_majors:
me.major.remove(current_major)
# iterate over the majors in the form
for form_major in form_majors:
# add new m2m relationship to student
if form_major not in me.major.all():
me.major.add(form_major)
except:
me.major = None
# don't change majors
pass
me.user.first_name = form.data['first_name']
me.user.last_name = form.data['last_name']
......@@ -278,7 +297,7 @@ class DetailMajor(LoginRequiredMixin, DetailView):
rooms = [
room
for room in Room.objects.filter(floor__building__neighbourhood=neighbourhood)
if room.student_set.filter(major=self.get_object())
if room.student_set.filter(major__in=[self.get_object()])
]
# identify if the student(s) in that room are visible to the requesting student
......@@ -294,7 +313,7 @@ class DetailMajor(LoginRequiredMixin, DetailView):
])), key=attrgetter('user.username'))
# see what students are left over (aren't visible)
hidden = set(Student.objects.filter(major=self.get_object()).order_by('user__username'))
hidden = set(Student.objects.filter(major__in=[self.get_object()]).order_by('user__username'))
# print(hidden)
for visible in visible_by_neighbourhood.values():
# print('visible', visible)
......
......@@ -29,7 +29,7 @@
<div class="col-sm-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
<h5><a href="{{ student.major.get_absolute_url }}"><em>{{ student.major.name }}</em></a></h5>
<h5><em>{% include 'double_major.html' %}</em></h5>
<h5><a href="{{ student.room.get_absolute_url }}">{{ student.room }}</a></h5>
{% if student.get_flag_count %}
<p><em>*a number of other floormates say this info is incorrect</em></p>
......
......@@ -30,7 +30,7 @@
<div class="col-sm-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
<h5><a href="{{ student.major.get_absolute_url }}"><em>{{ student.major.name }}</em></a></h5>
<h5><em>{% include 'double_major.html' %}</em></h5>
{% if student.get_flag_count > 4 %}
<p><em>* a number of other floormates say this info is incorrect</em></p>
{% endif %}
......
......@@ -29,7 +29,12 @@ class LandingPage(LoginRequiredMixin, TemplateView):
# Create Dictionaries to store Students that meet criteria
context["roomies"] = Student.objects.filter(room=me.room).exclude(user__username=me)
context["floories"] = Student.objects.filter(room__floor=me.get_floor()).exclude(user__username=me).exclude(room=me.room).order_by('room')
context["majormates"] = Student.objects.filter(major=me.major).exclude(user__username=me).order_by('?')[:8]
my_majors = tuple(me.major.all())
students_by_major = {}
for major in my_majors:
students_by_major[major] = Student.objects.filter(major__in=[major]).exclude(user__username=me).order_by('?')[:8]
context["majormates"] = students_by_major
return context
......
......@@ -19,7 +19,7 @@ Back
{% if me.on_campus %}
{% if not me.room %}
{% if not me.room and me.on_campus %}
<h3><em><a href="{% url 'update_student' request.user.username %}">Select your room</a>, and we'll show you the other students on your floor.</em></h3>
{% else %}
......@@ -79,14 +79,14 @@ Back
{% cache 120 landing_majors request.user.username %}
{% if not me.major %}
{% if not me.major.all %}
<h3><em><a href="{% url 'update_student' request.user.username %}">Set your major</a>, and we'll show you some of the other students in your program.</em></h3>
{% else %}
<h3>Other Students in <a href="{{ me.major.get_absolute_url }}">{{ me.major.name }}</a></h3>
<legend></legend>
{% for major, students in majormates.items %}
<h3>Other Students in <a href="{{ major.get_absolute_url }}">{{ major.name }}</a></h3>
<hr />
<div class="row">
{% for student in majormates %}
{% for student in students %}
<div class="col-md-3 text-center">
<img class="img-circle img-responsive center center-block" src="{{ student.profile_image_url }}" alt="{{ student.get_first_name_or_uname }} profile picture">
<h4><a href="{{ student.get_absolute_url }}"><strong>{{ student.get_full_name_or_uname }}</strong></a></h4>
......@@ -102,7 +102,7 @@ Back
</div>
{% endfor %}
</div>
{% endfor %}
{% endif %}
{% endcache %}
......
......@@ -44,8 +44,6 @@
</div>
<div class="panel-body">
<p><small>Right now you can only select one major. We're still working to add
support for overachievers.</small></p>
<form action="" method="post">{% csrf_token %}
{{ my_form.as_p }}
<input type="submit" value="Save" class="btn btn-primary"/>
......@@ -70,6 +68,7 @@
{% block javascript %}
<script type="text/javascript" src="/static/js/chosen.min.js"></script>
<script>
$(".chosen-select").chosen()
$(".chosen-select").chosen({max_selected_options: 2,
placeholder_text_multiple: '(select up to two)'});
</script>
{% endblock javascript %}
......@@ -10,7 +10,7 @@ from django.contrib import messages
from braces.views import LoginRequiredMixin
from ratelimit.decorators import ratelimit
# imports from your apps
from accounts.models import Student, Confirmation
from accounts.models import Student, Confirmation, Major
from housing.models import Room
from .forms import (WelcomeNameForm, WelcomeMajorForm,
WelcomePrivacyForm, WelcomeSocialForm)
......@@ -175,7 +175,7 @@ class WelcomeMajor(LoginRequiredMixin, FormView):
me = Student.objects.get(user=self.request.user)
form = WelcomeMajorForm(initial={'major': me.major,
form = WelcomeMajorForm(initial={'major': me.major.all(),
'graduating_year': me.graduating_year, })
form.fields['major'].widget.attrs['class'] = 'chosen-select'
......@@ -194,7 +194,23 @@ class WelcomeMajor(LoginRequiredMixin, FormView):
me = Student.objects.get(user=self.request.user)
me.major = form.instance.major
try:
# see UpdateStudent in accounts/ for a detailed explanation
# but m2m fields are more difficult to manage than other relationships
form_major_pks = set(form.data.getlist('major')[:2])
form_majors = [Major.objects.get(pk=pk) for pk in form_major_pks]
# a student likely won't have any majors on the welcome walkthrough
# using Python's implicit evaluation (empty is False, anything is True)
if me.major.all():
for current_major in me.major.all():
if current_major not in form_majors:
me.major.remove(current_major)
for form_major in form_majors:
if form_major not in me.major.all():
me.major.add(form_major)
except:
pass
me.graduating_year = form.instance.graduating_year
me.completedMajor = True
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment