diff --git a/FusionIIIT/Fusion/settings/common.py b/FusionIIIT/Fusion/settings/common.py index d9017dd40..9e3d325e4 100644 --- a/FusionIIIT/Fusion/settings/common.py +++ b/FusionIIIT/Fusion/settings/common.py @@ -51,6 +51,7 @@ # email of sender EMAIL_HOST_USER = 'fusionmailservice@iiitdmj.ac.in' +# EMAIL_HOST_PASSWORD = 'password' EMAIL_PORT = 587 ACCOUNT_EMAIL_REQUIRED = True @@ -79,8 +80,8 @@ # CELERY STUFF -# CELERY_BROKER_URL = 'redis://localhost:6379' -# CELERY_RESULT_BACKEND = 'redis://localhost:6379' +CELERY_BROKER_URL = 'redis://localhost:6379' +CELERY_RESULT_BACKEND = 'redis://localhost:6379' CELERY_ACCEPT_CONTENT = ['application/json'] CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' diff --git a/FusionIIIT/applications/department/views.py b/FusionIIIT/applications/department/views.py index 6e5a0d936..249f4875c 100644 --- a/FusionIIIT/applications/department/views.py +++ b/FusionIIIT/applications/department/views.py @@ -20,6 +20,7 @@ from .models import SpecialRequest, Announcements , Information from jsonschema import validate from jsonschema.exceptions import ValidationError +from notification.views import create_announcement def department_information(request): @@ -261,127 +262,128 @@ def staff_view(request): department_context = department_information(request) requests_received = get_to_request(usrnm) - if request.method == 'POST': - form_type = request.POST.get('form_type', '') - if form_type == 'form1' : + # if request.method == 'POST': + # form_type = request.POST.get('form_type', '') + # if form_type == 'form1' : - batch = request.POST.get('batch', '') - programme = request.POST.get('programme', '') - message = request.POST.get('announcement', '') - upload_announcement = request.FILES.get('upload_announcement') - department = request.POST.get('department') - ann_date = date.today() - user_info = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id) - getstudents = ExtraInfo.objects.select_related('user') - recipients = User.objects.filter(extrainfo__in=getstudents) - - obj1, created = Announcements.objects.get_or_create(maker_id=user_info, - batch=batch, - programme=programme, - message=message, - upload_announcement=upload_announcement, - department = department, - ann_date=ann_date) - department_notif(usrnm, recipients , message) + # batch = request.POST.get('batch', '') + # programme = request.POST.get('programme', '') + # message = request.POST.get('announcement', '') + # upload_announcement = request.FILES.get('upload_announcement') + # department = request.POST.get('department') + # ann_date = date.today() + # user_info = ExtraInfo.objects.all().select_related('user','department').get(id=ann_maker_id) + # getstudents = ExtraInfo.objects.select_related('user') + # recipients = User.objects.filter(extrainfo__in=getstudents) + + # obj1, created = Announcements.objects.get_or_create(maker_id=user_info, + # batch=batch, + # programme=programme, + # message=message, + # upload_announcement=upload_announcement, + # department = department, + # ann_date=ann_date) + # department_notif(usrnm, recipients , message) - elif form_type == 'form2' : + # elif form_type == 'form2' : - email = request.POST.get('email', '') - phone_number = request.POST.get('contact_number', '') - facilites = request.POST.get('facilities', '') - labs = request.POST.get('labs', '') - department_id = user_departmentid - - # Check if a row with the specified department_id already exists - try: - department_info = Information.objects.get(department_id=department_id) - # If row exists, update the values - department_info.email = email - department_info.phone_number_number = phone_number - department_info.facilites = facilites - department_info.labs = labs - department_info.save() - except Information.DoesNotExist: - # If row does not exist, create a new one - department_info = Information.objects.create( - department_id=department_id, - email=email, - phone_number=phone_number, - facilites=facilites, - labs=labs - ) + # email = request.POST.get('email', '') + # phone_number = request.POST.get('contact_number', '') + # facilites = request.POST.get('facilities', '') + # labs = request.POST.get('labs', '') + # department_id = user_departmentid + + # # Check if a row with the specified department_id already exists + # try: + # department_info = Information.objects.get(department_id=department_id) + # # If row exists, update the values + # department_info.email = email + # department_info.phone_number_number = phone_number + # department_info.facilites = facilites + # department_info.labs = labs + # department_info.save() + # except Information.DoesNotExist: + # # If row does not exist, create a new one + # department_info = Information.objects.create( + # department_id=department_id, + # email=email, + # phone_number=phone_number, + # facilites=facilites, + # labs=labs + # ) - context = browse_announcements() + # context = browse_announcements() - department_templates = { - 51: 'department/csedep_request.html', - 30: 'department/ecedep_request.html', - 37: 'department/medep_request.html', - 53: 'department/smdep_request.html', - - } - default_template = 'department/dep_request.html' + # department_templates = { + # 51: 'department/csedep_request.html', + # 30: 'department/ecedep_request.html', + # 37: 'department/medep_request.html', + # 53: 'department/smdep_request.html', + + # } + # default_template = 'department/dep_request.html' - desig=request.session.get('currentDesignationSelected', 'default_value') - if desig=='deptadmin_cse': - template_name = 'department/admin_cse.html' + # desig=request.session.get('currentDesignationSelected', 'default_value') + # if desig=='deptadmin_cse': + # template_name = 'department/admin_cse.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - elif desig=='deptadmin_ece': - template_name = 'department/admin_ece.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - elif desig=='deptadmin_me': - template_name = 'department/admin_me.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) - elif desig=='deptadmin_sm': - template_name = 'department/admin_sm.html' - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) + # return render(request, template_name, { + # "user_designation": user_info.user_type, + # "announcements": context, + # "request_to": requests_received, + # "fac_list": context_f, + # "department_info": department_context + # }) + # elif desig=='deptadmin_ece': + # template_name = 'department/admin_ece.html' + # return render(request, template_name, { + # "user_designation": user_info.user_type, + # "announcements": context, + # "request_to": requests_received, + # "fac_list": context_f, + # "department_info": department_context + # }) + # elif desig=='deptadmin_me': + # template_name = 'department/admin_me.html' + # return render(request, template_name, { + # "user_designation": user_info.user_type, + # "announcements": context, + # "request_to": requests_received, + # "fac_list": context_f, + # "department_info": department_context + # }) + # elif desig=='deptadmin_sm': + # template_name = 'department/admin_sm.html' + # return render(request, template_name, { + # "user_designation": user_info.user_type, + # "announcements": context, + # "request_to": requests_received, + # "fac_list": context_f, + # "department_info": department_context + # }) - # if desig == 'deptadmin_cse': - # return render(request, 'admin_cse.html') - # elif desig == 'deptadmin_ece': - # return render(request, 'admin_ece.html') - # elif desig == 'deptadmin_sm': - # return render(request, 'admin_sm.html') - # elif desig == 'deptadmin_me': - # return render(request, 'admin_me.html') - # else: - # return render(request, 'default.html') - - template_name = department_templates.get(user_departmentid, default_template) - return render(request, template_name, { - "user_designation": user_info.user_type, - "announcements": context, - "request_to": requests_received, - "fac_list": context_f, - "department_info": department_context - }) + # # if desig == 'deptadmin_cse': + # # return render(request, 'admin_cse.html') + # # elif desig == 'deptadmin_ece': + # # return render(request, 'admin_ece.html') + # # elif desig == 'deptadmin_sm': + # # return render(request, 'admin_sm.html') + # # elif desig == 'deptadmin_me': + # # return render(request, 'admin_me.html') + # # else: + # # return render(request, 'default.html') + + # template_name = department_templates.get(user_departmentid, default_template) + # return render(request, template_name, { + # "user_designation": user_info.user_type, + # "announcements": context, + # "request_to": requests_received, + # "fac_list": context_f, + # "department_info": department_context + # }) + return create_announcement(request, 'department/dep_request.html', 'Department', {"user_designation": user_info.user_type}) diff --git a/FusionIIIT/applications/globals/api/urls.py b/FusionIIIT/applications/globals/api/urls.py index 53dd23f95..f25b901b9 100644 --- a/FusionIIIT/applications/globals/api/urls.py +++ b/FusionIIIT/applications/globals/api/urls.py @@ -1,4 +1,6 @@ from django.conf.urls import url +from django.urls import path +from .views import department_info from . import views @@ -19,5 +21,9 @@ url(r'^notification/',views.notification,name='notification'), url(r'^notificationread',views.NotificationRead,name='notifications-read'), url(r'^notificationdelete',views.delete_notification,name='notifications-delete'), - url(r'^notificationunread',views.NotificationUnread,name='notifications-unread') + url(r'^notificationunread',views.NotificationUnread,name='notifications-unread'), + url(r'^search-users/', views.search_users, name='search_users'), + # url(r'^department-info/', views.department_info, name='department_info'), + path('department-info/', department_info, name='department-info'), + ] diff --git a/FusionIIIT/applications/globals/api/views.py b/FusionIIIT/applications/globals/api/views.py index 0aff2180d..953fdc268 100644 --- a/FusionIIIT/applications/globals/api/views.py +++ b/FusionIIIT/applications/globals/api/views.py @@ -14,6 +14,7 @@ from rest_framework.decorators import api_view, permission_classes,authentication_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response +from django.http import JsonResponse from . import serializers @@ -376,4 +377,31 @@ def delete_notification(request): 'error': 'Failed to mark the notification as deleted.', 'details': str(e) } - return Response(response, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file + return Response(response, status=status.HTTP_400_BAD_REQUEST) + + +def search_users(request): + query = request.GET.get('q', '') + if query: + users = ExtraInfo.objects.filter(user__username__icontains=query).values('id', 'user__username') + else: + users = ExtraInfo.objects.none() + + results = [ + {'id': user['id'], 'text': user['user__username']} for user in users + ] + return JsonResponse({'results': results}) + +@api_view(['GET']) # Declare that this view handles GET requests +@permission_classes([]) # No permissions required +@authentication_classes([]) # No authentication required +def department_info(request): + """ + Retrieve department information and return as JSON. + """ + try: + departments = DepartmentInfo.objects.all() # Fetch all department objects + serializer = serializers.DepartmentInfoSerializer(departments, many=True) # Serialize the data + return Response(serializer.data, status=status.HTTP_200_OK) # Return serialized data as JSON + except Exception as e: + return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/FusionIIIT/applications/globals/views.py b/FusionIIIT/applications/globals/views.py index dedad3d8d..f982ba075 100644 --- a/FusionIIIT/applications/globals/views.py +++ b/FusionIIIT/applications/globals/views.py @@ -30,6 +30,7 @@ from notifications.models import Notification from .models import * from applications.hostel_management.models import (HallCaretaker,HallWarden) +from notification.views import announcement_list def index(request): context = {} @@ -754,11 +755,13 @@ def dashboard(request): 'designation' : designation, 'hall_caretaker': hall_caretaker_user, 'hall_warden': hall_warden_user, + 'announcements': announcement_list(request)['announcements'] } # a=HoldsDesignation.objects.select_related('user','working','designation').filter(designation = user) print(context) print(type(user.extrainfo.user_type)) + print(announcement_list(request)) if(request.user.get_username() == 'director'): return render(request, "dashboard/director_dashboard2.html", {}) elif( "dean_rspc" in designation): diff --git a/FusionIIIT/applications/leave/tasks.py b/FusionIIIT/applications/leave/tasks.py index d29285a92..8460273bd 100644 --- a/FusionIIIT/applications/leave/tasks.py +++ b/FusionIIIT/applications/leave/tasks.py @@ -1,6 +1,6 @@ from __future__ import absolute_import, unicode_literals -import celery +from Fusion.celery import app from django.db import transaction from django.db.models import Q from django.utils import timezone @@ -8,7 +8,7 @@ from .models import LeaveMigration -@celery.task() +@app.task() @transaction.atomic def execute_leave_migrations(): today = timezone.now().date() @@ -27,6 +27,6 @@ def execute_leave_migrations(): migrations.delete() -@celery.task() +@app.task() def testing(a, b): return a+b \ No newline at end of file diff --git a/FusionIIIT/applications/notifications_extension/api/serializers.py b/FusionIIIT/applications/notifications_extension/api/serializers.py index 899c50e57..08a7f7948 100644 --- a/FusionIIIT/applications/notifications_extension/api/serializers.py +++ b/FusionIIIT/applications/notifications_extension/api/serializers.py @@ -1,6 +1,51 @@ from rest_framework import serializers from notifications.models import Notification +from notification.models import Announcements, AnnouncementRecipients +from applications.globals.models import ExtraInfo, DepartmentInfo class NotificationSerializer(serializers.ModelSerializer): class Meta: model = Notification - fields = '__all__' \ No newline at end of file + fields = '__all__' + +class AnnouncementListSerializer(serializers.ModelSerializer): + class Meta: + model = Announcements + fields = '__all__' # Specify the fields you need + + +class AnnouncementSerializer(serializers.ModelSerializer): + specific_users = serializers.ListField( + child=serializers.IntegerField(), write_only=True, required=False + ) # List of specific user IDs for specific_users target group + + class Meta: + model = Announcements + fields = ['message', 'target_group', 'department', 'batch', 'module', 'specific_users'] + + def validate(self, data): + target_group = data.get('target_group') + + # Validate 'faculty' target group: department must be provided + if target_group == 'faculty' and not data.get('department'): + raise serializers.ValidationError("Department is required for faculty announcements.") + + # Validate 'students' target group: department and batch must be provided + if target_group == 'students': + if not data.get('department'): + raise serializers.ValidationError("Department is required for student announcements.") + if not data.get('batch'): + raise serializers.ValidationError("Batch is required for student announcements.") + + return data + + def create(self, validated_data): + specific_users = validated_data.pop('specific_users', []) + announcement = Announcements.objects.create(**validated_data) + + # Handle specific_users for AnnouncementRecipients + if validated_data['target_group'] == 'specific_users': + extra_info_users = ExtraInfo.objects.filter(id__in=specific_users) + for extra_info in extra_info_users: + AnnouncementRecipients.objects.create(announcement=announcement, user=extra_info) + + return announcement \ No newline at end of file diff --git a/FusionIIIT/applications/notifications_extension/api/urls.py b/FusionIIIT/applications/notifications_extension/api/urls.py index 62238c007..c3f42cb17 100644 --- a/FusionIIIT/applications/notifications_extension/api/urls.py +++ b/FusionIIIT/applications/notifications_extension/api/urls.py @@ -24,6 +24,9 @@ MarkAsRead, Delete, NotificationsList, + AnnouncementCreateView, + AnnouncementListView, + RSPCNotificationAPIView ) urlpatterns = [ @@ -50,4 +53,7 @@ path('office_dean_RSPC_notification/', OfficeDeanRSPCNotificationAPIView.as_view(), name='office_dean_RSPC_notification'), path('research_procedures_notification/', ResearchProceduresNotificationAPIView.as_view(), name='research_procedures_notification'), path('hostel_notifications/', HostelModuleNotificationAPIView.as_view(), name='hostel_notifications'), + path('announcements/create', AnnouncementCreateView.as_view(), name='announcement_create'), + path('announcements/', AnnouncementListView.as_view(), name='announcement_list'), + path('RSPC_notif', RSPCNotificationAPIView.as_view(), name='RSPC_notification') ] diff --git a/FusionIIIT/applications/notifications_extension/api/views.py b/FusionIIIT/applications/notifications_extension/api/views.py index c5017f79f..e4c125786 100644 --- a/FusionIIIT/applications/notifications_extension/api/views.py +++ b/FusionIIIT/applications/notifications_extension/api/views.py @@ -7,7 +7,7 @@ from rest_framework.generics import ListAPIView from notifications.models import Notification from rest_framework import status -from .serializers import NotificationSerializer +from .serializers import NotificationSerializer, AnnouncementSerializer, AnnouncementListSerializer from notification.views import (leave_module_notif, placement_cell_notif, academics_module_notif, @@ -30,7 +30,9 @@ department_notif, office_module_DeanRSPC_notif, research_procedures_notif, - hostel_notifications) + hostel_notifications, + announcement_list, + RSPC_notif) @@ -368,4 +370,52 @@ class NotificationsList(ListAPIView): # queryset = Notification.objects.all(actor_object_id=) serializer_class = NotificationSerializer def get_queryset(self): - return Notification.objects.all().filter(recipient_id=self.request.user.id) \ No newline at end of file + return Notification.objects.all().filter(recipient_id=self.request.user.id) + +class AnnouncementCreateView(APIView): + def post(self, request): + # Extract module from request or default to 'Fusion' + module = request.data.get('module', 'Fusion') + request.data['module'] = module + + # Initialize serializer with the request data + serializer = AnnouncementSerializer(data=request.data) + if serializer.is_valid(): + # Save the announcement with the current user as 'created_by' + serializer.save(created_by=request.user) + return Response(serializer.data, status=status.HTTP_201_CREATED) + + # Return error response if data is invalid + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + +class AnnouncementListView(APIView): + """ + API View that reuses the announcement_list function to fetch announcements for the user + and return them via an API response. + """ + + def get(self, request): + # Call the existing announcement_list function + announcement_context = announcement_list(request) + + # Get the queryset of announcements from the context + announcements = announcement_context['announcements'] + + # Serialize the queryset + serializer = AnnouncementListSerializer(announcements, many=True) + + # Return the serialized data as JSON response + return Response(serializer.data, status=status.HTTP_200_OK) + +class RSPCNotificationAPIView(APIView): + def post(self, request, *args, **kwargs): + + sender = request.user + recipient_id = request.data.get('recipient') + type = request.data.get('type') + User = get_user_model() + recipient = User.objects.get(pk=recipient_id) + + RSPC_notif(sender,recipient, type) + + return Response({'message' : 'Notification sent successfully'}, status=status.HTTP_201_CREATED) \ No newline at end of file diff --git a/FusionIIIT/applications/programme_curriculum/views.py b/FusionIIIT/applications/programme_curriculum/views.py index 4ea51e609..e0fdb5a50 100644 --- a/FusionIIIT/applications/programme_curriculum/views.py +++ b/FusionIIIT/applications/programme_curriculum/views.py @@ -225,6 +225,7 @@ def view_a_courseslot(request, courseslot_id): elif 'hod' in request.session['currentDesignationSelected'].lower(): url+='faculty/' course_slot = get_object_or_404(CourseSlot, Q(id=courseslot_id)) + notifs = request.user.notifications.all() return render(request, url+'view_a_courseslot.html', {'course_slot': course_slot,'notifications': notifs,}) diff --git a/FusionIIIT/notification/admin.py b/FusionIIIT/notification/admin.py new file mode 100644 index 000000000..c8a0866df --- /dev/null +++ b/FusionIIIT/notification/admin.py @@ -0,0 +1,19 @@ +from django.contrib import admin +from .models import Announcements, AnnouncementRecipients + +# Customize the admin interface for Announcements +@admin.register(Announcements) +class AnnouncementsAdmin(admin.ModelAdmin): + list_display = ('created_by', 'created_at', 'message', 'target_group', 'department', 'batch') + search_fields = ('message', 'created_by__username', 'department') + list_filter = ('target_group', 'department', 'created_at') + date_hierarchy = 'created_at' + ordering = ('-created_at',) + +# Customize the admin interface for AnnouncementRecipients +@admin.register(AnnouncementRecipients) +class AnnouncementRecipientsAdmin(admin.ModelAdmin): + list_display = ('announcement', 'user') + search_fields = ('announcement__message', 'user__user__username') + list_filter = ('announcement__target_group',) + diff --git a/FusionIIIT/notification/forms.py b/FusionIIIT/notification/forms.py new file mode 100644 index 000000000..fe18ec6a9 --- /dev/null +++ b/FusionIIIT/notification/forms.py @@ -0,0 +1,32 @@ +from django import forms +from .models import Announcements, AnnouncementRecipients +from applications.globals.models import ExtraInfo +from django.contrib.auth.models import User + + +class AnnouncementForm(forms.ModelForm): + specific_users = forms.CharField( + required=False, + widget=forms.SelectMultiple(attrs={'class': 'ui fluid multiple search selection dropdown'}) + ) + + class Meta: + model = Announcements + fields = ['message', 'target_group', 'department', 'batch', 'specific_users'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def clean(self): + cleaned_data = super().clean() + target_group = cleaned_data.get('target_group') + + # Validation based on target group + if target_group == 'faculty' and not cleaned_data.get('department'): + self.add_error('department', 'Department is required for faculty announcements.') + elif target_group == 'students': + if not cleaned_data.get('department') or not cleaned_data.get('batch'): + self.add_error('department', 'Department is required for student announcements.') + self.add_error('batch', 'Batch is required for student announcements.') + + return cleaned_data \ No newline at end of file diff --git a/FusionIIIT/notification/migrations/0001_initial.py b/FusionIIIT/notification/migrations/0001_initial.py new file mode 100644 index 000000000..6b4bf8941 --- /dev/null +++ b/FusionIIIT/notification/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 3.1.5 on 2024-11-06 22:10 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('globals', '0002_auto_20241007_2302'), + ] + + operations = [ + migrations.CreateModel( + name='Announcements', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('message', models.CharField(max_length=200)), + ('target_group', models.CharField(choices=[('faculty', 'Faculty'), ('students', 'Students'), ('all', 'All Staff and Students'), ('specific_users', 'Specific Users')], max_length=20)), + ('batch', models.IntegerField(blank=True, null=True)), + ('upload_announcement', models.FileField(default=' ', null=True, upload_to='notifications/upload_announcement')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('department', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='globals.departmentinfo')), + ], + ), + migrations.CreateModel( + name='AnnouncementRecipients', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('announcement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recipients', to='notification.announcements')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='globals.extrainfo')), + ], + ), + ] diff --git a/FusionIIIT/notification/migrations/0002_announcements_module.py b/FusionIIIT/notification/migrations/0002_announcements_module.py new file mode 100644 index 000000000..43537cf35 --- /dev/null +++ b/FusionIIIT/notification/migrations/0002_announcements_module.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.5 on 2024-11-07 13:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('notification', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='announcements', + name='module', + field=models.CharField(default='Fusion', max_length=200), + ), + ] diff --git a/FusionIIIT/notification/migrations/__init__.py b/FusionIIIT/notification/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/FusionIIIT/notification/models.py b/FusionIIIT/notification/models.py index 71a836239..a6f1e697a 100644 --- a/FusionIIIT/notification/models.py +++ b/FusionIIIT/notification/models.py @@ -1,3 +1,29 @@ from django.db import models +from django.contrib.auth.models import User +from applications.globals.models import ExtraInfo, DepartmentInfo # Create your models here. +class Announcements(models.Model): + TARGET_GROUPS = [ + ('faculty', 'Faculty'), + ('students', 'Students'), + ('all', 'All Staff and Students'), + ('specific_users', 'Specific Users'), + ] + created_by = models.ForeignKey(User, on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True) + message = models.CharField(max_length=200) + target_group = models.CharField(max_length=20, choices=TARGET_GROUPS) + department = models.ForeignKey(DepartmentInfo, on_delete=models.CASCADE, null=True, blank=True) + batch = models.IntegerField(null=True, blank=True) + upload_announcement = models.FileField(upload_to='notifications/upload_announcement', null=True, default=" ") + module = models.CharField(max_length=200, default='Fusion') + def __str__(self): + return str(self.created_by.username) + +class AnnouncementRecipients(models.Model): + announcement = models.ForeignKey(Announcements, on_delete=models.CASCADE, related_name='recipients') + user = models.ForeignKey(ExtraInfo, on_delete=models.CASCADE) + + def __str__(self): + return f"{self.user.id} - {self.announcement.message}" \ No newline at end of file diff --git a/FusionIIIT/notification/views.py b/FusionIIIT/notification/views.py index ca732a7b7..5f134a9a7 100644 --- a/FusionIIIT/notification/views.py +++ b/FusionIIIT/notification/views.py @@ -1,9 +1,118 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect from requests import Response from notifications.signals import notify +from django.core.mail import EmailMessage +from django.template.loader import render_to_string +from Fusion.celery import app +from .forms import AnnouncementForm +from .models import Announcements, AnnouncementRecipients +from applications.globals.models import ExtraInfo +from applications.academic_information.models import Student +from django.contrib import messages +from django.db.models import Q +import ast # Create your views here. +def create_announcement(request, template_name='notifications/create_announcement.html', module='Module', extra_context=None): + if request.method == 'POST': + form = AnnouncementForm(request.POST) + print(form) + if form.is_valid(): + announcement = form.save(commit=False) + announcement.created_by = request.user + announcement.module = module + announcement.save() + + # If specific users are selected, create entries in AnnouncementRecipients + if form.cleaned_data['target_group'] == 'specific_users': + specific_users = form.cleaned_data['specific_users'] + print(specific_users, type(specific_users)) + # Split the input into individual user IDs and clean them + specific_user_ids = ast.literal_eval(specific_users) + print(specific_user_ids) + + # Fetch corresponding ExtraInfo objects for these user IDs + extra_info_users = ExtraInfo.objects.filter(id__in=specific_user_ids) + print(extra_info_users) + + # Create entries in AnnouncementRecipients for each valid user + for extra_info in extra_info_users: + AnnouncementRecipients.objects.create( + announcement=announcement, + user=extra_info # This links the ExtraInfo object, not User + ) + + messages.success(request, 'Announcement created successfully.') + return redirect('/') + else: + # Handle invalid form and return the errors to the template + messages.error(request, 'There were errors in the form. Please correct them and try again.') + context = {'form': form} # Pass form with errors back to the template + if extra_context: + context.update(extra_context) + return render(request, template_name, context) + else: + form = AnnouncementForm() + # print(form) + context = {'form': form} + rendered_form = render_to_string('notifications/create_announcement.html', context, request=request) + context = {'rendered_form': rendered_form} + if extra_context: + context.update(extra_context) + + return render(request, template_name, context) + + +def announcement_list(request): + user_extrainfo = ExtraInfo.objects.filter(user=request.user).first() + if user_extrainfo.user_type == 'faculty': + announcements = Announcements.objects.filter( + Q(target_group='all') | + (Q(target_group='faculty') & (Q(department=user_extrainfo.department) | Q(department__isnull=True))) + ).order_by('-created_at') + elif user_extrainfo.user_type == 'student': + student = Student.objects.filter(id=user_extrainfo).first() + announcements = Announcements.objects.filter( + Q(target_group='all') | + (Q(target_group='students') & + (Q(department=user_extrainfo.department) | Q(department__isnull=True)) & + (Q(batch=student.batch) | Q(batch__isnull=True)) + ) + ).order_by('-created_at') + else: + announcements = Announcements.objects.filter(target_group='all') + + # Include specific user announcements + specific_announcements = Announcements.objects.filter(recipients__user=user_extrainfo).order_by('-created_at') + + context = { + 'announcements': (announcements | specific_announcements).distinct().order_by('-created_at') + } + return context + +@app.task +def send_notification_email(recipient_username, recipient_email, verb, module): + print("Trying to send notif.") + + # Make sure the recipient has an email address + if recipient_email: + subject = f"New Notification from {module}" + html_content = render_to_string('notifications/email_notification.html', { + 'recipient_username': recipient_username, + 'module': module, + 'verb': verb + }) + + email = EmailMessage( + subject, + html_content, + 'akashsah2003@gmail.com', # Replace with your email + [recipient_email] + ) + email.content_subtype = 'html' + email.send() + def leave_module_notif(sender, recipient, type, date=None): url = 'leave:leave' module = 'Leave Module' @@ -35,6 +144,7 @@ def leave_module_notif(sender, recipient, type, date=None): notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) + # send_notification_email(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def placement_cell_notif(sender, recipient, type): @@ -46,6 +156,7 @@ def placement_cell_notif(sender, recipient, type): notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) + # send_notification_email(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def academics_module_notif(sender, recipient, type): @@ -57,6 +168,7 @@ def academics_module_notif(sender, recipient, type): notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) + # send_notification_email(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def office_module_notif(sender, recipient): @@ -68,6 +180,7 @@ def office_module_notif(sender, recipient): notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) + # send_notification_email(sender=sender, recipient=recipient, url=url, module=module, verb=verb) def central_mess_notif(sender, recipient, type, message=None): @@ -79,6 +192,7 @@ def central_mess_notif(sender, recipient, type, message=None): if type == 'feedback_submitted': verb = 'Your feedback has been successfully submitted.' + send_notification_email(sender=sender, recipient=recipient, url=url, module=module, verb=verb) elif type == 'menu_change_accepted': verb = 'Menu request has been approved' elif type == 'leave_request': @@ -155,6 +269,7 @@ def healthcare_center_notif(sender, recipient, type, message): elif type == 'rel_approved': verb = 'Your medical relief request has been approved' notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb, flag=flag) + # send_notification_email.delay(recipient.username, recipient.email, verb, module) def file_tracking_notif(sender, recipient, title): url = 'filetracking:inward' @@ -437,6 +552,26 @@ def office_module_DeanRSPC_notif(sender, recipient, type): notify.send(sender=sender, recipient=recipient, url=url, module=module, verb=verb) +def RSPC_notif(sender, recipient, type): + url = 'rspc' + module = 'RSPC' + sender = sender + recipient = recipient + verb = "" + + if type == "Approved": + verb = "Your request has been approved." + elif type == "Rejected": + verb = "Your request has been rejected." + elif type == "Processing": + verb = "You have a new request to process." + elif type == "Created": + verb = "Your project has been added to RSPC." + elif type == "Forwording": + verb = f"Your request has been forworded to {sender.username}. Kindly wait for decision" + + notify.send(sender=sender, recipient=recipient, + url=url, module=module, verb=verb) def research_procedures_notif(sender, recipient, type): url = 'research_procedures:patent_registration' diff --git a/FusionIIIT/templates/dashboard/alerts1.html b/FusionIIIT/templates/dashboard/alerts1.html index ae78bdebd..47e5d2d95 100644 --- a/FusionIIIT/templates/dashboard/alerts1.html +++ b/FusionIIIT/templates/dashboard/alerts1.html @@ -3,47 +3,30 @@ {% block alerts1 %} {% load notifications_tags %}
- {% for notice in notifications %} + {% for notice in announcements %} {% comment %}A single notification starts here!{% endcomment %} - {% if notice.data.flag %} - {% if notice.unread %}
- {% else %} -
- {% endif %}
- {{ notice.timesince }} ago + At {{ notice.created_at }}
- {{ notice.verb }} - {% ifnotequal notice.actor notice.recipient %} - -by {{ notice.actor.first_name }} {{ notice.actor.last_name }} - {% endifnotequal %} + {{ notice.message }} + -by {{ notice.created_by.first_name }} {{ notice.created_by.last_name }}
- {% if notice.unread %} -
-
Mark as Read
-
- {% else %} -
-
Mark as Unread
-
- {% endif %}
- {% endif %} {% comment %}A single notification ends here!{% endcomment %} {% endfor %} diff --git a/FusionIIIT/templates/department/dep_request.html b/FusionIIIT/templates/department/dep_request.html index 37d375246..89de8b732 100644 --- a/FusionIIIT/templates/department/dep_request.html +++ b/FusionIIIT/templates/department/dep_request.html @@ -89,13 +89,7 @@ {% comment %}Make announcement{% endcomment %}
- {% block make_announcement %} - {% if user_designation == "faculty" %} - {% include "department/make_announcements_fac.html" %} - {% elif user_designation == "staff" %} - {% include "department/make_announcements_staff.html" %} - {% endif %} - {% endblock %} + {{ rendered_form|safe}}
{% comment %}Make announcement{% endcomment %} diff --git a/FusionIIIT/templates/notifications/create_announcement.html b/FusionIIIT/templates/notifications/create_announcement.html new file mode 100644 index 000000000..aabf27fec --- /dev/null +++ b/FusionIIIT/templates/notifications/create_announcement.html @@ -0,0 +1,128 @@ +{% load static %} +{% block make_announcements %} +{% if form.errors %} +
+
    + {% for field in form %} + {% if field.errors %} +
  • {{ field.label }}: + {% for error in field.errors %} +

    {{ error }}

    + {% endfor %} +
  • + {% endif %} + {% endfor %} +
