Skip to content

Commit ce69479

Browse files
Merge pull request #8 from Cooperation-org/fix-split-add-user
implement add teacher and student to school
2 parents 7d31ed7 + 385a398 commit ce69479

File tree

3 files changed

+181
-1
lines changed

3 files changed

+181
-1
lines changed

core/serializers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,21 @@ class Meta:
230230
fields = ['id', 'user', 'school', 'user_name', 'user_role', 'school_name', 'joined_at', 'is_active']
231231
read_only_fields = ['id', 'joined_at']
232232

233+
class SchoolAddTeacherSerializer(serializers.Serializer):
234+
"""Serializer for adding a teacher to school"""
235+
teacher_email = serializers.EmailField(required=True)
236+
teacher_role = serializers.ChoiceField(choices=TeacherProfile.TEACHER_ROLES, default="subject_teacher")
237+
assigned_classes = serializers.ListField(
238+
child=serializers.ChoiceField(choices=Class.ClassName.choices),
239+
allow_empty=True,
240+
required=False
241+
)
242+
243+
class SchoolAddStudentSerializer(serializers.Serializer):
244+
"""Serializer for adding a student to school"""
245+
student_email = serializers.EmailField(required=True)
246+
assigned_class = serializers.ChoiceField(choices=Class.ClassName.choices)
247+
233248

234249
# =============================================================================
235250
# SUBJECT & CLASS SERIALIZERS

core/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@
8888
path('schools/<uuid:pk>/members/', views.get_school_members, name='school-members'),
8989
path('schools/<uuid:pk>/projects/', views.get_school_projects, name='school-projects'),
9090
path('schools/<uuid:school_id>/add-user/', views.add_user_to_school, name='add-user-to-school'),
91+
path('schools/<uuid:school_id>/add-teacher-school/', views.add_teacher_to_school, name='add-teacher-to-school'),
92+
path('schools/<uuid:school_id>/add-student-school/', views.add_student_to_school, name='add-student-to-school'),
9193
path('classes/<uuid:class_id>/add-student/', views.add_student_to_class, name='add-student-to-class'),
9294

9395
# =================================================================

core/views.py

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from django.views.decorators.http import require_GET
1313
from django.core.mail import send_mail
1414
import random
15+
import logging
1516

