├── mylms
├── __init__.py
├── lmsstatic
│ └── assets
│ │ ├── img
│ │ └── header.jpg
│ │ └── js
│ │ ├── bs-init.js
│ │ └── creative.js
├── __pycache__
│ └── custom_azure.cpython-39.pyc
├── asgi.py
├── wsgi.py
├── views.py
├── custom_azure.py
├── templates
│ ├── 403.html
│ └── mylms
│ │ └── home.html
├── urls.py
└── settings.py
├── accounts
├── __init__.py
├── tests.py
├── static
│ └── accounts
│ │ ├── assets
│ │ ├── img
│ │ │ └── header.jpg
│ │ └── js
│ │ │ ├── bs-init.js
│ │ │ └── creative.js
│ │ └── js
│ │ └── theme.js
├── apps.py
├── passtests.py
├── templates
│ └── accounts
│ │ ├── index.html
│ │ ├── student_stream.html
│ │ ├── teacher_stream.html
│ │ ├── teacher_signup.html
│ │ ├── login.html
│ │ ├── base.html
│ │ └── student_signup.html
├── admin.py
├── urls.py
├── views.py
├── forms.py
└── models.py
├── students
├── __init__.py
├── models.py
├── admin.py
├── tests.py
├── static
│ └── students
│ │ ├── img
│ │ └── avatars
│ │ │ └── avatar5.jpeg
│ │ ├── fonts
│ │ └── fontawesome5-overrides.min.css
│ │ └── js
│ │ └── theme.js
├── apps.py
├── forms.py
├── templates
│ └── students
│ │ ├── deletesubmission.html
│ │ ├── classroom_detail.html
│ │ ├── resources.html
│ │ ├── dashboard.html
│ │ ├── assignments.html
│ │ ├── assignmentsubmission.html
│ │ ├── join_classroom.html
│ │ ├── my_assignment.html
│ │ └── base.html
├── urls.py
└── views.py
├── teachers
├── __init__.py
├── models.py
├── admin.py
├── tests.py
├── apps.py
├── static
│ └── teachers
│ │ ├── img
│ │ └── avatars
│ │ │ └── 2022-04-21_15-55-27-569_lQA2GHZJMk.png
│ │ ├── fonts
│ │ └── fontawesome5-overrides.min.css
│ │ └── js
│ │ └── theme.js
├── templates
│ └── teachers
│ │ ├── add_resource.html
│ │ ├── add_section.html
│ │ ├── add_assignment.html
│ │ ├── addclassroom.html
│ │ ├── delete_section.html
│ │ ├── delete_resource.html
│ │ ├── deleteclassroom.html
│ │ ├── delete_assignment.html
│ │ ├── classroomstudents.html
│ │ ├── dashboard.html
│ │ ├── submissions.html
│ │ ├── classroom_detail.html
│ │ ├── resources.html
│ │ ├── assignments.html
│ │ └── base.html
├── forms.py
├── urls.py
└── views.py
├── cc_erd.png
├── db.sqlite3
├── Final CCL Journal.pdf
├── screenshots
└── 2022-04-22_11-58-18-957_hwgAvOaf6J.png
├── manage.py
├── requirements.txt
├── .github
└── workflows
│ └── master_lms-app.yml
├── README.md
└── .gitignore
/mylms/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/accounts/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/students/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/teachers/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/students/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/teachers/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/accounts/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/students/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/students/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/teachers/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/teachers/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/cc_erd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/cc_erd.png
--------------------------------------------------------------------------------
/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/db.sqlite3
--------------------------------------------------------------------------------
/Final CCL Journal.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/Final CCL Journal.pdf
--------------------------------------------------------------------------------
/mylms/lmsstatic/assets/img/header.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/mylms/lmsstatic/assets/img/header.jpg
--------------------------------------------------------------------------------
/accounts/static/accounts/assets/img/header.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/accounts/static/accounts/assets/img/header.jpg
--------------------------------------------------------------------------------
/mylms/__pycache__/custom_azure.cpython-39.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/mylms/__pycache__/custom_azure.cpython-39.pyc
--------------------------------------------------------------------------------
/students/static/students/img/avatars/avatar5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/students/static/students/img/avatars/avatar5.jpeg
--------------------------------------------------------------------------------
/screenshots/2022-04-22_11-58-18-957_hwgAvOaf6J.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/screenshots/2022-04-22_11-58-18-957_hwgAvOaf6J.png
--------------------------------------------------------------------------------
/accounts/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class AccountsConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'accounts'
7 |
--------------------------------------------------------------------------------
/students/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class StudentsConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'students'
7 |
--------------------------------------------------------------------------------
/teachers/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class TeachersConfig(AppConfig):
5 | default_auto_field = 'django.db.models.BigAutoField'
6 | name = 'teachers'
7 |
--------------------------------------------------------------------------------
/teachers/static/teachers/img/avatars/2022-04-21_15-55-27-569_lQA2GHZJMk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishimule/django-learning-management-system/HEAD/teachers/static/teachers/img/avatars/2022-04-21_15-55-27-569_lQA2GHZJMk.png
--------------------------------------------------------------------------------
/mylms/lmsstatic/assets/js/bs-init.js:
--------------------------------------------------------------------------------
1 |
2 | if (window.innerWidth < 768) {
3 | [].slice.call(document.querySelectorAll('[data-bss-disabled-mobile]')).forEach(function (elem) {
4 | elem.classList.remove('animated');
5 | elem.removeAttribute('data-bss-hover-animate');
6 | elem.removeAttribute('data-aos');
7 | });
8 | }
9 |
10 | document.addEventListener('DOMContentLoaded', function() {
11 | AOS.init();
12 | }, false);
--------------------------------------------------------------------------------
/accounts/static/accounts/assets/js/bs-init.js:
--------------------------------------------------------------------------------
1 |
2 | if (window.innerWidth < 768) {
3 | [].slice.call(document.querySelectorAll('[data-bss-disabled-mobile]')).forEach(function (elem) {
4 | elem.classList.remove('animated');
5 | elem.removeAttribute('data-bss-hover-animate');
6 | elem.removeAttribute('data-aos');
7 | });
8 | }
9 |
10 | document.addEventListener('DOMContentLoaded', function() {
11 | AOS.init();
12 | }, false);
--------------------------------------------------------------------------------
/students/forms.py:
--------------------------------------------------------------------------------
1 | from accounts.models import Classroom,Teacher, Section, Resource,Assignment,AssignmentSubmission
2 | from django import forms
3 |
4 | class AssignmentSubmissionForm(forms.ModelForm):
5 |
6 | class Meta:
7 | model = AssignmentSubmission
8 | fields = ("file",)
9 |
10 | class JoinClassroomForm(forms.Form):
11 | code = forms.CharField(max_length=50, required=True)
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/mylms/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for mylms project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mylms.settings')
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/mylms/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for mylms project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mylms.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/teachers/templates/teachers/add_resource.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% load bootstrap4 %} {% block title %}Add Resource{% endblock title %} {% bootstrap_messages %} {% block content %}
2 |
3 |
Add Resource
4 |
5 |
11 |
12 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/add_section.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% load bootstrap4 %} {% block title %}Add Section{% endblock title %} {% bootstrap_messages %} {% block content %}
2 |
3 | Add Section
4 |
5 |
11 |
12 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/add_assignment.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% load bootstrap4 %} {% block title %}Add Assignment{% endblock title %} {% bootstrap_messages %} {% block content %}
2 |
3 | Add Assignment
4 |
5 |
11 |
12 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/addclassroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% load bootstrap4 %} {% block title %}Add Classroom{% endblock title %} {% bootstrap_messages %} {% block content %}
2 |
3 | Add Classroom
4 |
5 |
11 |
12 | {% endblock content %}
--------------------------------------------------------------------------------
/accounts/passtests.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.mixins import UserPassesTestMixin
2 |
3 | class StudentTestMixin(UserPassesTestMixin):
4 | def test_func(self):
5 | if self.request.user.is_authenticated:
6 | return self.request.user.is_student
7 | else:
8 | return False
9 |
10 | class TeacherTestMixin(UserPassesTestMixin):
11 | def test_func(self):
12 | if self.request.user.is_authenticated:
13 | return self.request.user.is_teacher
14 | else:
15 | return False
--------------------------------------------------------------------------------
/students/templates/students/deletesubmission.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% load static %}
5 |
6 |
7 | {% block title %}Resources{% endblock title %}
8 |
9 |
10 | {% block content %}
11 |
12 |
13 |
14 |
18 |
19 |
20 | {% endblock content %}
--------------------------------------------------------------------------------
/mylms/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import redirect, render
2 | from django.views.generic import TemplateView, CreateView, ListView
3 | from django.contrib.auth.decorators import login_required, user_passes_test
4 |
5 |
6 | class HomeView(TemplateView):
7 | template_name = "mylms/home.html"
8 |
9 | @login_required
10 | def DashboardView(request):
11 | if request.user.is_student:
12 | return redirect('students:dashboard')
13 | if request.user.is_teacher:
14 | return redirect('teachers:dashboard')
15 | if request.user.is_superuser:
16 | return redirect('admin:index')
--------------------------------------------------------------------------------
/students/static/students/fonts/fontawesome5-overrides.min.css:
--------------------------------------------------------------------------------
1 | .fab.fa-bitcoin:before{content:"\f379"}.far.fa-calendar:before,.fas.fa-calendar:before{content:"\f133"}.far.fa-clipboard:before,.fas.fa-clipboard:before{content:"\f328"}.fab.fa-facebook-f:before{content:"\f39e"}.fab.fa-google-plus:before{content:"\f2b3"}.fas.fa-hotel:before{content:"\f594"}.fab.fa-linkedin:before{content:"\f08c"}.fas.fa-reply:before{content:"\f3e5"}.fas.fa-thermometer:before{content:"\f491"}.fab.fa-vimeo:before{content:"\f40a"}.far.fa-window-close:before,.fas.fa-window-close:before{content:"\f410"}.fab.fa-youtube-square:before{content:"\f431"}
--------------------------------------------------------------------------------
/teachers/static/teachers/fonts/fontawesome5-overrides.min.css:
--------------------------------------------------------------------------------
1 | .fab.fa-bitcoin:before{content:"\f379"}.far.fa-calendar:before,.fas.fa-calendar:before{content:"\f133"}.far.fa-clipboard:before,.fas.fa-clipboard:before{content:"\f328"}.fab.fa-facebook-f:before{content:"\f39e"}.fab.fa-google-plus:before{content:"\f2b3"}.fas.fa-hotel:before{content:"\f594"}.fab.fa-linkedin:before{content:"\f08c"}.fas.fa-reply:before{content:"\f3e5"}.fas.fa-thermometer:before{content:"\f491"}.fab.fa-vimeo:before{content:"\f40a"}.far.fa-window-close:before,.fas.fa-window-close:before{content:"\f410"}.fab.fa-youtube-square:before{content:"\f431"}
--------------------------------------------------------------------------------
/teachers/templates/teachers/delete_section.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Delete Section{% endblock title %} {% block content %}
2 |
3 |
4 |
Confirm Delete?
5 |
This action is irreversible
6 |
7 |
Do you want to Delete this Section
8 |
12 |
13 |
14 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/delete_resource.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Delete Resource{% endblock title %} {% block content %}
2 |
3 |
4 |
Confirm Delete?
5 |
This action is irreversible
6 |
7 |
Do you want to Delete this Resource
8 |
12 |
13 |
14 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/deleteclassroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Delete Classroom{% endblock title %} {% block content %}
2 |
3 |
4 |
Confirm Delete?
5 |
This action is irreversible
6 |
7 |
Do you want to Delete this Classroom
8 |
12 |
13 |
14 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/delete_assignment.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Delete Assignment{% endblock title %} {% block content %}
2 |
3 |
4 |
Confirm Delete?
5 |
This action is irreversible
6 |
7 |
Do you want to Delete this Assignment
8 |
12 |
13 |
14 | {% endblock content %}
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mylms.settings')
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == '__main__':
22 | main()
23 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'accounts/base.html' %} {% block title %}Sign Up{% endblock title %} {% block content %}
2 |
3 |
4 |
5 |
Sign Up!
6 |
7 |
Create a account!
8 |
9 |
10 |
11 |
Choose your role.
12 |
13 |
14 | I'm a Student!
15 |
16 | I'm a Teacher!
17 |
18 |
19 |
20 |
21 | {% endblock content %}
--------------------------------------------------------------------------------
/accounts/templates/accounts/student_stream.html:
--------------------------------------------------------------------------------
1 | {% extends 'accounts/base.html' %}
2 |
3 |
4 | {% block title %}Sign Up{% endblock title %}
5 |
6 |
7 | {% block content %}
8 |
9 |
10 |
Select Your Stream
11 |
12 |
13 |
26 |
27 |
28 |
29 |
30 |
31 | {% endblock content %}
--------------------------------------------------------------------------------
/accounts/templates/accounts/teacher_stream.html:
--------------------------------------------------------------------------------
1 | {% extends 'accounts/base.html' %}
2 |
3 |
4 | {% block title %}Sign Up{% endblock title %}
5 |
6 |
7 | {% block content %}
8 |
9 |
10 |
Select Your Stream
11 |
12 |
13 |
26 |
27 |
28 |
29 |
30 |
31 | {% endblock content %}
--------------------------------------------------------------------------------
/mylms/custom_azure.py:
--------------------------------------------------------------------------------
1 | from storages.backends.azure_storage import AzureStorage
2 | from django.conf import settings
3 |
4 | class AzureMediaStorage(AzureStorage):
5 | account_name = settings.AZURE_ACCOUNT_NAME # Must be replaced by your
6 | account_key = settings.AZURE_STORAGE_ACCOUNT_KEY # Must be replaced by your
7 | azure_container = 'media'
8 | expiration_secs = None
9 | # file_overwrite = False
10 |
11 | class AzureStaticStorage(AzureStorage):
12 | account_name = settings.AZURE_ACCOUNT_NAME # Must be replaced by your storage_account_name
13 | account_key = settings.AZURE_STORAGE_ACCOUNT_KEY # Must be replaced by your
14 | azure_container = 'static'
15 | expiration_secs = None
16 | # file_overwrite = False
--------------------------------------------------------------------------------
/accounts/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from .models import (
4 | User,
5 | Stream,
6 | Class,
7 | Teacher,
8 | Classroom,
9 | Student,
10 | Section,
11 | Resource,
12 | Assignment,
13 | AssignmentSubmission
14 | )
15 | # Register your models here.
16 |
17 | admin.site.register(User)
18 | admin.site.register(Stream)
19 | admin.site.register(Class)
20 | admin.site.register(Teacher)
21 | admin.site.register(Classroom)
22 | admin.site.register(Student)
23 | admin.site.register(Section)
24 | admin.site.register(Resource)
25 | admin.site.register(Assignment)
26 | admin.site.register(AssignmentSubmission)
27 |
28 |
--------------------------------------------------------------------------------
/teachers/forms.py:
--------------------------------------------------------------------------------
1 | from accounts.models import Classroom,Teacher, Section, Resource,Assignment,AssignmentSubmission
2 | from django import forms
3 |
4 | class ClassroomForm(forms.ModelForm):
5 |
6 | class Meta:
7 | model = Classroom
8 | exclude = ('teacher',)
9 |
10 | class SectionForm(forms.ModelForm):
11 |
12 | class Meta:
13 | model = Section
14 | # fields = '__all__'
15 | exclude = ("classroom",)
16 |
17 | class ResourceForm(forms.ModelForm):
18 |
19 | class Meta:
20 | model = Resource
21 | # fields = '__all__'
22 | exclude = ("section",)
23 |
24 | class AssignmentForm(forms.ModelForm):
25 |
26 | class Meta:
27 | model = Assignment
28 | exclude = ("section",)
29 | widgets = {
30 | 'deadline' : forms.SelectDateWidget
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/mylms/templates/403.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Untitled
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
403 Forbidden
18 |
ERROR
19 |
You are not Authorized to view this page.
Home
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/accounts/urls.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.urls import include, path
3 | from django.conf import settings
4 | from django.conf.urls.static import static
5 | from .views import IndexView, StudentCreateView, StreamView, TeacherCreateView
6 | from django.contrib.auth.views import LoginView, LogoutView
7 |
8 | app_name = 'accounts'
9 |
10 | urlpatterns = [
11 | path('', IndexView.as_view(), name='signup'),
12 | path('login/', LoginView.as_view(template_name='accounts/login.html',redirect_authenticated_user=True), name='login'),
13 | path('logout/', LogoutView.as_view(), name='logout'),
14 |
15 | path('signup/student/stream/', StreamView.as_view(template_name = "accounts/student_stream.html"), name='student_stream'),
16 | path('signup/student//', StudentCreateView.as_view(), name='student_signup'),
17 |
18 | path('signup/teacher/stream/', StreamView.as_view(template_name = "accounts/teacher_stream.html"), name='teacher_stream'),
19 | path('signup/teacher//', TeacherCreateView.as_view(), name='teacher_signup'),
20 |
21 | ]
22 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
23 |
--------------------------------------------------------------------------------
/students/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import include, path
2 | from .views import (DashboardView,
3 | ClassroomDetailView,
4 | ResourcesView,
5 | AssignmentsView,
6 | AssignmentSubmissionCreateView,
7 | MyAssignmentView,
8 | AssignmentSubmissionDeleteView,
9 | joinClassroomView,
10 | LeaveClassroomView
11 | )
12 |
13 | app_name = 'students'
14 |
15 | urlpatterns = [
16 | path('', DashboardView.as_view(), name='dashboard'),
17 | path('classroom//', ClassroomDetailView,name='classroom_detail'),
18 | path('section//resources/', ResourcesView, name='resources'),
19 | path('section//assignments/', AssignmentsView, name='assignments'),
20 | path('assignment//', MyAssignmentView, name='my_assignment'),
21 | path('assignment//submit/', AssignmentSubmissionCreateView.as_view(), name='submit_assignment'),
22 | path('delete-submission//', AssignmentSubmissionDeleteView.as_view(), name='delete_submission'),
23 | path('join-new/',joinClassroomView, name='join_classroom'),
24 | path('leave-classroom//', LeaveClassroomView,name='leave_classroom'),
25 | ]
26 |
--------------------------------------------------------------------------------
/mylms/urls.py:
--------------------------------------------------------------------------------
1 | """mylms URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.urls import include, path
18 | from django.conf import settings
19 | from django.conf.urls.static import static
20 | from .views import HomeView, DashboardView
21 |
22 | urlpatterns = [
23 | path('admin/', admin.site.urls),
24 | path("", view=HomeView.as_view(),name='home'),
25 | path('dashboard/', DashboardView, name='dashboard'),
26 | path('accounts/', include('accounts.urls', namespace='accounts')),
27 | path('students/', include('students.urls', namespace='students')),
28 | path('teachers/', include('teachers.urls', namespace='teachers')),
29 | ]
30 |
31 | if settings.DEBUG:
32 | urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
33 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | asgiref==3.4.1
2 | autopep8==1.6.0
3 | azure-core==1.23.1
4 | azure-storage-blob==12.11.0
5 | backoff==1.11.1
6 | beautifulsoup4==4.10.0
7 | Bottleneck==1.3.4
8 | brotlipy==0.7.0
9 | certifi==2021.10.8
10 | cffi==1.15.0
11 | charset-normalizer==2.0.4
12 | click==8.1.2
13 | colorama==0.4.4
14 | cryptography==36.0.0
15 | decorator==5.1.1
16 | distlib==0.3.4
17 | Django==3.2.5
18 | django-bootstrap4==22.1
19 | django-storages==1.12.3
20 | docopt==0.6.2
21 | filelock==3.6.0
22 | future==0.18.2
23 | geocoder==1.38.1
24 | geographiclib==1.52
25 | geopy==2.2.0
26 | idna==3.3
27 | isodate==0.6.1
28 | mkl-fft==1.3.1
29 | mkl-random==1.2.2
30 | mkl-service==2.4.0
31 | mock==4.0.3
32 | msrest==0.6.21
33 | networkx==2.8
34 | nominatim==0.1
35 | numexpr==2.8.1
36 | numpy==1.21.5
37 | oauthlib==3.2.0
38 | opencage==2.0.0
39 | packaging==21.3
40 | pandas==1.4.1
41 | pip==21.2.4
42 | pipenv==2022.4.20
43 | pipreqs==0.4.11
44 | platformdirs==2.5.2
45 | psycopg2==2.8.6
46 | pybbn==3.2.1
47 | pycodestyle==2.8.0
48 | pycparser==2.21
49 | pyOpenSSL==22.0.0
50 | pyparsing==3.0.4
51 | PySocks==1.7.1
52 | python-dateutil==2.8.2
53 | pytz==2021.3
54 | ratelim==0.1.6
55 | requests==2.27.1
56 | requests-oauthlib==1.3.1
57 | scipy==1.8.0
58 | setuptools==58.0.4
59 | six==1.16.0
60 | soupsieve==2.3.1
61 | sqlparse==0.4.1
62 | tables==3.7.0
63 | toml==0.10.2
64 | typing_extensions==4.1.1
65 | urllib3==1.26.8
66 | virtualenv==20.14.1
67 | virtualenv-clone==0.5.7
68 | wheel==0.37.1
69 | win-inet-pton==1.1.0
70 | wincertstore==0.2
71 | yarg==0.1.9
72 |
--------------------------------------------------------------------------------
/mylms/lmsstatic/assets/js/creative.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict"; // Start of use strict
3 |
4 | var mainNav = document.querySelector('#mainNav');
5 |
6 | if (mainNav) {
7 |
8 | var navbarCollapse = mainNav.querySelector('.navbar-collapse');
9 |
10 | if (navbarCollapse) {
11 |
12 | var collapse = new bootstrap.Collapse(navbarCollapse, {
13 | toggle: false
14 | });
15 |
16 | var navbarItems = navbarCollapse.querySelectorAll('a');
17 |
18 | // Closes responsive menu when a scroll trigger link is clicked
19 | for (var item of navbarItems) {
20 | item.addEventListener('click', function (event) {
21 | collapse.hide();
22 | });
23 | }
24 | }
25 |
26 | // Collapse Navbar
27 | var collapseNavbar = function() {
28 |
29 | var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
30 |
31 | if (scrollTop > 100) {
32 | mainNav.classList.add("navbar-shrink");
33 | } else {
34 | mainNav.classList.remove("navbar-shrink");
35 | }
36 | };
37 | // Collapse now if page is not at top
38 | collapseNavbar();
39 | // Collapse the navbar when page is scrolled
40 | document.addEventListener("scroll", collapseNavbar);
41 | }
42 |
43 | // bageutteBox init
44 | if (document.getElementsByClassName('popup-gallery').length > 0) {
45 | baguetteBox.run('.popup-gallery', { animation: 'slideIn' });
46 | }
47 |
48 | })(); // End of use strict
49 |
--------------------------------------------------------------------------------
/students/templates/students/classroom_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% block title %}Classroom Detail{% endblock title %}
5 |
6 |
7 | {% block content %}
8 |
9 |
10 |
{{ classroom.subject|upper }}
11 |
12 |
13 |
14 | {% for section in classroom.sections.all %}
15 |
28 | {% endfor %}
29 |
30 |
31 |
32 | {% endblock content %}
--------------------------------------------------------------------------------
/accounts/static/accounts/assets/js/creative.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | "use strict"; // Start of use strict
3 |
4 | var mainNav = document.querySelector('#mainNav');
5 |
6 | if (mainNav) {
7 |
8 | var navbarCollapse = mainNav.querySelector('.navbar-collapse');
9 |
10 | if (navbarCollapse) {
11 |
12 | var collapse = new bootstrap.Collapse(navbarCollapse, {
13 | toggle: false
14 | });
15 |
16 | var navbarItems = navbarCollapse.querySelectorAll('a');
17 |
18 | // Closes responsive menu when a scroll trigger link is clicked
19 | for (var item of navbarItems) {
20 | item.addEventListener('click', function (event) {
21 | collapse.hide();
22 | });
23 | }
24 | }
25 |
26 | // Collapse Navbar
27 | var collapseNavbar = function() {
28 |
29 | var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
30 |
31 | if (scrollTop > 100) {
32 | mainNav.classList.add("navbar-shrink");
33 | } else {
34 | mainNav.classList.remove("navbar-shrink");
35 | }
36 | };
37 | // Collapse now if page is not at top
38 | collapseNavbar();
39 | // Collapse the navbar when page is scrolled
40 | document.addEventListener("scroll", collapseNavbar);
41 | }
42 |
43 | // bageutteBox init
44 | if (document.getElementsByClassName('popup-gallery').length > 0) {
45 | baguetteBox.run('.popup-gallery', { animation: 'slideIn' });
46 | }
47 |
48 | })(); // End of use strict
49 |
--------------------------------------------------------------------------------
/accounts/static/accounts/js/theme.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | "use strict"; // Start of use strict
3 |
4 | // Toggle the side navigation
5 | $("#sidebarToggle, #sidebarToggleTop").on('click', function(e) {
6 | $("body").toggleClass("sidebar-toggled");
7 | $(".sidebar").toggleClass("toggled");
8 | if ($(".sidebar").hasClass("toggled")) {
9 | $('.sidebar .collapse').collapse('hide');
10 | };
11 | });
12 |
13 | // Close any open menu accordions when window is resized below 768px
14 | $(window).resize(function() {
15 | if ($(window).width() < 768) {
16 | $('.sidebar .collapse').collapse('hide');
17 | };
18 | });
19 |
20 | // Prevent the content wrapper from scrolling when the fixed side navigation hovered over
21 | $('body.fixed-nav .sidebar').on('mousewheel DOMMouseScroll wheel', function(e) {
22 | if ($(window).width() > 768) {
23 | var e0 = e.originalEvent,
24 | delta = e0.wheelDelta || -e0.detail;
25 | this.scrollTop += (delta < 0 ? 1 : -1) * 30;
26 | e.preventDefault();
27 | }
28 | });
29 |
30 | // Scroll to top button appear
31 | $(document).on('scroll', function() {
32 | var scrollDistance = $(this).scrollTop();
33 | if (scrollDistance > 100) {
34 | $('.scroll-to-top').fadeIn();
35 | } else {
36 | $('.scroll-to-top').fadeOut();
37 | }
38 | });
39 |
40 | // Smooth scrolling using jQuery easing
41 | $(document).on('click', 'a.scroll-to-top', function(e) {
42 | var $anchor = $(this);
43 | $('html, body').stop().animate({
44 | scrollTop: ($($anchor.attr('href')).offset().top)
45 | }, 1000, 'easeInOutExpo');
46 | e.preventDefault();
47 | });
48 |
49 | })(jQuery); // End of use strict
50 |
--------------------------------------------------------------------------------
/students/static/students/js/theme.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | "use strict"; // Start of use strict
3 |
4 | // Toggle the side navigation
5 | $("#sidebarToggle, #sidebarToggleTop").on('click', function(e) {
6 | $("body").toggleClass("sidebar-toggled");
7 | $(".sidebar").toggleClass("toggled");
8 | if ($(".sidebar").hasClass("toggled")) {
9 | $('.sidebar .collapse').collapse('hide');
10 | };
11 | });
12 |
13 | // Close any open menu accordions when window is resized below 768px
14 | $(window).resize(function() {
15 | if ($(window).width() < 768) {
16 | $('.sidebar .collapse').collapse('hide');
17 | };
18 | });
19 |
20 | // Prevent the content wrapper from scrolling when the fixed side navigation hovered over
21 | $('body.fixed-nav .sidebar').on('mousewheel DOMMouseScroll wheel', function(e) {
22 | if ($(window).width() > 768) {
23 | var e0 = e.originalEvent,
24 | delta = e0.wheelDelta || -e0.detail;
25 | this.scrollTop += (delta < 0 ? 1 : -1) * 30;
26 | e.preventDefault();
27 | }
28 | });
29 |
30 | // Scroll to top button appear
31 | $(document).on('scroll', function() {
32 | var scrollDistance = $(this).scrollTop();
33 | if (scrollDistance > 100) {
34 | $('.scroll-to-top').fadeIn();
35 | } else {
36 | $('.scroll-to-top').fadeOut();
37 | }
38 | });
39 |
40 | // Smooth scrolling using jQuery easing
41 | $(document).on('click', 'a.scroll-to-top', function(e) {
42 | var $anchor = $(this);
43 | $('html, body').stop().animate({
44 | scrollTop: ($($anchor.attr('href')).offset().top)
45 | }, 1000, 'easeInOutExpo');
46 | e.preventDefault();
47 | });
48 |
49 | })(jQuery); // End of use strict
50 |
--------------------------------------------------------------------------------
/teachers/static/teachers/js/theme.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | "use strict"; // Start of use strict
3 |
4 | // Toggle the side navigation
5 | $("#sidebarToggle, #sidebarToggleTop").on('click', function(e) {
6 | $("body").toggleClass("sidebar-toggled");
7 | $(".sidebar").toggleClass("toggled");
8 | if ($(".sidebar").hasClass("toggled")) {
9 | $('.sidebar .collapse').collapse('hide');
10 | };
11 | });
12 |
13 | // Close any open menu accordions when window is resized below 768px
14 | $(window).resize(function() {
15 | if ($(window).width() < 768) {
16 | $('.sidebar .collapse').collapse('hide');
17 | };
18 | });
19 |
20 | // Prevent the content wrapper from scrolling when the fixed side navigation hovered over
21 | $('body.fixed-nav .sidebar').on('mousewheel DOMMouseScroll wheel', function(e) {
22 | if ($(window).width() > 768) {
23 | var e0 = e.originalEvent,
24 | delta = e0.wheelDelta || -e0.detail;
25 | this.scrollTop += (delta < 0 ? 1 : -1) * 30;
26 | e.preventDefault();
27 | }
28 | });
29 |
30 | // Scroll to top button appear
31 | $(document).on('scroll', function() {
32 | var scrollDistance = $(this).scrollTop();
33 | if (scrollDistance > 100) {
34 | $('.scroll-to-top').fadeIn();
35 | } else {
36 | $('.scroll-to-top').fadeOut();
37 | }
38 | });
39 |
40 | // Smooth scrolling using jQuery easing
41 | $(document).on('click', 'a.scroll-to-top', function(e) {
42 | var $anchor = $(this);
43 | $('html, body').stop().animate({
44 | scrollTop: ($($anchor.attr('href')).offset().top)
45 | }, 1000, 'easeInOutExpo');
46 | e.preventDefault();
47 | });
48 |
49 | })(jQuery); // End of use strict
50 |
--------------------------------------------------------------------------------
/teachers/templates/teachers/classroomstudents.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}List of Students{% endblock title %} {% block content %}
2 |
3 |
4 |
5 |
{{classroom.subject|upper}}
6 |
7 |
10 |
11 |
12 |
13 |
14 |
15 | Name
16 | Student ID
17 | Class
18 | Roll No.
19 |
20 |
21 |
22 |
23 | {% for student in classroom_students %}
24 |
25 | {{student.user.first_name}} {{student.user.last_name}}
26 | {{student.student_id}}
27 | {{student.current_class}}
28 | {{student.roll_no}}
29 |
30 | {% endfor %}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | {% endblock content %}
--------------------------------------------------------------------------------
/students/templates/students/resources.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% block title %}Resources{% endblock title %}
5 |
6 |
7 | {% block content %}
8 |
9 |
10 |
{{section.classroom.subject|upper}} : Resources
11 |
12 |
13 |
14 | {% for resource in section.resources.all|dictsortreversed:"created_timestamp" %}
15 |
16 |
17 |
18 |
19 |
20 |
{{resource.created_timestamp|date:"M d, Y"}}
21 |
{{resource.text|linebreaksbr}}
22 |
23 |
24 |
25 | {% if resource.file %}
26 |
Download Resource {% endif %}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 | {% endblock content %}
--------------------------------------------------------------------------------
/.github/workflows/master_lms-app.yml:
--------------------------------------------------------------------------------
1 | # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
2 | # More GitHub Actions for Azure: https://github.com/Azure/actions
3 | # More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions
4 |
5 | name: Build and deploy Python app to Azure Web App - lms-app
6 |
7 | on:
8 | push:
9 | branches:
10 | - master
11 | workflow_dispatch:
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v2
19 |
20 | - name: Set up Python version
21 | uses: actions/setup-python@v1
22 | with:
23 | python-version: '3.9'
24 |
25 | - name: Create and start virtual environment
26 | run: |
27 | python -m venv venv
28 | source venv/bin/activate
29 |
30 | - name: Install dependencies
31 | run: pip install -r requirements.txt
32 |
33 | # Optional: Add step to run tests here (PyTest, Django test suites, etc.)
34 |
35 | - name: Upload artifact for deployment jobs
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: python-app
39 | path: |
40 | .
41 | !venv/
42 |
43 | deploy:
44 | runs-on: ubuntu-latest
45 | needs: build
46 | environment:
47 | name: 'Production'
48 | url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
49 |
50 | steps:
51 | - name: Download artifact from build job
52 | uses: actions/download-artifact@v2
53 | with:
54 | name: python-app
55 | path: .
56 |
57 | - name: 'Deploy to Azure Web App'
58 | uses: azure/webapps-deploy@v2
59 | id: deploy-to-webapp
60 | with:
61 | app-name: 'lms-app'
62 | slot-name: 'Production'
63 | publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_42E0E845E61C43DCB8DA10DADAD661C8 }}
64 |
--------------------------------------------------------------------------------
/students/templates/students/dashboard.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% block title %}Student Dashboard{% endblock title %}
5 |
6 |
7 | {% block content %}
8 |
9 |
10 |
Classrooms
11 |
12 |
13 |
14 | {% for classroom in user.students.classrooms.all %}
15 |
42 | {% endfor %}
43 |
44 |
45 |
46 |
47 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import include, path
2 | from .views import (DashboardView,
3 | ClassroomCreateView,
4 | ClassroomDeleteView,
5 | ClassroomDetailView,
6 | ResourcesView,
7 | SectionCreateView,
8 | AssignmentsView,
9 | SectionDeleteView,
10 | ResourceCreateView,
11 | ResourceDeleteView,
12 | AssignmentCreateView,
13 | AssignmentDeleteView,
14 | AssignmentSubmissionsView,
15 | ClassroomStudentsView,
16 | )
17 |
18 | app_name = 'teachers'
19 |
20 | urlpatterns = [
21 | path('', DashboardView.as_view(), name='dashboard'),
22 | path('classroom/add/', ClassroomCreateView.as_view(), name='add_classroom'),
23 | path('classroom//delete/', ClassroomDeleteView.as_view(), name='delete_classroom'),
24 | path('classroom//', ClassroomDetailView, name='classroom_detail'),
25 | path('classroom//students/', ClassroomStudentsView, name='classroom_students'),
26 | path('classroom//add/', SectionCreateView.as_view(), name='add_section'),
27 | path('section//delete/', SectionDeleteView.as_view(), name='delete_section'),
28 | path('resources//', ResourcesView, name='resources'),
29 | path('resources//add/', ResourceCreateView.as_view(), name='add_resource'),
30 | path('resources/delete//', ResourceDeleteView.as_view(), name='delete_resource'),
31 | path('assignments//', AssignmentsView, name='assignments'),
32 | path('assignments//add/', AssignmentCreateView.as_view(), name='add_assignment'),
33 | path('assignments/delete//', AssignmentDeleteView.as_view(), name='delete_assignment'),
34 | path('submissions//', AssignmentSubmissionsView, name='assignment_submissions'),
35 | ]
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Learning Management System
2 |
3 | ### Demo:
4 | https://lmsdjango.azurewebsites.net/
5 |
6 | ### This project is made using :
7 | - Django 3.2.5
8 | - Python 3.9.11
9 | - Bootstrap
10 | - django-bootstrap4
11 | - Microsoft Azure
12 | - django-storages
13 |
14 |
15 | ## Problem Statement
16 |
17 | The Learning Management System is for any learner or teacher which will replace
18 | existing conventional methods of chalk and board style of learning. The new system
19 | will control the following information: store the instructional materials , books ,
20 | videos , and control information and various other educational resources, which will
21 | be organized by category. These services are to be provided in an effective manner,
22 | with the goal of giving and receiving simpler, prolific, and productive learning.
23 |
24 | ## Objectives
25 | The Learning Management System is designed to replace existing conventional
26 | methods of chalk and board style of learning. A Learning Management System
27 | (LMS) is software that automates the administration of teaching events. All
28 | Learning Management Systems manage the log-in of registered users, manage
29 | course catalogs, record data from learners, and provide reports to management.
30 | However, most of the LMS in service have different characteristics and are mutually
31 | incompatible, hence, effective Learning Management System needs to be
32 | standardized thus, we are implementing LMS.
33 |
34 |
35 |
36 | ## Installation
37 |
38 | ```bash
39 | $ git clone https://github.com/radonintro1234/django-learning-management-system.git
40 |
41 | $ cd django-learning-management-system
42 |
43 | $ pip install -r requirements.txt
44 | ```
45 |
46 | ## Usage
47 |
48 | ```bash
49 | $ python manage.py makemigrations
50 |
51 | $ python manage.py migrate
52 |
53 | $ python manage.py runserver
54 | ```
55 |
56 | ## Relation Schema of the database
57 |
58 | 
59 |
60 | ## Contributing
61 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
62 |
63 | Please make sure to update tests as appropriate.
64 |
65 | ## License
66 | [MIT](https://choosealicense.com/licenses/mit/)
67 |
--------------------------------------------------------------------------------
/accounts/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import redirect, render
2 | from django.contrib.auth import login, logout
3 | from django.views.generic import TemplateView, CreateView, ListView
4 | from .models import User, Student, Teacher, Stream
5 | from .forms import StudentForm, TeacherForm
6 | from accounts.passtests import StudentTestMixin, TeacherTestMixin
7 |
8 |
9 | # Create your views here.
10 |
11 | class IndexView(TemplateView):
12 | template_name = "accounts/index.html"
13 |
14 |
15 | class StudentCreateView(CreateView):
16 | model = User
17 | form_class = StudentForm
18 | template_name = "accounts/student_signup.html"
19 |
20 | def get_context_data(self, **kwargs):
21 | kwargs['user_type'] = 'student'
22 | return super().get_context_data(**kwargs)
23 |
24 | def form_valid(self, form):
25 | user = form.save()
26 | login(self.request, user)
27 | return redirect('dashboard')
28 |
29 | def get_form_kwargs(self):
30 | kwargs = super().get_form_kwargs()
31 | print(self.kwargs)
32 | kwargs.update({
33 | 'stream': Stream.objects.filter(pk=self.kwargs['stream_pk']).first(),
34 | })
35 | print(Stream.objects.filter(pk=self.kwargs['stream_pk']).first())
36 | return kwargs
37 |
38 | class StreamView(TemplateView):
39 |
40 | def get_context_data(self, **kwargs):
41 | context = super(StreamView, self).get_context_data(**kwargs)
42 | context['stream_list'] = Stream.objects.all()
43 | print
44 | return context
45 |
46 | class TeacherCreateView(CreateView):
47 | model = User
48 | form_class = TeacherForm
49 | template_name = "accounts/teacher_signup.html"
50 |
51 | def get_context_data(self, **kwargs):
52 | kwargs['user_type'] = 'teacher'
53 | return super().get_context_data(**kwargs)
54 |
55 | def form_valid(self, form):
56 | user = form.save()
57 | login(self.request, user)
58 | return redirect('dashboard')
59 |
60 | def get_form_kwargs(self):
61 | kwargs = super().get_form_kwargs()
62 | print(self.kwargs)
63 | kwargs.update({
64 | 'stream': Stream.objects.filter(pk=self.kwargs['stream_pk']).first(),
65 | })
66 | print(Stream.objects.filter(pk=self.kwargs['stream_pk']).first())
67 | return kwargs
--------------------------------------------------------------------------------
/teachers/templates/teachers/dashboard.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% block title %}Dashboard{% endblock title %} {% block content %}
2 |
3 |
4 |
Classrooms
5 |
6 |
7 |
8 | {% for classrom in user.teachers.classrooms.all %}
9 |
10 |
16 |
17 |
Classroom code : {{ classrom.code }}
18 |
19 |
20 |
34 |
35 |
36 |
37 | {% endfor %}
38 |
39 |
40 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/submissions.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Submissions{% endblock title %} {% block content %}
2 |
3 |
4 |
5 |
Assignment Submissions
6 |
7 |
10 |
11 |
Deadline : {{assignment.deadline|date:"M d, Y"}}
12 |
13 |
14 |
15 |
16 | Name
17 | Student ID
18 | Class
19 | Roll No.
20 | Submission date
21 | Download File
22 |
23 |
24 |
25 |
26 | {% for submission in assignment_submissions %}
27 |
28 | {{submission.student.user.first_name}} {{submission.student.user.last_name}}
29 | {{submission.student.student_id}}
30 | {{submission.student.current_class}}
31 | {{submission.student.roll_no}}
32 | {{submission.submission_timestamp|date:"d/m/Y"}} {{submission.submission_timestamp|date:"h:i A"}}
33 | Download
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/classroom_detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Classroom Detail{% endblock title %} {% block content %}
2 |
3 |
4 |
{{ classroom.subject|upper }}
5 |
6 |
7 |
13 |
14 |
15 |
16 | {% for section in classroom.sections.all %}
17 |
41 |
42 | {% endfor %}
43 |
44 |
45 |
46 | {% endblock content %}
--------------------------------------------------------------------------------
/students/templates/students/assignments.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% block title %}Resources{% endblock title %}
5 |
6 |
7 | {% block content %}
8 |
9 |
10 |
{{section.classroom.subject|upper}} : Assignments
11 |
12 |
13 |
14 |
15 |
16 | {% for assignment in section.assignments.all|dictsortreversed:"created_timestamp" %}
17 |
18 |
19 |
20 |
Deadline : {{assignment.deadline|date:"M d, Y"}}
21 |
{{assignment.text|linebreaksbr}}
22 |
23 |
24 |
25 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {% endfor %}
58 |
59 |
60 |
61 |
62 |
63 | {% endblock content %}
--------------------------------------------------------------------------------
/students/templates/students/assignmentsubmission.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% load static %}
5 |
6 |
7 | {% block title %}Resources{% endblock title %}
8 |
9 |
10 | {% block content %}
11 |
12 |
13 |
14 |
15 |
18 |
19 |
× Deadline : 25 April, 2022
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
Upload your File....
32 |
33 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
60 |
61 |
62 |
63 |
70 |
71 |
72 | {% endblock content %}
--------------------------------------------------------------------------------
/students/templates/students/join_classroom.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% load static %}
5 |
6 |
7 | {% block title %}Resources{% endblock title %}
8 |
9 |
10 | {% block content %}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
Class code
21 |
Ask your teacher for the class code , then enter it here !
22 |
23 |
28 |
29 |
30 |
31 |
To sign in with a class code
32 | Use an authorized account
33 | Use a class code with 5-7 letters or numbers, and no
34 | spaces or symbols.
35 |
36 | If you have trouble joining the class, go to the Help.
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
49 |
50 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/resources.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Resources{% endblock title %} {% block content %}
2 |
3 |
4 |
Resources
5 |
9 |
10 |
11 |
12 | {% for resource in section.resources.all|dictsortreversed:"created_timestamp" %}
13 |
14 |
15 |
16 |
17 |
18 |
19 |
{{resource.created_timestamp|date:"M d, Y"}}
20 |
21 |
{{resource.text|linebreaksbr}}
22 |
23 |
24 |
25 | {% if resource.file %}
26 |
30 | {% endif %}
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {% endfor %}
47 |
48 |
49 |
50 | {% endblock content %}
--------------------------------------------------------------------------------
/teachers/templates/teachers/assignments.html:
--------------------------------------------------------------------------------
1 | {% extends 'teachers/base.html' %} {% load static %} {% block title %}Assignments{% endblock title %} {% block content %}
2 |
3 |
4 |
Assignments
5 |
6 |
7 |
11 |
12 |
13 |
14 | {% for assignment in section.assignments.all|dictsortreversed:"created_timestamp" %}
15 |
16 |
17 |
18 |
19 |
20 |
Deadline : {{assignment.deadline|date:"M d, Y"}}
21 |
{{assignment.text|linebreaksbr}}
22 |
23 |
24 |
25 | {% if assignment.file %}
26 |
30 | {% endif %}
31 |
32 |
Submissions
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {% endfor %}
50 |
51 |
52 | {% endblock content %}
--------------------------------------------------------------------------------
/students/templates/students/my_assignment.html:
--------------------------------------------------------------------------------
1 | {% extends 'students/base.html' %}
2 |
3 |
4 | {% load static %}
5 |
6 |
7 | {% block title %}Resources{% endblock title %}
8 |
9 |
10 | {% block content %}
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 | ×
22 |
23 |
24 | Deadline : {{assignment.deadline|date:"M d, Y"}}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Submitted on : {{submission.submission_timestamp|date:"M d, Y"}}
33 |
34 |
35 |
36 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | {% endblock content %}
--------------------------------------------------------------------------------
/accounts/templates/accounts/teacher_signup.html:
--------------------------------------------------------------------------------
1 | {% load static %} {% load bootstrap4 %} {% bootstrap_messages %}
2 |
3 |
4 |
5 |
6 |
7 | Register - Teacher
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
Create an Teacher Account!
21 |
22 |
41 |
45 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
80 |
81 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/login.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 | Login - LMS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
Welcome Back!
23 |
24 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/students/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponseRedirect
2 | from django import forms
3 | from django import views
4 | from django.shortcuts import redirect, render, get_object_or_404
5 | from django.urls import reverse, reverse_lazy
6 | from django.views.generic import TemplateView, CreateView, ListView, DeleteView, DetailView
7 | from accounts.models import Classroom,Teacher,User,Section, Resource,Assignment,AssignmentSubmission
8 | from accounts.passtests import StudentTestMixin, TeacherTestMixin
9 | from .forms import AssignmentSubmissionForm, JoinClassroomForm
10 |
11 | # Create your views here.
12 |
13 | class DashboardView(StudentTestMixin, TemplateView):
14 | template_name = "students/dashboard.html"
15 |
16 | def ClassroomDetailView(request,code):
17 | context_dict = {}
18 | room = get_object_or_404(Classroom, code=code)
19 | context_dict['classroom'] = room
20 | return render(request, 'students/classroom_detail.html', context=context_dict)
21 |
22 | def ResourcesView(request, pk):
23 | context_dict = {}
24 | section = get_object_or_404(Section, pk=pk)
25 | print(section)
26 | context_dict['section'] = section
27 | return render(request, 'students/resources.html', context=context_dict)
28 |
29 | def AssignmentsView(request, pk):
30 | context_dict = {}
31 | section = get_object_or_404(Section, pk=pk)
32 | print(section)
33 | context_dict['section'] = section
34 | return render(request, 'students/assignments.html', context=context_dict)
35 |
36 | class AssignmentSubmissionCreateView(CreateView):
37 | model = AssignmentSubmission
38 | form_class = AssignmentSubmissionForm
39 | template_name = "students/assignmentsubmission.html"
40 |
41 | def form_valid(self, form):
42 | form.instance.assignment = get_object_or_404(Assignment, pk=self.kwargs['pk'])
43 | form.instance.student = self.request.user.students
44 | return super().form_valid(form)
45 |
46 | def get_success_url(self):
47 | return reverse('students:assignments', kwargs={'pk':Assignment.objects.get(pk=self.kwargs['pk']).section.pk})
48 |
49 | def MyAssignmentView(request, pk):
50 | context_dict = {}
51 | student = request.user.students
52 | assignment = Assignment.objects.get(pk=pk)
53 |
54 | if AssignmentSubmission.objects.filter(assignment=assignment).filter(student=student).exists():
55 | context_dict['assignment'] = assignment
56 | context_dict['submission'] = AssignmentSubmission.objects.filter(assignment=assignment).filter(student=student)[0]
57 | context_dict['assignmentfilename'] = context_dict['submission'].file.name.split('/')[-1]
58 | return render(request, template_name='students/my_assignment.html', context=context_dict)
59 | else:
60 | return redirect('students:submit_assignment', pk=pk)
61 |
62 | class AssignmentSubmissionDeleteView(DeleteView):
63 | model = AssignmentSubmission
64 | template_name = "students/deletesubmission.html"
65 |
66 | def get_success_url(self):
67 | return reverse('students:assignments', kwargs={'pk':self.object.assignment.section.pk})
68 |
69 | def joinClassroomView(request):
70 | form = JoinClassroomForm()
71 |
72 | if request.method == 'POST':
73 | form = JoinClassroomForm(request.POST)
74 |
75 | if form.is_valid():
76 | code = form.cleaned_data['code']
77 | print(code)
78 | if Classroom.objects.filter(code=code).exists():
79 | print('Exists', form.cleaned_data['code'])
80 |
81 | if request.user.students.classrooms.filter(code=code).exists():
82 | print('User already a member')
83 | return HttpResponseRedirect(reverse('students:dashboard'))
84 | else:
85 | print('User not a member')
86 | request.user.students.classrooms.add(Classroom.objects.filter(code=code).first())
87 | print('User joined the classroom')
88 | return HttpResponseRedirect(reverse('students:dashboard'))
89 | else:
90 | print('Classroom dosent exist')
91 | return render(request, 'students/join_classroom.html', {'form':form})
92 |
93 |
94 | def LeaveClassroomView(request,code):
95 | request.user.students.classrooms.remove(Classroom.objects.filter(code=code).first())
96 | return HttpResponseRedirect(reverse('students:dashboard'))
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/accounts/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib.auth.forms import UserCreationForm
3 | from django.db import transaction
4 | from .models import Student, User, Teacher, Class, Classroom
5 |
6 |
7 | class StudentForm(UserCreationForm):
8 | def __init__(self,*args,**kwargs):
9 | self.stream = kwargs.pop('stream')
10 | print('stream is ', self.stream)
11 | super(StudentForm,self).__init__(*args,**kwargs)
12 |
13 | if Class.objects.all().exists():
14 | self.fields['current_class'].queryset = Class.objects.filter(stream=self.stream).order_by('name')
15 |
16 | email = forms.EmailField(label='Email', required=True)
17 |
18 | first_name = forms.CharField(
19 | label='First Name', max_length=250, required=True)
20 |
21 | last_name = forms.CharField(
22 | label='Last Name', max_length=250, required=True)
23 |
24 | current_class = forms.ModelChoiceField(queryset=None, empty_label=None, required=True)
25 |
26 | roll_no =forms.IntegerField(label='Roll No.', required=True, min_value=1, max_value=500)
27 |
28 | student_id = forms.CharField(label='Student ID', max_length=50, required=True, min_length=4)
29 |
30 | class Meta(UserCreationForm.Meta):
31 | model = User
32 |
33 | @transaction.atomic
34 | def save(self):
35 | user = super().save(commit=False)
36 | user.is_student = True
37 | user.first_name = self.cleaned_data['first_name']
38 | user.last_name = self.cleaned_data['last_name']
39 | user.email = self.cleaned_data['email']
40 | user.save()
41 | student = Student.objects.create(
42 | user=user,
43 | current_class = self.cleaned_data['current_class'],
44 | roll_no = self.cleaned_data['roll_no'],
45 | student_id = self.cleaned_data['student_id'],
46 | )
47 | student.save()
48 | return user
49 |
50 | def clean(self):
51 | cleaned_data = super().clean()
52 | current_class = cleaned_data.get('current_class')
53 | roll_no = self.cleaned_data.get('roll_no')
54 | student_id = self.cleaned_data.get('student_id')
55 | email = self.cleaned_data.get('email')
56 |
57 | # Class and Roll No. Unique Constraint Validation
58 | if Student.objects.filter(current_class=current_class).filter(roll_no=roll_no).exists():
59 | print("Roll no in class")
60 | raise forms.ValidationError('Student with Roll No. already exists.')
61 |
62 | # Student id validation
63 | if Student.objects.filter(student_id=student_id).exists():
64 | print("Student ID exists")
65 | raise forms.ValidationError('Student with Student ID already exists.')
66 |
67 | # Email Validations
68 | if User.objects.filter(email=email).exists():
69 | print("Email exists")
70 | raise forms.ValidationError('User with Email already exists.')
71 |
72 |
73 | class TeacherForm(UserCreationForm):
74 | def __init__(self,*args,**kwargs):
75 | self.stream = kwargs.pop('stream')
76 | print('stream is ', self.stream)
77 | print(type(self.stream))
78 | super(TeacherForm,self).__init__(*args,**kwargs)
79 |
80 | email = forms.EmailField(label='Email', required=True)
81 |
82 | first_name = forms.CharField(
83 | label='First Name', max_length=250, required=True)
84 |
85 | last_name = forms.CharField(
86 | label='Last Name', max_length=250, required=True)
87 |
88 | class Meta(UserCreationForm.Meta):
89 | model = User
90 |
91 | @transaction.atomic
92 | def save(self):
93 | user = super().save(commit=False)
94 | user.is_teacher = True
95 | user.first_name = self.cleaned_data['first_name']
96 | user.last_name = self.cleaned_data['last_name']
97 | user.email = self.cleaned_data['email']
98 | user.save()
99 | teacher = Teacher.objects.create(
100 | user=user,
101 | stream = self.stream
102 | )
103 | teacher.save()
104 | return user
105 |
106 | def clean(self):
107 | cleaned_data = super().clean()
108 | email = self.cleaned_data.get('email')
109 |
110 | # Email Validations
111 | if User.objects.filter(email=email).exists():
112 | print("Email exists")
113 | raise forms.ValidationError('User with Email already exists.')
114 |
--------------------------------------------------------------------------------
/mylms/templates/mylms/home.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}L.M.S{% endblock title %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
L.M.S
20 |
21 |
22 |
32 |
33 |
34 |
35 |
36 | {% block content %}
37 |
38 |
39 |
40 |
41 | Get started with the learning management system
42 |
43 |
44 |
45 |
46 |
47 |
51 |
52 |
53 | {% endblock content %}
54 |
55 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/accounts/templates/accounts/base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}L.M>S{% endblock title %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
L.M.S
21 |
22 |
23 |
33 |
34 |
35 |
36 |
37 | {% block content %}
38 |
39 |
40 |
41 |
42 | Get started with the learning management system
43 |
44 |
45 |
46 |
47 |
48 |
52 |
53 |
54 | {% endblock content %}
55 |
56 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/mylms/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for mylms project.
3 |
4 | Generated by 'django-admin startproject' using Django 3.2.5.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/3.2/ref/settings/
11 | """
12 | import environ
13 | # Initialise environment variables
14 | env = environ.Env()
15 | environ.Env.read_env()
16 |
17 | from pathlib import Path
18 |
19 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
20 | BASE_DIR = Path(__file__).resolve().parent.parent
21 |
22 |
23 | # Quick-start development settings - unsuitable for production
24 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
25 |
26 | # SECURITY WARNING: keep the secret key used in production secret!
27 | SECRET_KEY = env('SECRET_KEY')
28 |
29 | # SECURITY WARNING: don't run with debug turned on in production!
30 | DEBUG = True
31 |
32 | ALLOWED_HOSTS = ['*']
33 |
34 |
35 | # Application definition
36 |
37 | INSTALLED_APPS = [
38 |
39 | 'django.contrib.admin',
40 | 'django.contrib.auth',
41 | 'django.contrib.contenttypes',
42 | 'django.contrib.sessions',
43 | 'django.contrib.messages',
44 | 'django.contrib.staticfiles',
45 | 'storages',
46 | 'accounts',
47 | 'bootstrap4',
48 | 'students',
49 | 'teachers',
50 | ]
51 |
52 | MIDDLEWARE = [
53 | 'django.middleware.security.SecurityMiddleware',
54 | 'django.contrib.sessions.middleware.SessionMiddleware',
55 | 'django.middleware.common.CommonMiddleware',
56 | 'django.middleware.csrf.CsrfViewMiddleware',
57 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
58 | 'django.contrib.messages.middleware.MessageMiddleware',
59 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
60 | ]
61 |
62 | ROOT_URLCONF = 'mylms.urls'
63 |
64 | TEMPLATES = [
65 | {
66 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
67 | 'DIRS': [BASE_DIR / 'mylms/templates'],
68 | 'APP_DIRS': True,
69 | 'OPTIONS': {
70 | 'context_processors': [
71 | 'django.template.context_processors.debug',
72 | 'django.template.context_processors.request',
73 | 'django.contrib.auth.context_processors.auth',
74 | 'django.contrib.messages.context_processors.messages',
75 | ],
76 | },
77 | },
78 | ]
79 |
80 | WSGI_APPLICATION = 'mylms.wsgi.application'
81 |
82 |
83 | # Database
84 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases
85 |
86 | DATABASES = {
87 | 'default': {
88 | 'ENGINE': 'django.db.backends.sqlite3',
89 | 'NAME': BASE_DIR / 'db.sqlite3',
90 | }
91 | }
92 |
93 |
94 | # Password validation
95 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
96 |
97 | AUTH_PASSWORD_VALIDATORS = [
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
100 | },
101 | {
102 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
103 | },
104 | {
105 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
106 | },
107 | {
108 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
109 | },
110 | ]
111 |
112 |
113 | # Internationalization
114 | # https://docs.djangoproject.com/en/3.2/topics/i18n/
115 |
116 | LANGUAGE_CODE = 'en-us'
117 |
118 | TIME_ZONE = 'Asia/Kolkata'
119 |
120 | USE_I18N = True
121 |
122 | USE_L10N = True
123 |
124 | USE_TZ = True
125 |
126 |
127 | # Static files (CSS, JavaScript, Images)
128 | # https://docs.djangoproject.com/en/3.2/howto/static-files/
129 |
130 | STATIC_URL = '/static/'
131 | STATIC_FILES_DIRS = [BASE_DIR / 'mylms/lmsstatic']
132 |
133 | # Default primary key field type
134 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
135 |
136 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
137 |
138 | AUTH_USER_MODEL = 'accounts.User'
139 |
140 | MEDIA_ROOT = BASE_DIR / 'media/'
141 | MEDIA_URL = "/cloud/media/"
142 |
143 | LOGIN_REDIRECT_URL = "/dashboard/"
144 | LOGIN_URL = '/accounts/login/'
145 |
146 | LOGOUT_REDIRECT_URL = '/'
147 |
148 |
149 | DEFAULT_FILE_STORAGE = 'mylms.custom_azure.AzureMediaStorage'
150 | STATICFILES_STORAGE = 'mylms.custom_azure.AzureStaticStorage'
151 |
152 | STATIC_LOCATION = "static"
153 | MEDIA_LOCATION = "media"
154 |
155 | AZURE_ACCOUNT_NAME = env('AZURE_ACCOUNT_NAME')
156 | AZURE_STORAGE_ACCOUNT_KEY = env('AZURE_STORAGE_ACCOUNT_KEY')
157 |
158 | AZURE_CUSTOM_DOMAIN = f'{AZURE_ACCOUNT_NAME}.blob.core.windows.net'
159 | STATIC_URL = f'https://{AZURE_CUSTOM_DOMAIN}/{STATIC_LOCATION}/'
160 | MEDIA_URL = f'https://{AZURE_CUSTOM_DOMAIN}/{MEDIA_LOCATION}/'
--------------------------------------------------------------------------------
/accounts/templates/accounts/student_signup.html:
--------------------------------------------------------------------------------
1 | {% load static %} {% load bootstrap4 %} {% bootstrap_messages %}
2 |
3 |
4 |
5 |
6 |
7 | Register - Student
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
Create an Student Account!
21 |
22 |
50 |
54 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
98 |
99 |
--------------------------------------------------------------------------------
/teachers/views.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpResponseRedirect
2 | from django.shortcuts import render, get_object_or_404
3 | from django.urls import reverse, reverse_lazy
4 | from django.views.generic import TemplateView, CreateView, ListView, DeleteView, DetailView
5 | from accounts.models import Classroom,Teacher,User,Section, Resource,Assignment,AssignmentSubmission
6 | from .forms import ClassroomForm, SectionForm,ResourceForm, AssignmentForm
7 | from accounts.passtests import StudentTestMixin, TeacherTestMixin
8 |
9 |
10 | # Create your views here.
11 |
12 | class DashboardView(TeacherTestMixin,TemplateView):
13 | template_name = "teachers/dashboard.html"
14 |
15 | def get_context_data(self, **kwargs):
16 | context = super().get_context_data(**kwargs)
17 | context['classrooms'] = self.request.user.teachers.classrooms.all()
18 | return context
19 |
20 | def add_classroom(request):
21 | form = ClassroomForm()
22 |
23 | def form_valid(self, form):
24 | classroom = form.save(commit=False)
25 | classroom.teacher = Teacher.objects.get(user=self.request.user.teachers)
26 | classroom.save()
27 | return HttpResponseRedirect(self.get_success_url())
28 |
29 | class ClassroomCreateView(CreateView):
30 | model = Classroom
31 | form_class = ClassroomForm
32 | template_name = "teachers/addclassroom.html"
33 |
34 | def form_valid(self, form):
35 | form.instance.teacher = self.request.user.teachers
36 | return super().form_valid(form)
37 |
38 | def get_success_url(self):
39 | return reverse('teachers:dashboard')
40 |
41 | class ClassroomDeleteView(DeleteView):
42 | model = Classroom
43 | template_name = "teachers/deleteclassroom.html"
44 |
45 | # Should match the value after ':' from url
46 | slug_url_kwarg = 'code'
47 |
48 | # Should match the name of the slug field on the model
49 | slug_field = 'code' # DetailView's default value: optional
50 |
51 | def get_success_url(self):
52 | return reverse('teachers:dashboard')
53 |
54 | def ClassroomDetailView(request,code):
55 | context_dict = {}
56 | room = get_object_or_404(Classroom, code=code)
57 | context_dict['classroom'] = room
58 | return render(request, 'teachers/classroom_detail.html', context=context_dict)
59 |
60 | class SectionCreateView(CreateView):
61 | model = Section
62 | form_class = SectionForm
63 | template_name = "teachers/add_section.html"
64 |
65 | def form_valid(self, form):
66 | form.instance.classroom = get_object_or_404(Classroom, code=self.kwargs['code'])
67 | return super().form_valid(form)
68 |
69 | def get_success_url(self):
70 | return reverse('teachers:classroom_detail', kwargs={'code':self.kwargs['code']})
71 |
72 | class SectionDeleteView(DeleteView):
73 | model = Section
74 | template_name = "teachers/delete_section.html"
75 |
76 | def get_success_url(self):
77 | return reverse('teachers:classroom_detail', kwargs={'code':self.object.classroom.code})
78 |
79 | def ResourcesView(request, pk):
80 | context_dict = {}
81 | section = get_object_or_404(Section, pk=pk)
82 | print(section)
83 | context_dict['section'] = section
84 | return render(request, 'teachers/resources.html', context=context_dict)
85 |
86 | class ResourceCreateView(CreateView):
87 | model = Resource
88 | form_class = ResourceForm
89 | template_name = "teachers/add_resource.html"
90 |
91 | def form_valid(self, form):
92 | form.instance.section = get_object_or_404(Section, pk=self.kwargs['pk'])
93 | return super().form_valid(form)
94 |
95 | def get_success_url(self):
96 | return reverse('teachers:resources', kwargs={'pk':self.kwargs['pk']})
97 |
98 | class ResourceDeleteView(DeleteView):
99 | model = Resource
100 | template_name = "teachers/delete_resource.html"
101 |
102 | def get_success_url(self):
103 | return reverse('teachers:resources', kwargs={'pk':self.object.section.pk})
104 |
105 |
106 | def AssignmentsView(request, pk):
107 | context_dict = {}
108 | section = get_object_or_404(Section, pk=pk)
109 | print(section)
110 | context_dict['section'] = section
111 | return render(request, 'teachers/assignments.html', context=context_dict)
112 |
113 |
114 | class AssignmentCreateView(CreateView):
115 | model = Assignment
116 | form_class = AssignmentForm
117 | template_name = "teachers/add_assignment.html"
118 |
119 | def form_valid(self, form):
120 | form.instance.section = get_object_or_404(Section, pk=self.kwargs['pk'])
121 | return super().form_valid(form)
122 |
123 | def get_success_url(self):
124 | return reverse('teachers:assignments', kwargs={'pk':self.kwargs['pk']})
125 |
126 | class AssignmentDeleteView(DeleteView):
127 | model = Assignment
128 | template_name = "teachers/delete_assignment.html"
129 |
130 | def get_success_url(self):
131 | return reverse('teachers:assignments', kwargs={'pk':self.object.section.pk})
132 |
133 | def AssignmentSubmissionsView(request,pk):
134 | context_dict = {}
135 | assignment = Assignment.objects.get(pk=pk)
136 |
137 | context_dict['assignment'] = assignment
138 | context_dict['assignment_submissions'] = AssignmentSubmission.objects.filter(assignment=assignment)
139 |
140 | return render(request, template_name='teachers/submissions.html', context=context_dict)
141 |
142 | def ClassroomStudentsView(request,code):
143 | context_dict = {}
144 | classroom = get_object_or_404(Classroom, code=code)
145 |
146 | context_dict['classroom'] = classroom
147 | context_dict['classroom_students'] = classroom.students.all()
148 |
149 | return render(request, template_name='teachers/classroomstudents.html', context=context_dict)
150 |
151 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode/launch.json
2 | accounts/__pycache__/__init__.cpython-39.pyc
3 | accounts/__pycache__/admin.cpython-39.pyc
4 | accounts/__pycache__/apps.cpython-39.pyc
5 | accounts/__pycache__/forms.cpython-39.pyc
6 | accounts/__pycache__/models.cpython-39.pyc
7 | accounts/__pycache__/passtests.cpython-39.pyc
8 | accounts/__pycache__/urls.cpython-39.pyc
9 | accounts/__pycache__/views.cpython-39.pyc
10 | accounts/migrations/__pycache__/__init__.cpython-39.pyc
11 | accounts/migrations/__pycache__/0001_initial.cpython-39.pyc
12 | accounts/migrations/__pycache__/0002_alter_student_current_class.cpython-39.pyc
13 | accounts/migrations/__pycache__/0003_alter_class_year.cpython-39.pyc
14 | accounts/migrations/__pycache__/0004_alter_student_classrooms.cpython-39.pyc
15 | accounts/migrations/__pycache__/0005_alter_classroom_code.cpython-39.pyc
16 | accounts/migrations/__pycache__/0006_auto_20220319_0130.cpython-39.pyc
17 | accounts/migrations/__pycache__/0007_auto_20220402_1009.cpython-39.pyc
18 | accounts/migrations/__pycache__/0008_alter_resource_title.cpython-39.pyc
19 | accounts/migrations/__pycache__/0009_alter_assignmentsubmission_file.cpython-39.pyc
20 | accounts/migrations/__init__.py
21 | accounts/migrations/0001_initial.py
22 | accounts/migrations/0002_alter_student_current_class.py
23 | accounts/migrations/0003_alter_class_year.py
24 | accounts/migrations/0004_alter_student_classrooms.py
25 | accounts/migrations/0005_alter_classroom_code.py
26 | accounts/migrations/0006_auto_20220319_0130.py
27 | accounts/migrations/0007_auto_20220402_1009.py
28 | accounts/migrations/0008_alter_resource_title.py
29 | accounts/migrations/0009_alter_assignmentsubmission_file.py
30 | media/files/assignment_submission/55zrrva4/chapter-1-asasa/assignment-1/st1/pdf/qa-question-bankpdf/qa-question-bank.pdf
31 | media/files/assignment_submission/55zrrva4/chapter-1-asasa/assignment-1/st1/svg/digitourssvg/digitours.svg
32 | media/files/assignment_submission/pmnz4czl/virtual-machines-cloud-computing/exp-1/rishim/pdf/generated-1pdf/generated-1_JYTiAGf.pdf
33 | media/files/assignment_submission/pmnz4czl/virtual-machines-cloud-computing/exp-1/rishim/pdf/generated-1pdf/generated-1.pdf
34 | media/files/assignment_submission/yhy76bt9/ip-1-ip/assip1/st1/pdf/assignment-front-page-mc-ass-1-n-2pdf/assignment-front-page-mc-ass-1-n-2_DgqOZpC.pdf
35 | media/files/assignment_submission/yhy76bt9/ip-1-ip/assip1/st1/pdf/assignment-front-page-mc-ass-1-n-2pdf/assignment-front-page-mc-ass-1-n-2_mzt8neC.pdf
36 | media/files/assignment_submission/yhy76bt9/ip-1-ip/assip1/st1/pdf/assignment-front-page-mc-ass-1-n-2pdf/assignment-front-page-mc-ass-1-n-2.pdf
37 | media/files/assignment_submission/yhy76bt9/ip-1-ip/assip1/st1/pdf/scan-2022-04-01-1107pdf/scan-2022-04-01-1107_xeHfnRb.pdf
38 | media/files/assignment_submission/yhy76bt9/ip-1-ip/assip1/st1/pdf/scan-2022-04-01-1107pdf/scan-2022-04-01-1107.pdf
39 | media/files/assignment_submission/yhy76bt9/ip-1-ip/assip1/st1/png/screenshot-2022-04-11-at-10-00-57-absolute-loaderpng/screenshot-2022-04-11-at-10-00-57-absolute-loader.png
40 | media/files/assignment_submission/yhy76bt9/ip-1-ip/new-assignment-2/st1/pdf/system-programming-and-compiler-construction-spccpdf/system-programming-and-compiler-construction-spcc_8cYNChI.pdf
41 | media/files/assignment_submission/yhy76bt9/ip-1-ip/new-assignment-2/st1/pdf/system-programming-and-compiler-construction-spccpdf/system-programming-and-compiler-construction-spcc.pdf
42 | media/files/assignment_submission/yhy76bt9/ip-1-ip/new-assignment-2/st1/png/screenshot-2022-04-11-at-10-00-57-absolute-loaderpng/screenshot-2022-04-11-at-10-00-57-absolute-loader_RqDjqxg.png
43 | media/files/assignment_submission/yhy76bt9/ip-1-ip/new-assignment-2/st1/png/screenshot-2022-04-11-at-10-00-57-absolute-loaderpng/screenshot-2022-04-11-at-10-00-57-absolute-loader.png
44 | media/files/assignments/55zrrva4/chapter-1-asasa/assignment-1/2022-04-02-0912031605740000/docx/expt-7docx/expt-7.docx
45 | media/files/assignments/55zrrva4/chapter-1-asasa/assignment1/2022-04-02-0617203783230000/docx/expt-7docx/expt-7.docx
46 | media/files/assignments/55zrrva4/chapter-1-asasa/assignment1/2022-04-02-0618074343560000/docx/expt-7docx/expt-7.docx
47 | media/files/assignments/55zrrva4/chapter-1-asasa/assignment2/2022-04-02-0618581688390000/pptx/team-meraki-sih-1-1pptx/team-meraki-sih-1-1.pptx
48 | media/files/assignments/55zrrva4/chapter-1-asasa/assignment2/2022-04-21-1123373444610000/pdf/generated-1pdf/generated-1.pdf
49 | media/files/assignments/pmnz4czl/virtual-machines-cloud-computing/exp-1/2022-04-21-1336347187860000/conf/de6a1_ch_wgconf/de6a1_ch_wg.conf
50 | media/files/assignments/yhy76bt9/ip-1-ip/new-assignment-2/2022-04-05-1805032091080000/png/2022-04-02_19-25-07-935-msedge_ucws2urw5kpng/2022-04-02_19-25-07-935-msedge_ucws2urw5k.png
51 | media/files/resources/55zrrva4/chapter-1-asasa/res1/2022-03-30-1824265869670000/json/widget_statejson/json.json
52 | media/files/resources/55zrrva4/chapter-1-asasa/res1/2022-03-30-1824265869670000/pdf/studentpdf.pdf
53 | media/files/resources/55zrrva4/chapter-1-asasa/res1/2022-04-01-1855123282940000/docx/css_lab_manualdocx/css_lab_manualdocx.docx
54 | media/files/resources/55zrrva4/chapter-1-asasa/res2/2022-03-30-1824462272620000/pdf/gopalakrishnan2017pdf/gopalakrishnan2017pdf.pdf
55 | media/files/resources/55zrrva4/chapter-1-asasa/res2/2022-03-30-1824462272620000/png/workflowpng/png.png
56 | media/files/resources/55zrrva4/chapter-1-asasa/res2/2022-03-30-1824462272620000/pptx/team-meraki-sihpptx/pptx.pptx
57 | media/files/resources/55zrrva4/chapter-1-asasa/resource-3/2022-04-01-1906412724830000/pdf/rishi_mule_resumepdf/rishi_mule_resume.pdf
58 | media/files/resources/55zrrva4/chapter-1-asasa/resource-book/2022-04-02-0742252706380000/pdf/hr_employee_attrition_analysis1pdf/hr_employee_attrition_analysis1.pdf
59 | media/files/resources/55zrrva4/section-3-asasa/pdf/2022-04-02-0915185818980000/docx/css_lab_manualdocx/css_lab_manual.docx
60 | media/files/resources/pmnz4czl/virtual-machines-cloud-computing/software-links/2022-04-21-1335299711850000/pdf/generated-1pdf/generated-1.pdf
61 | media/files/resources/yhy76bt9/ip-1-ip/resource-no-1/2022-04-05-1541339433010000/png/2022-03-28_10-17-21-044-chrome_umjivy9tgmpng/2022-03-28_10-17-21-044-chrome_umjivy9tgm.png
62 | mylms/__pycache__/__init__.cpython-39.pyc
63 | mylms/__pycache__/settings.cpython-39.pyc
64 | mylms/__pycache__/urls.cpython-39.pyc
65 | mylms/__pycache__/views.cpython-39.pyc
66 | mylms/__pycache__/wsgi.cpython-39.pyc
67 | students/__pycache__/__init__.cpython-39.pyc
68 | students/__pycache__/admin.cpython-39.pyc
69 | students/__pycache__/apps.cpython-39.pyc
70 | students/__pycache__/forms.cpython-39.pyc
71 | students/__pycache__/models.cpython-39.pyc
72 | students/__pycache__/urls.cpython-39.pyc
73 | students/__pycache__/views.cpython-39.pyc
74 | students/migrations/__init__.py
75 | students/migrations/__pycache__/__init__.cpython-39.pyc
76 | teachers/migrations/__init__.py
77 | teachers/migrations/__pycache__/__init__.cpython-39.pyc
78 | teachers/__pycache__/__init__.cpython-39.pyc
79 | teachers/__pycache__/admin.cpython-39.pyc
80 | teachers/__pycache__/apps.cpython-39.pyc
81 | teachers/__pycache__/forms.cpython-39.pyc
82 | teachers/__pycache__/models.cpython-39.pyc
83 | teachers/__pycache__/urls.cpython-39.pyc
84 | teachers/__pycache__/views.cpython-39.pyc
85 | mylms/.env
86 | mylms/__pycache__/
87 | .gitignore
88 |
--------------------------------------------------------------------------------
/students/templates/students/base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}Student Dashboard{% endblock title %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
40 |
41 |
42 |
43 |
44 |
47 |
57 |
58 |
59 |
60 |
72 |
73 |
74 |
75 |
90 |
91 |
92 |
93 |
94 |
95 | {% block content %}
96 |
97 | {% endblock content %}
98 |
99 |
100 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/teachers/templates/teachers/base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
7 | {% block title %}Base{% endblock title %}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
40 |
41 |
42 |
43 |
44 |
47 |
57 |
58 |
59 |
60 |
72 |
73 |
74 |
75 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | {% block content %}
99 |
100 |
101 | {% endblock content %}
102 |
103 |
104 |
105 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/accounts/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import AbstractUser
3 | from django.forms import CharField
4 | from django.urls import reverse
5 | from django.utils import timezone, text
6 | from django.core.validators import MaxValueValidator, MinValueValidator
7 | import os
8 | import datetime
9 | import random
10 | import string
11 |
12 |
13 | def _(something):
14 | return something
15 |
16 |
17 | class User(AbstractUser):
18 | is_student = models.BooleanField('student status', default=False)
19 | is_teacher = models.BooleanField('teacher status', default=False)
20 |
21 |
22 | class Stream(models.Model):
23 |
24 | name = models.CharField(
25 | _("Stream Name"),
26 | max_length=250,
27 | blank=False,
28 | unique=True)
29 |
30 | class Meta:
31 | verbose_name = _("stream")
32 | verbose_name_plural = _("streams")
33 |
34 | def __str__(self):
35 | return self.name
36 |
37 | def get_absolute_url(self):
38 | return reverse("stream_detail", kwargs={"pk": self.pk})
39 |
40 |
41 | class Class(models.Model):
42 |
43 | stream = models.ForeignKey(
44 | "Stream",
45 | on_delete=models.CASCADE,
46 | related_name="classes",
47 | blank=False)
48 |
49 | name = models.CharField(
50 | _("Class Name"),
51 | max_length=250,
52 | blank=False,
53 | unique=True)
54 |
55 | semester = models.PositiveIntegerField(
56 | blank=False,
57 | default=1,
58 | validators=[
59 | MaxValueValidator(8),
60 | MinValueValidator(1)
61 | ]
62 | )
63 |
64 | year = models.PositiveIntegerField(
65 | blank=False,
66 | default=2022,
67 | validators=[
68 | MaxValueValidator(2030),
69 | MinValueValidator(2015)
70 | ]
71 | )
72 |
73 | class Meta:
74 | verbose_name = _("class")
75 | verbose_name_plural = _("classes")
76 |
77 | def __str__(self):
78 | return self.name
79 |
80 | def get_absolute_url(self):
81 | return reverse("class_detail", kwargs={"pk": self.pk})
82 |
83 |
84 | class Teacher(models.Model):
85 |
86 | user = models.OneToOneField(
87 | User,
88 | on_delete=models.CASCADE,
89 | primary_key=True,
90 | related_name='teachers')
91 |
92 | stream = models.ForeignKey(
93 | "Stream",
94 | on_delete=models.CASCADE,
95 | related_name="teachers",
96 | blank=False)
97 |
98 | class Meta:
99 | verbose_name = _("teacher")
100 | verbose_name_plural = _("teachers")
101 |
102 | def __str__(self):
103 | return self.user.username
104 |
105 | def get_absolute_url(self):
106 | return reverse("teacher_detail", kwargs={"pk": self.pk})
107 |
108 | def random_string(size=8, chars=string.ascii_uppercase + string.digits):
109 | return ''.join(random.choice(chars) for _ in range(size))
110 |
111 | class Classroom(models.Model):
112 |
113 | teacher = models.ForeignKey(
114 | "Teacher",
115 | on_delete=models.CASCADE,
116 | blank=False,
117 | related_name='classrooms')
118 |
119 | subject = models.CharField(
120 | _("Subject Name"),
121 | max_length=50,
122 | blank=False)
123 |
124 | code = models.SlugField(
125 | _("Subject Code"),
126 | max_length=10,
127 | default=random_string,
128 | unique=True)
129 |
130 | semester = models.PositiveIntegerField(
131 | blank=False,
132 | default=1,
133 | validators=[
134 | MaxValueValidator(8),
135 | MinValueValidator(1)
136 | ]
137 | )
138 |
139 | created_timestamp = models.DateTimeField(
140 | default=timezone.now,
141 | editable=False)
142 |
143 | class Meta:
144 | verbose_name = _("Classroom")
145 | verbose_name_plural = _("Classrooms")
146 |
147 | def __str__(self):
148 | return self.code
149 |
150 | def get_absolute_url(self):
151 | return reverse("Classroom_detail", kwargs={"pk": self.pk})
152 |
153 |
154 | class Student(models.Model):
155 |
156 | user = models.OneToOneField(
157 | User,
158 | on_delete=models.CASCADE,
159 | primary_key=True,
160 | related_name='students')
161 |
162 | current_class = models.ForeignKey(
163 | "Class",
164 | verbose_name=_("Class"),
165 | on_delete=models.CASCADE,
166 | blank=False,
167 | related_name='students')
168 |
169 | roll_no = models.PositiveIntegerField(
170 | _("Roll No."),
171 | blank=False)
172 |
173 | student_id = models.CharField(
174 | _("Student ID"),
175 | max_length=50,
176 | blank=False,
177 | unique=True)
178 |
179 | classrooms = models.ManyToManyField("Classroom", related_name='students', blank=True)
180 |
181 | class Meta:
182 | verbose_name = _("student")
183 | verbose_name_plural = _("students")
184 | unique_together = ('roll_no', 'current_class')
185 |
186 | def __str__(self):
187 | return self.user.username
188 |
189 | def get_absolute_url(self):
190 | return reverse("Student_detail", kwargs={"pk": self.pk})
191 |
192 |
193 | class Section(models.Model):
194 |
195 | classroom = models.ForeignKey(
196 | "Classroom",
197 | on_delete=models.CASCADE,
198 | blank=False,
199 | related_name='sections')
200 |
201 | title = models.CharField(
202 | _("section title"),
203 | max_length=150)
204 |
205 | created_timestamp = models.DateTimeField(
206 | default=timezone.now,
207 | editable=False
208 | )
209 |
210 | class Meta:
211 | verbose_name = _("section")
212 | verbose_name_plural = _("sections")
213 |
214 | def __str__(self):
215 | return f'{self.title} -> {self.classroom.subject}'
216 |
217 | def get_absolute_url(self):
218 | return reverse("section_detail", kwargs={"pk": self.pk})
219 |
220 |
221 | def resource_rename_upload_file(instance, filename):
222 | ogfilename = filename.rsplit('.', 1)[0]
223 | ext = filename.split('.')[-1]
224 | filename = f'{text.slugify(instance.section.classroom)}/{text.slugify(instance.section)}/{text.slugify(instance.title)}/{text.slugify(instance.created_timestamp)}/{text.slugify(ext)}/{text.slugify(filename)}/{text.slugify(ogfilename)}.{text.slugify(ext)}'
225 | print(filename)
226 | return os.path.join('files/resources/', filename)
227 |
228 |
229 | class Resource(models.Model):
230 |
231 | title = models.CharField(
232 | _("resource title"),
233 | max_length=150)
234 |
235 | created_timestamp = models.DateTimeField(
236 | default=timezone.now,
237 | editable=False
238 | )
239 |
240 | text = models.TextField()
241 |
242 | section = models.ForeignKey(
243 | "Section",
244 | on_delete=models.CASCADE,
245 | related_name='resources',
246 | blank=False)
247 |
248 | file = models.FileField(
249 | upload_to=resource_rename_upload_file,
250 | blank=True,
251 | max_length=999)
252 |
253 | class Meta:
254 | verbose_name = _("resource")
255 | verbose_name_plural = _("resources")
256 |
257 | def __str__(self):
258 | return self.title
259 |
260 | def get_absolute_url(self):
261 | return reverse("resource_detail", kwargs={"pk": self.pk})
262 |
263 |
264 | def assignment_rename_upload_file(instance, filename):
265 | ogfilename = filename.rsplit('.', 1)[0]
266 | ext = filename.split('.')[-1]
267 | filename = f'{text.slugify(instance.section.classroom)}/{text.slugify(instance.section)}/{text.slugify(instance.title)}/{text.slugify(instance.created_timestamp)}/{text.slugify(ext)}/{text.slugify(filename)}/{text.slugify(ogfilename)}.{text.slugify(ext)}'
268 | print(filename)
269 | return os.path.join('files/assignments/', filename)
270 |
271 |
272 | class Assignment(models.Model):
273 |
274 | title = models.CharField(
275 | _("section title"),
276 | max_length=150)
277 |
278 | section = models.ForeignKey(
279 | "Section",
280 | on_delete=models.CASCADE,
281 | related_name='assignments',
282 | blank=False)
283 |
284 | text = models.TextField()
285 |
286 | created_timestamp = models.DateTimeField(
287 | default=timezone.now,
288 | editable=False
289 | )
290 |
291 | deadline = models.DateTimeField(
292 | default=timezone.now,
293 | blank=False
294 | )
295 |
296 | file = models.FileField(
297 | upload_to=assignment_rename_upload_file,
298 | blank=True,
299 | max_length=999)
300 |
301 | class Meta:
302 | verbose_name = _("assignment")
303 | verbose_name_plural = _("assignments")
304 |
305 | def __str__(self):
306 | return self.title
307 |
308 | def get_absolute_url(self):
309 | return reverse("assignment_detail", kwargs={"pk": self.pk})
310 |
311 |
312 | def submission_rename_upload_file(instance, filename):
313 | ogfilename = filename.rsplit('.', 1)[0]
314 | ext = filename.split('.')[-1]
315 | filename = f'{text.slugify(instance.assignment.section.classroom)}/{text.slugify(instance.assignment.section)}/{text.slugify(instance.assignment.title)}/{text.slugify(instance.student)}/{text.slugify(ext)}/{text.slugify(filename)}/{text.slugify(ogfilename)}.{text.slugify(ext)}'
316 | print(filename)
317 | return os.path.join('files/assignment_submission/', filename)
318 |
319 |
320 |
321 | class AssignmentSubmission(models.Model):
322 |
323 | student = models.ForeignKey(
324 | "Student",
325 | verbose_name=_("Submitted by"),
326 | on_delete=models.CASCADE,
327 | related_name='assignment_submissions',
328 | blank=False)
329 |
330 | assignment = models.ForeignKey(
331 | "Assignment",
332 | on_delete=models.CASCADE,
333 | related_name='assignment_submissions',
334 | blank=False)
335 |
336 | file = models.FileField(
337 | upload_to=submission_rename_upload_file,
338 | blank=False,
339 | max_length=999)
340 |
341 | submission_timestamp = models.DateTimeField(
342 | default=timezone.now,
343 | editable=False
344 | )
345 |
346 | class Meta:
347 | verbose_name = _("assignmentsubmission")
348 | verbose_name_plural = _("assignmentsubmissions")
349 | unique_together = ('student', 'assignment')
350 |
351 | def __str__(self):
352 | return f'{self.assignment} -> {self.student}'
353 |
354 | def get_absolute_url(self):
355 | return reverse("assignmentsubmission_detail", kwargs={"pk": self.pk})
356 |
357 |
--------------------------------------------------------------------------------