+
+{% endif %} + +
+
+ {% csrf_token %} +
+ Make a new Announcement: +
+
+ + {{ form.non_field_errors }} + +
+ {{ form.message.label_tag }} + {{ form.message }} +
+ +
+ {{ form.target_group.label_tag }} + {{ form.target_group }} +
+ +
+ {{ form.department.label_tag }} + {{ form.department }} +
+ +
+ {{ form.batch.label_tag }} + {{ form.batch }} +
+ + +
+ {{ form.specific_users.label_tag }} + +
+ +
+ + +
+ +
+ + + +
+
+ +
+ +
+
+
+ +{% endblock %} + +{% block javascript %} + + + + + + + +{% endblock %} diff --git a/FusionIIIT/templates/notifications/email_notification.html b/FusionIIIT/templates/notifications/email_notification.html new file mode 100644 index 000000000..4b0afd755 --- /dev/null +++ b/FusionIIIT/templates/notifications/email_notification.html @@ -0,0 +1,70 @@ + + + + + + Notification Email + + + +
+
+

Notification

+
+
+

Dear {{ recipient_username }},

+

This is a notification email from Fusion.

+

{{ verb }}

+

Best regards,
Fusion Team, {{ module }}

+
+ +
+ + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 4b97f144f..f6a4b0aa8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -55,6 +55,7 @@ python-bidi==0.4.2 python-dateutil==2.9.0 python3-openid==3.2.0 pytz==2020.5 +redis==5.1.1 reportlab==3.5.59 requests==2.25.1 requests-oauthlib==1.3.0