Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion FusionIIIT/Fusion/settings/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'fusionlab',
'HOST': os.environ.get("DB_HOST", default='localhost'),
'PORT': os.environ.get("DB_PORT", default='5433'),
'USER': 'fusion_admin',
'PASSWORD': 'hello123',
}
Expand All @@ -27,7 +28,9 @@
)
}

if DEBUG:
ENABLE_DEBUG_TOOLBAR = os.environ.get("ENABLE_DEBUG_TOOLBAR", "0") == "1"

if DEBUG and ENABLE_DEBUG_TOOLBAR:
MIDDLEWARE += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
Expand Down
72 changes: 72 additions & 0 deletions FusionIIIT/Fusion/settings/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Minimal test settings for running Fusion Dashboard tests
Uses PostgreSQL database (already connected in development)
"""
import os
from pathlib import Path
from Fusion.settings.common import *

# PostgreSQL test database - uses separate test database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'fusionlab_test_suite',
'HOST': os.environ.get("DB_HOST", default='localhost'),
'PORT': os.environ.get("DB_PORT", default='5433'),
'USER': 'fusion_admin',
'PASSWORD': 'hello123',
'TEST': {
'NAME': 'fusionlab_test_suite',
'CHARSET': 'UTF8',
'CREATE_DB': True,
},
}
}

# Keep standard migrations for PostgreSQL (not needed for in-memory DB)
# Tests will run migrations as part of setup

# Test security settings
SECRET_KEY = 'test-secret-key-for-testing-only-24-apr-2026'
DEBUG = True
ALLOWED_HOSTS = ['*', 'localhost', '127.0.0.1']

# Keep middleware but avoid test-only imports that are unavailable in this
# environment.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# Authentication backends for testing
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]

# Allauth settings (required)
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_USERNAME_REQUIRED = False

# Celery disabled for testing
CELERY_ALWAYS_EAGER = True
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True

# Disable email sending in tests
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

# Use minimal URL configuration to avoid importing problematic modules
ROOT_URLCONF = 'Fusion.test_urls'

# Avoid WhiteNoise manifest lookup during tests; many views resolve static URLs
# while rendering error pages or shared templates.
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'

print("Using minimal test settings (notifications app excluded)")
10 changes: 10 additions & 0 deletions FusionIIIT/Fusion/test_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""
Minimal URL configuration for Django tests
"""
from django.conf.urls import include, url

