Primary Contact
Please make sure to fill out all fields.
- {this.props.renderContactGenderField('primary_gender')} + {this.props.renderContactGenderField("primary_gender")}diff --git a/.flowconfig b/.flowconfig new file mode 100644 index 000000000..a74d99bf5 --- /dev/null +++ b/.flowconfig @@ -0,0 +1,13 @@ +[ignore] + +[include] + +[libs] + +[lints] + +[options] +module.system.node.allow_root_relative=true +module.system.node.root_relative_dirname=./huxley/www/js + +[strict] diff --git a/.gitignore b/.gitignore index 5784b012a..20c890729 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,5 @@ test_position_paper.doc env/ .vscode/ + +huxley/service.json \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index f891e01cc..b864ae74a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,11 +3,11 @@ python: - "3.6" install: - pip install -r requirements.txt - - nvm install 6.4 - - nvm use 6.4 + - nvm install 12.19.0 + - nvm use 12.19.0 - npm install before_script: - python manage.py migrate script: - python manage.py test - - npm test + - npm run flow diff --git a/docs/tutorials/setup/tutorial.md b/docs/tutorials/setup/tutorial.md index 1943932dc..caba5c086 100644 --- a/docs/tutorials/setup/tutorial.md +++ b/docs/tutorials/setup/tutorial.md @@ -181,7 +181,7 @@ var TodoApp = React.createClass({ } }); -module.exports = TodoApp; +export default TodoApp; ``` Then in www.html change the line `
Hello, world!
` to `` diff --git a/huxley/api/permissions.py b/huxley/api/permissions.py index 851f9ed01..ec9657e13 100644 --- a/huxley/api/permissions.py +++ b/huxley/api/permissions.py @@ -120,7 +120,8 @@ def has_permission(self, request, view): return ( user_is_advisor(request, view, assignment.registration.school_id) or user_is_chair(request, view, assignment.committee_id) or - user_is_delegate(request, view, assignment_id, 'assignment')) + user_is_delegate(request, view, assignment_id, 'assignment') or + user_is_in_committee(request, view, assignment.committee_id)) class AssignmentListPermission(permissions.BasePermission): @@ -132,10 +133,31 @@ def has_permission(self, request, view): school_id = request.query_params.get('school_id', -1) committee_id = request.query_params.get('committee_id', -1) return (user_is_chair(request, view, committee_id) or - user_is_advisor(request, view, school_id)) + user_is_advisor(request, view, school_id) or + user_is_in_committee(request, view, committee_id)) return False +class CommitteeDetailPermission(permissions.BasePermission): + '''Accept requests to retrieve a committee from any user. + Only allow superusers and chairs to update committees.''' + + def has_permission(self, request, view): + if request.user.is_superuser: + return True + + committee_id = view.kwargs.get('pk', None) + committee = Committee.objects.get(id=committee_id) + user = request.user + method = request.method + + if method != 'GET': + return user_is_chair(request, view, + committee_id) + + return True + + class DelegateDetailPermission(permissions.BasePermission): '''Accept requests to retrieve, update, and destroy a delegate from the @@ -185,7 +207,8 @@ def has_permission(self, request, view): school_id = request.query_params.get('school_id', -1) committee_id = request.query_params.get('committee_id', -1) return (user_is_chair(request, view, committee_id) or - user_is_advisor(request, view, school_id)) + user_is_advisor(request, view, school_id) or + user_is_delegate(request, view, committee_id, 'committee')) if method == 'POST': return user_is_advisor(request, view, request.data['school']) @@ -331,6 +354,14 @@ def has_permission(self, request, view): return False +class NotePermission(permissions.BasePermission): + '''Accept requests to view Notes when user is not an advisor''' + def has_permission(self, request, view): + user = request.user + if user.is_authenticated and not user.is_advisor(): + return True + return False + def user_is_advisor(request, view, school_id): user = request.user @@ -351,7 +382,15 @@ def user_is_delegate(request, view, target_id, field=None): if not user.is_authenticated or not user.is_delegate(): return False - if field: + if field and field != 'committee': return getattr(user.delegate, field + '_id', -1) == int(target_id) + elif field == 'committee': + return getattr(getattr(user.delegate, 'assignment', -1), 'committee_id', -1) == int(target_id) return user.delegate_id == int(target_id) + +def user_is_in_committee(request, view, committee_id): + user = request.user + if not user.is_authenticated or not user.is_delegate() or not user.delegate: + return False + return user.delegate.assignment.committee_id == int(committee_id) diff --git a/huxley/api/serializers/__init__.py b/huxley/api/serializers/__init__.py index 2db53d4f6..3027f9493 100644 --- a/huxley/api/serializers/__init__.py +++ b/huxley/api/serializers/__init__.py @@ -12,3 +12,4 @@ from .position_paper import PositionPaperSerializer from .rubric import RubricSerializer from .secretariat_member import SecretariatMemberSerializer +from .note import NoteSerializer \ No newline at end of file diff --git a/huxley/api/serializers/committee.py b/huxley/api/serializers/committee.py index 1f496f091..51a136d62 100644 --- a/huxley/api/serializers/committee.py +++ b/huxley/api/serializers/committee.py @@ -17,4 +17,6 @@ class Meta: 'full_name', 'delegation_size', 'rubric', - 'special', ) + 'special', + 'zoom_link', + 'notes_activated') diff --git a/huxley/api/serializers/note.py b/huxley/api/serializers/note.py new file mode 100644 index 000000000..f88202156 --- /dev/null +++ b/huxley/api/serializers/note.py @@ -0,0 +1,16 @@ +# Copyright (c) 2011-2020 Berkeley Model United Nations. All rights reserved. +# Use of this source code is governed by a BSD License (see LICENSE). + +from rest_framework import serializers + +from huxley.core.models import Note + +class TimestampField(serializers.ReadOnlyField): + def to_representation(self, value): + return value.timestamp.timestamp() + +class NoteSerializer(serializers.ModelSerializer): + timestamp = TimestampField(source='*') + class Meta: + model = Note + fields = ('id', 'is_chair', 'sender', 'recipient', 'msg', 'timestamp') diff --git a/huxley/api/tests/test_committee.py b/huxley/api/tests/test_committee.py index fdce1aa9d..57398a134 100644 --- a/huxley/api/tests/test_committee.py +++ b/huxley/api/tests/test_committee.py @@ -1,6 +1,6 @@ # Copyright (c) 2011-2015 Berkeley Model United Nations. All rights reserved. # Use of this source code is governed by a BSD License (see LICENSE). - +from huxley.accounts.models import User from huxley.api import tests from huxley.api.tests import auto from huxley.utils.test import models @@ -22,20 +22,52 @@ class CommitteeDetailPutTestCase(tests.UpdateAPITestCase): params = {'name': 'DISC', 'special': True} def setUp(self): - self.committee = models.new_committee() + self.chair = models.new_user(username='chair', + password='chair', + user_type=User.TYPE_CHAIR) + self.committee = models.new_committee(user=self.chair) def test_anonymous_user(self): '''Unauthenticated users shouldn't be able to update committees.''' response = self.get_response(self.committee.id, params=self.params) - self.assertMethodNotAllowed(response, 'PUT') + self.assertNotAuthenticated(response) - def test_authenticated_user(self): - '''Authenticated users shouldn't be able to update committees.''' - models.new_user(username='user', password='user') - self.client.login(username='user', password='user') + def test_delegate(self): + '''Delegates shouldn't be able to update committees.''' + models.new_user(username='delegate', + password='user', + user_type=User.TYPE_DELEGATE) + self.client.login(username='delegate', password='user') + + response = self.get_response(self.committee.id, params=self.params) + self.assertPermissionDenied(response) + + def test_advisor(self): + '''Advisors shouldn't be able to update committees.''' + models.new_user(username='advisor', + password='user', + user_type=User.TYPE_ADVISOR) + self.client.login(username='advisor', password='user') + + response = self.get_response(self.committee.id, params=self.params) + self.assertPermissionDenied(response) + + def test_chair(self): + '''Chairs should be able to update committees.''' + self.client.login(username='chair', password='chair') response = self.get_response(self.committee.id, params=self.params) - self.assertMethodNotAllowed(response, 'PUT') + response.data.pop('rubric') + self.assertEqual( + response.data, { + 'id': self.committee.id, + 'name': 'DISC', + 'full_name': self.committee.full_name, + 'delegation_size': self.committee.delegation_size, + 'special': True, + 'notes_activated': self.committee.notes_activated, + 'zoom_link': self.committee.zoom_link + }) def test_superuser(self): '''Superusers shouldn't be able to update committees.''' @@ -43,7 +75,17 @@ def test_superuser(self): self.client.login(username='user', password='user') response = self.get_response(self.committee.id, params=self.params) - self.assertMethodNotAllowed(response, 'PUT') + response.data.pop('rubric') + self.assertEqual( + response.data, { + 'id': self.committee.id, + 'name': 'DISC', + 'full_name': self.committee.full_name, + 'delegation_size': self.committee.delegation_size, + 'special': True, + 'notes_activated': self.committee.notes_activated, + 'zoom_link': self.committee.zoom_link + }) class CommitteeDetailPatchTestCase(tests.PartialUpdateAPITestCase): @@ -51,20 +93,52 @@ class CommitteeDetailPatchTestCase(tests.PartialUpdateAPITestCase): params = {'name': 'DISC', 'special': True} def setUp(self): - self.committee = models.new_committee() + self.chair = models.new_user(username='chair', + password='chair', + user_type=User.TYPE_CHAIR) + self.committee = models.new_committee(user=self.chair) def test_anonymous_user(self): '''Unauthenticated users shouldn't be able to update committees.''' response = self.get_response(self.committee.id, params=self.params) - self.assertMethodNotAllowed(response, 'PATCH') + self.assertNotAuthenticated(response) - def test_authenticated_user(self): - '''Authenticated users shouldn't be able to update committees.''' - models.new_user(username='user', password='user') - self.client.login(username='user', password='user') + def test_delegate(self): + '''Delegates shouldn't be able to update committees.''' + models.new_user(username='delegate', + password='user', + user_type=User.TYPE_DELEGATE) + self.client.login(username='delegate', password='user') + + response = self.get_response(self.committee.id, params=self.params) + self.assertPermissionDenied(response) + + def test_advisor(self): + '''Advisors shouldn't be able to update committees.''' + models.new_user(username='advisor', + password='user', + user_type=User.TYPE_ADVISOR) + self.client.login(username='advisor', password='user') + + response = self.get_response(self.committee.id, params=self.params) + self.assertPermissionDenied(response) + + def test_chair(self): + '''Chairs should be able to update committees.''' + self.client.login(username='chair', password='chair') response = self.get_response(self.committee.id, params=self.params) - self.assertMethodNotAllowed(response, 'PATCH') + response.data.pop('rubric') + self.assertEqual( + response.data, { + 'id': self.committee.id, + 'name': 'DISC', + 'full_name': self.committee.full_name, + 'delegation_size': self.committee.delegation_size, + 'special': True, + 'notes_activated': self.committee.notes_activated, + 'zoom_link': self.committee.zoom_link + }) def test_superuser(self): '''Superusers shouldn't be able to update committees.''' @@ -72,7 +146,17 @@ def test_superuser(self): self.client.login(username='user', password='user') response = self.get_response(self.committee.id, params=self.params) - self.assertMethodNotAllowed(response, 'PATCH') + response.data.pop('rubric') + self.assertEqual( + response.data, { + 'id': self.committee.id, + 'name': 'DISC', + 'full_name': self.committee.full_name, + 'delegation_size': self.committee.delegation_size, + 'special': True, + 'notes_activated': self.committee.notes_activated, + 'zoom_link': self.committee.zoom_link + }) class CommitteeDetailDeleteTestCase(auto.DestroyAPIAutoTestCase): @@ -84,12 +168,12 @@ def get_test_object(cls): def test_anonymous_user(self): '''Anonymous users cannot delete committees.''' - self.do_test(expected_error=auto.EXP_DELETE_NOT_ALLOWED) + self.do_test(expected_error=auto.EXP_NOT_AUTHENTICATED) def test_authenticated_user(self): '''Authenticated users cannot delete committees.''' self.as_default_user().do_test( - expected_error=auto.EXP_DELETE_NOT_ALLOWED) + expected_error=auto.EXP_PERMISSION_DENIED) def test_superuser(self): '''Superusers cannot delete committees.''' @@ -107,24 +191,32 @@ def test_anonymous_user(self): response = self.get_response() for r in response.data: r.pop('rubric') - self.assertEqual(response.data, [ - {'delegation_size': c1.delegation_size, - 'special': c1.special, - 'id': c1.id, - 'full_name': c1.full_name, - 'name': c1.name}, {'delegation_size': c2.delegation_size, - 'special': c2.special, - 'id': c2.id, - 'full_name': c2.full_name, - 'name': c2.name} - ]) + self.assertEqual(response.data, [{ + 'delegation_size': c1.delegation_size, + 'special': c1.special, + 'id': c1.id, + 'full_name': c1.full_name, + 'name': c1.name, + 'notes_activated': c1.notes_activated, + 'zoom_link': c1.zoom_link + }, { + 'delegation_size': c2.delegation_size, + 'special': c2.special, + 'id': c2.id, + 'full_name': c2.full_name, + 'name': c2.name, + 'notes_activated': c2.notes_activated, + 'zoom_link': c2.zoom_link + }]) class CommitteeListPostTestCase(tests.CreateAPITestCase): url_name = 'api:committee_list' - params = {'name': 'DISC', - 'full_name': 'Disarmament and International Security', - 'delegation_size': 100} + params = { + 'name': 'DISC', + 'full_name': 'Disarmament and International Security', + 'delegation_size': 100 + } def test_anonymous_user(self): '''Unauthenticated users shouldn't be able to create committees.''' diff --git a/huxley/api/tests/test_delegate.py b/huxley/api/tests/test_delegate.py index a7de51885..5937ae2a4 100644 --- a/huxley/api/tests/test_delegate.py +++ b/huxley/api/tests/test_delegate.py @@ -447,7 +447,8 @@ def setUp(self): registration=self.registration, committee=self.committee) self.assignment2 = models.new_assignment( registration=self.registration) - self.delegate1 = models.new_delegate(assignment=self.assignment1, ) + self.delegate1 = models.new_delegate(assignment=self.assignment1, + user=self.delegate_user) self.delegate2 = models.new_delegate( assignment=self.assignment2, name='Trevor Dowds', @@ -492,7 +493,7 @@ def test_delegate(self): response = self.get_response( params={'committee_id': self.committee.id}) - self.assertPermissionDenied(response) + self.assert_delegates_equal(response, [self.delegate1]) def test_other_user(self): '''It rejects a request from another user.''' diff --git a/huxley/api/tests/test_registration.py b/huxley/api/tests/test_registration.py index d1217dae1..3f12607d5 100644 --- a/huxley/api/tests/test_registration.py +++ b/huxley/api/tests/test_registration.py @@ -22,6 +22,7 @@ class RegistrationListPostTest(tests.CreateAPITestCase): def test_valid(self): school = models.new_school() conference = Conference.get_current() + _, _ = models.new_country(), models.new_country() params = self.get_params() params['school'] = school.id params['conference'] = conference.session @@ -85,6 +86,7 @@ def test_fees(self): '''Fees should be read-only fields.''' school = models.new_school() conference = Conference.get_current() + _, _, _ = models.new_country(), models.new_country(), models.new_country() params = self.get_params( school=school.id, conference=conference.session, diff --git a/huxley/api/tests/test_user.py b/huxley/api/tests/test_user.py index 9d24c58a4..e22d11434 100644 --- a/huxley/api/tests/test_user.py +++ b/huxley/api/tests/test_user.py @@ -96,11 +96,12 @@ def test_self(self): def test_chair(self): '''It should have the correct fields for chairs.''' + committee = models.new_committee() user = models.new_user( username='testuser', password='test', user_type=User.TYPE_CHAIR, - committee_id=4) + committee_id=committee.id) self.client.login(username='testuser', password='test') response = self.get_response(user.id) diff --git a/huxley/api/urls.py b/huxley/api/urls.py index 9f647ea1b..5562a7238 100644 --- a/huxley/api/urls.py +++ b/huxley/api/urls.py @@ -89,6 +89,12 @@ url(r'^secretariat_member_committee/?$', views.secretariat_member.SecretariatMemberCommitteeList.as_view(), name='secretariat_member_committee_list'), + url(r'^notes/?$', + views.note.NoteList.as_view(), + name='note_list'), + url(r'^note/?$', + views.note.NoteDetail.as_view(), + name='note_detail') ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/huxley/api/views/__init__.py b/huxley/api/views/__init__.py index b3397bd75..d5ea180fe 100644 --- a/huxley/api/views/__init__.py +++ b/huxley/api/views/__init__.py @@ -1,4 +1,4 @@ # Copyright (c) 2011-2015 Berkeley Model United Nations. All rights reserved. # Use of this source code is governed by a BSD License (see LICENSE). -from huxley.api.views import assignment, committee, committee_feedback, country, delegate, school, user, position_paper, registration, register, rubric, secretariat_member +from huxley.api.views import assignment, committee, committee_feedback, country, delegate, school, user, position_paper, registration, register, rubric, secretariat_member, note diff --git a/huxley/api/views/committee.py b/huxley/api/views/committee.py index e863c2bbe..d281c0ba7 100644 --- a/huxley/api/views/committee.py +++ b/huxley/api/views/committee.py @@ -4,6 +4,7 @@ from rest_framework import generics from rest_framework.authentication import SessionAuthentication +from huxley.api import permissions from huxley.api.serializers import CommitteeSerializer from huxley.core.models import Committee @@ -14,7 +15,14 @@ class CommitteeList(generics.ListAPIView): serializer_class = CommitteeSerializer -class CommitteeDetail(generics.RetrieveAPIView): +class CommitteeDetail(generics.RetrieveUpdateAPIView): authentication_classes = (SessionAuthentication,) queryset = Committee.objects.all() + permission_classes = (permissions.CommitteeDetailPermission, ) serializer_class = CommitteeSerializer + + def patch(self, request, *args, **kwargs): + return super().patch(request, args, kwargs) + + def put(self, request, *args, **kwargs): + return self.partial_update(request, *args, **kwargs) diff --git a/huxley/api/views/note.py b/huxley/api/views/note.py new file mode 100644 index 000000000..cf0cc7608 --- /dev/null +++ b/huxley/api/views/note.py @@ -0,0 +1,66 @@ +# Copyright (c) 2011-2021 Berkeley Model United Nations. All rights reserved. +# Use of this source code is governed by a BSD License (see LICENSE). +import datetime + +from rest_framework import generics, status +from rest_framework.authentication import SessionAuthentication +from rest_framework.exceptions import PermissionDenied +from rest_framework.response import Response + +from huxley.api.mixins import ListUpdateModelMixin +from huxley.api import permissions +from huxley.api.serializers import NoteSerializer +from huxley.core.models import Assignment, Conference, Note + + +class NoteList(generics.ListCreateAPIView): + authentication_classes = (SessionAuthentication, ) + permission_classes = (permissions.NotePermission, ) + serializer_class = NoteSerializer + + def get_queryset(self): + queryset = Note.objects.all() + query_params = self.request.GET + sender_id = query_params.get('sender_id', None) + timestamp = query_params.get('timestamp', None) + committee_id = query_params.get('committee_id', None) + + if not timestamp: + return Note.objects.none() + + # Divide by 1000 because fromtimestamp takes in value in seconds + timestamp_date = datetime.datetime.fromtimestamp( + int(timestamp) / 1000.0) + + if committee_id and timestamp: + queryset = queryset.filter( + sender__committee_id=committee_id).filter( + timestamp__gte=timestamp_date) | queryset.filter( + recipient__committee_id=committee_id).filter( + timestamp__gte=timestamp_date) + + if sender_id and timestamp: + queryset = queryset.filter(sender_id=sender_id).filter( + timestamp__gte=timestamp_date) | queryset.filter( + recipient_id=sender_id).filter( + timestamp__gte=timestamp_date) + + return queryset + + +class NoteDetail(generics.CreateAPIView, generics.RetrieveAPIView): + authentication_classes = (SessionAuthentication, ) + queryset = Note.objects.all() + permission_classes = (permissions.NotePermission, ) + serializer_class = NoteSerializer + + def post(self, request, *args, **kwargs): + conference = Conference.get_current() + if not conference.notes_enabled: + return Response({'reason': 'Notes for this conference are currently off'}, status=status.HTTP_403_FORBIDDEN) + if request.data['sender'] and request.data['recipient']: + sender = Assignment.objects.get(id=request.data['sender']) + committee = sender.committee + if not committee.notes_activated: + return Response({'reason': 'The chair has disabled notes for this committee'}, status=status.HTTP_403_FORBIDDEN) + return super().post(request, args, kwargs) \ No newline at end of file diff --git a/huxley/core/admin/__init__.py b/huxley/core/admin/__init__.py index c8fbea61e..1fa53a005 100644 --- a/huxley/core/admin/__init__.py +++ b/huxley/core/admin/__init__.py @@ -27,3 +27,4 @@ admin.site.register(Rubric) admin.site.register(PositionPaper, PositionPaperAdmin) admin.site.register(SecretariatMember, SecretariatMemberAdmin) +admin.site.register(Note) \ No newline at end of file diff --git a/huxley/core/admin/assignment.py b/huxley/core/admin/assignment.py index f00f1b203..ba9556861 100644 --- a/huxley/core/admin/assignment.py +++ b/huxley/core/admin/assignment.py @@ -1,53 +1,57 @@ -# Copyright (c) 2011-2015 Berkeley Model United Nations. All rights reserved. +# Copyright (c) 2011-2021 Berkeley Model United Nations. All rights reserved. # Use of this source code is governed by a BSD License (see LICENSE). import csv +from django.conf import settings from django.conf.urls import url from django.urls import reverse from django.contrib import admin, messages from django.http import HttpResponse, HttpResponseRedirect from django.utils import html +from googleapiclient.discovery import build +from google.oauth2 import service_account + from huxley.core.models import Assignment, Committee, Country, School, PositionPaper class AssignmentAdmin(admin.ModelAdmin): - search_fields = ( - 'country__name', - 'registration__school__name', - 'committee__name', - 'committee__full_name' - ) + search_fields = ('country__name', 'registration__school__name', + 'committee__name', 'committee__full_name') + + def get_rows(self): + rows = [] + rows.append(['School', 'Committee', 'Country', 'Rejected']) + + for assignment in Assignment.objects.all().order_by( + 'registration__school__name', 'committee__name'): + rows.append([ + str(item) for item in [ + assignment.registration.school, assignment.committee, + assignment.country, assignment.rejected + ] + ]) + + return rows def list(self, request): '''Return a CSV file containing the current country assignments.''' assignments = HttpResponse(content_type='text/csv') - assignments['Content-Disposition'] = 'attachment; filename="assignments.csv"' + assignments[ + 'Content-Disposition'] = 'attachment; filename="assignments.csv"' writer = csv.writer(assignments) - writer.writerow([ - 'School', - 'Committee', - 'Country', - 'Rejected' - ]) - - for assignment in Assignment.objects.all().order_by('registration__school__name', - 'committee__name'): - writer.writerow([ - assignment.registration.school, - assignment.committee, - assignment.country, - assignment.rejected - ]) + for row in self.get_rows(): + writer.writerow(row) return assignments def load(self, request): '''Loads new Assignments.''' assignments = request.FILES - reader = csv.reader(assignments['csv'].read().decode('utf-8').splitlines()) + reader = csv.reader( + assignments['csv'].read().decode('utf-8').splitlines()) def get_model(model, name, cache): name = name.strip() @@ -67,52 +71,91 @@ def generate_assignments(reader): if len(row) == 0: continue - if (row[0]=='School' and row[1]=='Committee' and row[2]=='Country'): - continue # skip the first row if it is a header - + if (row[0] == 'School' and row[1] == 'Committee' + and row[2] == 'Country'): + continue # skip the first row if it is a header + while len(row) < 3: - row.append("") # extend the row to have the minimum proper num of columns + row.append( + "" + ) # extend the row to have the minimum proper num of columns if len(row) < 4: - rejected = False # allow for the rejected field to be null + rejected = False # allow for the rejected field to be null else: - rejected = (row[3].lower() == 'true') # use the provided value if admin provides it + rejected = ( + row[3].lower() == 'true' + ) # use the provided value if admin provides it committee = get_model(Committee, row[1], committees) country = get_model(Country, row[2], countries) school = get_model(School, row[0], schools) yield (committee, country, school, rejected) - - failed_rows = Assignment.update_assignments(generate_assignments(reader)) + failed_rows = Assignment.update_assignments( + generate_assignments(reader)) if failed_rows: # Format the message with HTML to put each failed assignment on a new line - messages.error(request, - html.format_html('Assignment upload aborted. These assignments failed:| Committee | Country | Delegation Size | -{finalized ? 'Delegate' : 'Delete Assignments'} | -{finalized ? 'Delegate' : ''} | +{finalized ? "Delegate" : "Delete Assignments"} | +{finalized ? "Delegate" : ""} | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {committees[assignment.committee].name} | -{countries[assignment.country].name} | -{committees[assignment.committee].delegation_size} | +||||||||||||
| {committees[assignment.committee] ? committees[assignment.committee].name : ''} | +{countries[assignment.country] ? countries[assignment.country].name : ''} | +{committees[assignment.committee] ? committees[assignment.committee].delegation_size : ''} | {finalized ? ( this.renderDelegateDropdown(assignment, 0) @@ -187,7 +185,8 @@ var AdvisorAssignmentsView = React.createClass({ )} @@ -202,9 +201,9 @@ var AdvisorAssignmentsView = React.createClass({ |
| Committee | @@ -133,20 +128,20 @@ var AdvisorFeedbackView = React.createClass({ ); } - }, + } - renderAssignmentRows: function() { + renderAssignmentRows = () => { var assignments = this.state.assignments; var committees = this.state.committees; var countries = this.state.countries; var feedback = this.state.feedback; - return assignments.map(assignment => { + return assignments.map((assignment) => { var delegates = feedback[assignment.id]; - if (delegates == null) { + if (delegates == null || committees[assignment.committee] === undefined || countries[assignment.country] === undefined) { return; } return ( -||||||
|---|---|---|---|---|---|---|
| {committees[assignment.committee].name} | {countries[assignment.country].name} | @@ -184,7 +179,7 @@ var AdvisorFeedbackView = React.createClass({ | @@ -192,7 +187,7 @@ var AdvisorFeedbackView = React.createClass({ | |||
| Rubric | @@ -186,11 +175,12 @@ var AdvisorPaperView = React.createClass({ ) : null; return ( -
| Assignment | @@ -205,31 +195,33 @@ var AdvisorPaperView = React.createClass({Total |
|---|
| Delegate | @@ -162,30 +156,32 @@ var AdvisorRosterView = React.createClass({
|---|
|
+ |
+
+ {this.state.recipient ? (
+ |
+
|
|
|
@@ -295,15 +298,15 @@ const ChairRubricView = React.createClass({
|
|
@@ -312,15 +315,15 @@ const ChairRubricView = React.createClass({
|
|
@@ -329,15 +332,15 @@ const ChairRubricView = React.createClass({
|
|
@@ -346,15 +349,15 @@ const ChairRubricView = React.createClass({
|
|
@@ -362,11 +365,11 @@ const ChairRubricView = React.createClass({
| Assignment | -Summary | +Summary |
|---|
| - {this.props.countryName} - | +{this.props.countryName} | @@ -49,7 +41,7 @@ var DelegationAttendanceRow = React.createClass({ className="choice" type="checkbox" checked={this.props.attendance.session_one} - onChange={this._handleChange.bind(this, 'session_one')} + onChange={this._handleChange.bind(this, "session_one")} /> @@ -59,7 +51,7 @@ var DelegationAttendanceRow = React.createClass({ className="choice" type="checkbox" checked={this.props.attendance.session_two} - onChange={this._handleChange.bind(this, 'session_two')} + onChange={this._handleChange.bind(this, "session_two")} /> @@ -69,7 +61,7 @@ var DelegationAttendanceRow = React.createClass({ className="choice" type="checkbox" checked={this.props.attendance.session_three} - onChange={this._handleChange.bind(this, 'session_three')} + onChange={this._handleChange.bind(this, "session_three")} /> @@ -79,18 +71,25 @@ var DelegationAttendanceRow = React.createClass({ className="choice" type="checkbox" checked={this.props.attendance.session_four} - onChange={this._handleChange.bind(this, 'session_four')} + onChange={this._handleChange.bind(this, "session_four")} /> |
Please choose 10 United Nations Member States or Observers your school would like to represent. A reference list of countries and their - relation to committees is available{' '} + relation to committees is available{" "} online - . Please diversify your selection. + + . Please diversify your selection.
Please make sure to fill out all fields.
- {this.props.renderContactGenderField('primary_gender')} + {this.props.renderContactGenderField("primary_gender")}- Tentative Total Number of Delegates:{' '} + Tentative Total Number of Delegates:{" "} {this._handleDelegateSum( - accessProgram('num_beginner_delegates'), - accessProgram('num_intermediate_delegates'), - accessProgram('num_advanced_delegates'), + accessProgram("num_beginner_delegates"), + accessProgram("num_intermediate_delegates"), + accessProgram("num_advanced_delegates") )}
Please make sure to fill out all fields.
- {this.props.renderContactGenderField('secondary_gender')} + {this.props.renderContactGenderField("secondary_gender")}
Comments
@@ -35,7 +30,12 @@ const RegistrationComments = React.createClass({ />