1617
from rest_framework import viewsets, status, permissions, filters
1718
from rest_framework.decorators import action, api_view, permission_classes
@@ -32,7 +33,7 @@
3233
ProjectParticipant
3334
)
3435
from .serializers import (
35-
UserRegistrationSerializer, UserSerializer, UserUpdateSerializer,
36+
SchoolAddStudentSerializer, SchoolAddTeacherSerializer, UserRegistrationSerializer, UserSerializer, UserUpdateSerializer,
3637
PasswordChangeSerializer, SchoolSerializer, SchoolCreateSerializer,
3738
SchoolMembershipSerializer, SubjectSerializer, ClassSerializer,
3839
TeacherProfileSerializer, StudentProfileSerializer, ProjectSerializer,
@@ -1123,6 +1124,168 @@ def add_user_to_school(request, school_id):
11231124
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
11241125

11251126

1127+
@api_view(['POST'])
1128+
@permission_classes([CanManageSchoolMembers])
1129+
def add_student_to_school(request, school_id):
1130+
"""
1131+
Add a student to a school.
1132+
Only school admins and teachers can add students to their schools.
1133+
"""
1134+
try:
1135+
school = get_object_or_404(School, id=school_id)
1136+
1137+
# Check permission
1138+
if (school.admin != request.user or request.user.role != "teacher") and not request.user.is_staff:
1139+
return Response({'error': 'Only school admins and teachers can add students to schools'},
1140+
status=status.HTTP_403_FORBIDDEN)
1141+
1142+
serializer_class = SchoolAddStudentSerializer(data=request.data)
1143+
if not serializer_class.is_valid():
1144+
return Response(serializer_class.errors, status=status.HTTP_400_BAD_REQUEST)
1145+
1146+
data = serializer_class.validated_data
1147+
student_email = data['student_email']
1148+
assigned_class_name = data['assigned_class']
1149+
1150+
try:
1151+
user = User.objects.get(email=student_email)
1152+
if user.role and user.role != "student":
1153+
return Response({'error': 'User already has a role other than student.'},
1154+
status=status.HTTP_400_BAD_REQUEST)
1155+
elif not user.role:
1156+
user.role = "student"
1157+
user.save()
1158+
except User.DoesNotExist:
1159+
return Response({'error': 'User not found'},
1160+
status=status.HTTP_404_NOT_FOUND)
1161+
1162+
# Create school membership
1163+
membership, created = SchoolMembership.objects.get_or_create(
1164+
user=user,
1165+
school=school,
1166+
defaults={'is_active': True}
1167+
)
1168+
1169+
if not created and membership.is_active:
1170+
return Response({'error': 'User is already a member of this school'},
1171+
status=status.HTTP_400_BAD_REQUEST)
1172+
elif not created:
1173+
membership.is_active = True
1174+
membership.save()
1175+
1176+
try:
1177+
assigned_class = Class.objects.get(name=assigned_class_name, school=school)
1178+
except Class.DoesNotExist:
1179+
return Response({'error': 'Class not found in this school'}, status=status.HTTP_400_BAD_REQUEST)
1180+
1181+
# Create appropriate profile
1182+
StudentProfile.objects.get_or_create(
1183+
user=user,
1184+
school=school,
1185+
defaults={
1186+
'student_id': f"{school.name[:3].upper()}{user.id}",
1187+
'current_class': assigned_class
1188+
}
1189+
)
1190+
1191+
return Response({
1192+
'message': f'Successfully added {user.get_full_name()} as {user.role} to {school.name}',
1193+
'user_id': str(user.id),
1194+
'user_name': user.get_full_name(),
1195+
'user_role': user.role,
1196+
'school_name': school.name
1197+
}, status=status.HTTP_200_OK)
1198+
1199+
except Exception as e:
1200+
logger.error(f"Error adding student to school: {str(e)}")
1201+
return Response({'error': f'Failed to add student to school: {str(e)}'},
1202+
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
1203+
1204+
1205+
@api_view(['POST'])
1206+
@permission_classes([CanManageSchoolMembers])
1207+
def add_teacher_to_school(request, school_id):
1208+
"""
1209+
Add a teacher to a school.
1210+
Only school admins can add teachers to their schools.
1211+
"""
1212+
try:
1213+
school = get_object_or_404(School, id=school_id)
1214+
1215+
# Check permission
1216+
if school.admin != request.user and not request.user.is_staff:
1217+
return Response({'error': 'Only school admins can add teachers to schools'},
1218+
status=status.HTTP_403_FORBIDDEN)
1219+
1220+
serializer_class = SchoolAddTeacherSerializer(data=request.data)
1221+
if not serializer_class.is_valid():
1222+
return Response(serializer_class.errors, status=status.HTTP_400_BAD_REQUEST)
1223+
1224+
data = serializer_class.validated_data
1225+
teacher_email = data['teacher_email']
1226+
teacher_role = data['teacher_role']
1227+
assigned_class_names = data.get('assigned_classes')
1228+
1229+
try:
1230+
user = User.objects.get(email=teacher_email)
1231+
if user.role and user.role != "teacher":
1232+
return Response({'error': 'User already has a role other than teacher.'},
1233+
status=status.HTTP_400_BAD_REQUEST)
1234+
elif not user.role:
1235+
user.role = "teacher"
1236+
user.save()
1237+
except User.DoesNotExist:
1238+
return Response({'error': 'User not found'},
1239+
status=status.HTTP_404_NOT_FOUND)
1240+
1241+
# Create school membership
1242+
membership, created = SchoolMembership.objects.get_or_create(
1243+
user=user,
1244+
school=school,
1245+
defaults={'is_active': True}
1246+
)
1247+
1248+
if not created and membership.is_active:
1249+
return Response({'error': 'User is already a member of this school'},
1250+
status=status.HTTP_400_BAD_REQUEST)
1251+
elif not created:
1252+
membership.is_active = True
1253+
membership.save()
1254+
1255+
# Create appropriate profile
1256+
teacher_profile, _ = TeacherProfile.objects.get_or_create(
1257+
user=user,
1258+
school=school,
1259+
defaults={'teacher_role': teacher_role, 'status': 'active'}
1260+
)
1261+
# Query classes matching names and this school
1262+
valid_class_names = Class.objects.filter(name__in=assigned_class_names, school=school).values_list('name', flat=True)
1263+
1264+
# Find invalid class names
1265+
invalid_classes = set(assigned_class_names) - set(valid_class_names)
1266+
if invalid_classes:
1267+
return Response(
1268+
{'error': f'The following classes are invalid or do not belong to the school: {", ".join(invalid_classes)}'},
1269+
status=status.HTTP_400_BAD_REQUEST
1270+
)
1271+
1272+
valid_classes_qs = Class.objects.filter(name__in=assigned_class_names, school=school)
1273+
teacher_profile.assigned_classes.set(valid_classes_qs)
1274+
1275+
return Response({
1276+
'message': f'Successfully added {user.get_full_name()} as {user.role} to {school.name}',
1277+
'user_id': str(user.id),
1278+
'user_name': user.get_full_name(),
1279+
'user_role': teacher_role,
1280+
'school_name': school.name
1281+
}, status=status.HTTP_200_OK)
1282+
1283+
except Exception as e:
1284+
logger.error(f"Error adding teacher to school: {str(e)}")
1285+
return Response({'error': f'Failed to add teacher to school: {str(e)}'},
1286+
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
1287+
1288+
11261289
@api_view(['POST'])
11271290
@permission_classes([CanManageSchoolContent])
11281291
def add_student_to_class(request, class_id):

0 commit comments

Comments
 (0)