# Keep the globals namespace available so shared error pages and redirects can
# resolve reverse() calls during test execution.
urlpatterns = [
url(r'^', include('applications.globals.urls')),
]
14 changes: 0 additions & 14 deletions FusionIIIT/applications/central_mess/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,20 +203,6 @@ class Migration(migrations.Migration):
('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')),
],
),
migrations.CreateModel(
name='Payments',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('amount_paid', models.IntegerField(default=0)),
('payment_month', models.CharField(default=applications.central_mess.models.current_month, max_length=20)),
('payment_year', models.IntegerField(default=applications.central_mess.models.current_year)),
('payment_date', models.DateField(default=datetime.date(2024, 7, 16))),
('student_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='academic_information.student')),
],
options={
'unique_together': {('student_id', 'payment_date')},
},
),
migrations.CreateModel(
name='Monthly_bill',
fields=[
Expand Down
73 changes: 73 additions & 0 deletions FusionIIIT/applications/globals/api/selectors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from django.db.models import Avg

from applications.globals.models import Feedback, HoldsDesignation, Issue, ModuleAccess
from applications.placement_cell.models import (
Achievement,
Course,
Education,
Experience,
Has,
Patent,
Project,
Publication,
)


def get_designation_names(user):
designation_names = []

if str(user.extrainfo.user_type) == "student":
designation_names.append("student")

designations = HoldsDesignation.objects.select_related("designation").filter(working=user)
for designation in designations:
designation_name = str(designation.designation)
if designation_name not in designation_names:
designation_names.append(designation_name)

return designation_names


def get_accessible_modules(designation_names):
accessible_modules = {}

for designation_name in designation_names:
module_access = ModuleAccess.objects.prefetch_related('modules').filter(
designation__iexact=designation_name
).first()
if not module_access:
continue

accessible_modules[designation_name] = module_access.get_module_access_map()

return accessible_modules


def get_student_profile_querysets(student):
return {
"skills": list(
Has.objects.filter(unique_id=student)
.select_related("skill_id")
.values("skill_id__skill", "skill_rating")
),
"education": Education.objects.filter(unique_id=student),
"course": Course.objects.filter(unique_id=student),
"experience": Experience.objects.filter(unique_id=student),
"project": Project.objects.filter(unique_id=student),
"achievement": Achievement.objects.filter(unique_id=student),
"publication": Publication.objects.filter(unique_id=student),
"patent": Patent.objects.filter(unique_id=student),
"current": student.id.user.current_designation.select_related("designation").all(),
}


def get_open_issues():
return Issue.objects.get_open_issues()


def get_closed_issues():
return Issue.objects.get_closed_issues()


def get_feedback_average_rating():
return Feedback.objects.aggregate(avg_rating=Avg('rating'))['avg_rating'] or 0
123 changes: 117 additions & 6 deletions FusionIIIT/applications/globals/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from notifications.models import Notification

from applications.globals.models import (ExtraInfo, HoldsDesignation, DepartmentInfo,
Designation)
Designation, Issue, IssueImage, Feedback)

from applications.placement_cell.api.serializers import (SkillSerializer, HasSerializer,
EducationSerializer, CourseSerializer, ExperienceSerializer,
Expand All @@ -14,12 +14,12 @@

User = get_user_model()

class UserLoginSerializer(serializers.Serializer):
class LoginRequestSerializer(serializers.Serializer):
username = serializers.CharField(max_length=30, required=True)
password = serializers.CharField(required=True, write_only=True)


class AuthUserSerializer(serializers.ModelSerializer):
class AuthTokenSerializer(serializers.ModelSerializer):
auth_token = serializers.SerializerMethodField()

class Meta:
Expand All @@ -30,17 +30,54 @@ def get_auth_token(self, obj):
token, _ = Token.objects.get_or_create(user=obj)
return token.key


class ProfileSkillCreateSerializer(serializers.Serializer):
skill_name = serializers.CharField(max_length=255)
skill_rating = serializers.IntegerField(min_value=1, max_value=5)


class ProfileSubmitSerializer(serializers.Serializer):
about = serializers.CharField(required=False, allow_blank=True, max_length=1000)
age = serializers.DateField(required=True)
address = serializers.CharField(required=False, allow_blank=True, max_length=1000)
contact = serializers.IntegerField(required=True, min_value=1000000000, max_value=999999999999)


class ProfileDeleteRequestSerializer(serializers.Serializer):
deleteskill = serializers.CharField(required=False)
deleteedu = serializers.CharField(required=False)
deletecourse = serializers.CharField(required=False)
deleteexp = serializers.CharField(required=False)
deletepro = serializers.CharField(required=False)
deleteach = serializers.CharField(required=False)
deletepub = serializers.CharField(required=False)
deletepat = serializers.CharField(required=False)

def validate(self, attrs):
delete_keys = [key for key, value in attrs.items() if value not in [None, ""]]
if len(delete_keys) != 1:
raise serializers.ValidationError("Exactly one delete action key is required.")
attrs["delete_key"] = delete_keys[0]
return attrs

class NotificationSerializer(serializers.ModelSerializer):

class Meta:
model=Notification
fields=('__all__')
fields = (
'id', 'level', 'unread', 'verb', 'description', 'timestamp',
'public', 'deleted', 'emailed', 'data',
'actor_content_type', 'actor_object_id',
'target_content_type', 'target_object_id',
'action_object_content_type', 'action_object_object_id',
'recipient'
)

class DepartmentInfoSerializer(serializers.ModelSerializer):

class Meta:
model = DepartmentInfo
fields = ('__all__')
fields = ('id', 'name')

class ExtraInfoSerializer(serializers.ModelSerializer):
department = DepartmentInfoSerializer()
Expand All @@ -59,7 +96,7 @@ class DesignationSerializer(serializers.ModelSerializer):

class Meta:
model = Designation
fields = ('__all__')
fields = ('id', 'name', 'full_name', 'type')

class HoldsDesignationSerializer(serializers.ModelSerializer):
user = UserSerializer()
Expand All @@ -68,3 +105,77 @@ class HoldsDesignationSerializer(serializers.ModelSerializer):
class Meta:
model = HoldsDesignation
fields = ('user','designation','held_at')


class IssueImageSerializer(serializers.ModelSerializer):
class Meta:
model = IssueImage
fields = ("id", "image")


class IssueListSerializer(serializers.ModelSerializer):
images = IssueImageSerializer(many=True, read_only=True)
support_count = serializers.SerializerMethodField()
is_supported = serializers.SerializerMethodField()
is_owner = serializers.SerializerMethodField()
username = serializers.SerializerMethodField()

class Meta:
model = Issue
fields = (
"id",
"title",
"text",
"module",
"report_type",
"closed",
"timestamp",
"added_on",
"images",
"support_count",
"is_supported",
"is_owner",
"username",
)

def get_support_count(self, obj):
return obj.support.count()

def get_is_supported(self, obj):
user = self.context.get("request").user
return obj.support.filter(id=user.id).exists()

def get_is_owner(self, obj):
user = self.context.get("request").user
return obj.user_id == user.id

def get_username(self, obj):
return obj.user.username


class IssueCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Issue
fields = ("module", "report_type", "title", "text")


class FeedbackSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()

class Meta:
model = Feedback
fields = ("id", "rating", "feedback", "timestamp", "username")

def get_username(self, obj):
return obj.user.username


class FeedbackCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Feedback
fields = ("rating", "feedback")


# Backward-compatible aliases used by existing views.
UserLoginSerializer = LoginRequestSerializer
AuthUserSerializer = AuthTokenSerializer
Loading
Loading