├── .dockerignore
├── .github
└── workflows
│ └── fortify.yml
├── .gitignore
├── .vscode
└── settings.json
├── Dockerfile
├── Pipfile
├── Pipfile.lock
├── Procfile
├── README.md
├── accounts
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── managers.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_alter_baseaccount_email.py
│ ├── 0003_alter_baseaccount_contact_no_and_more.py
│ ├── 0004_alter_baseaccount_options.py
│ └── __init__.py
├── models.py
├── serializers.py
├── tests.py
├── urls.py
└── views.py
├── class_diagrams
├── classroomAPI_class_diagram.png
├── img_3.png
├── img_accounts.png
└── img_classroom.png
├── classroom
├── __init__.py
├── admin.py
├── apps.py
├── constants.py
├── managers.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_allowedteacher.py
│ ├── 0003_teacher.py
│ ├── 0004_classroom.py
│ ├── 0005_teacher_classroom.py
│ ├── 0006_semester.py
│ ├── 0007_student.py
│ ├── 0008_allowedstudents.py
│ ├── 0009_subject.py
│ ├── 0010_announcement.py
│ ├── 0011_notes.py
│ ├── 0012_notesattachmentfile.py
│ ├── 0013_alter_college_allowed_teacher_list.py
│ ├── 0014_remove_teacher_classroom_classroom_teachers.py
│ ├── 0015_alter_teacher_user.py
│ ├── 0016_alter_classroom_teachers.py
│ ├── 0017_allowedteacherclassroomlevel.py
│ ├── 0018_alter_classroom_teachers.py
│ ├── 0019_alter_notesattachmentfile_title.py
│ ├── 0020_alter_notesattachmentfile_file_path.py
│ ├── 0021_assignment.py
│ ├── 0022_alter_assignment_options_alter_assignment_due_time.py
│ ├── 0023_alter_assignment_options_assignment_alloted_marks_and_more.py
│ ├── 0024_delete_assignment.py
│ ├── 0025_collegedba.py
│ ├── 0026_alter_allowedteacher_email.py
│ ├── 0027_allowedcollegedba.py
│ ├── 0028_alter_collegedba_college.py
│ ├── 0029_alter_college_allowed_teacher_list.py
│ ├── 0030_college_allowed_dba_list.py
│ ├── 0031_college_owner_email_id_collegedba_is_owner.py
│ ├── 0032_alter_collegedba_college.py
│ ├── 0033_alter_college_allowed_dba_list.py
│ ├── 0034_alter_classroom_slug.py
│ ├── 0035_alter_classroom_allowed_student_list_and_more.py
│ ├── 0036_college_stream_list_stream.py
│ ├── 0037_alter_stream_options_alter_subject_credit_points.py
│ ├── 0038_alter_subject_slug.py
│ ├── 0039_alter_subject_slug.py
│ ├── 0040_assignment.py
│ ├── 0041_alter_assignment_due_time_submittedassignment.py
│ ├── 0042_alter_assignment_due_date_alter_assignment_due_time.py
│ ├── 0043_rename_submittedassignment_assignmentsubmission.py
│ ├── 0044_remove_assignment_given_by_and_more.py
│ ├── 0045_alter_college_options.py
│ ├── 0046_alter_subject_options.py
│ ├── 0047_alter_stream_unique_together.py
│ ├── 0048_alter_allowedstudents_unique_together_and_more.py
│ ├── 0049_alter_classroom_section.py
│ └── __init__.py
├── model.py
├── models
│ ├── __init__.py
│ ├── announcement.py
│ ├── assignment.py
│ ├── classroom.py
│ ├── college.py
│ ├── college_dba.py
│ ├── imports.py
│ ├── notes.py
│ ├── student.py
│ ├── subject.py
│ └── teacher.py
├── routers
│ ├── dba_urls.py
│ ├── students_urls.py
│ └── teacher_urls.py
├── serializers
│ ├── __init__.py
│ ├── classroom.py
│ ├── college_dba.py
│ ├── student.py
│ ├── teacher.py
│ └── usertype.py
├── signals
│ ├── __init__.py
│ ├── classroom_handlers.py
│ ├── college_handlers.py
│ ├── common_imports.py
│ ├── dba_handlers.py
│ ├── handlers.py
│ ├── profile_handlers.py
│ ├── teacher_classroom_handlers.py
│ └── user_handlers.py
├── tasks.py
├── tests.py
├── urls.py
├── validators.py
└── views
│ ├── __init__.py
│ ├── college_dba_view.py
│ ├── student_view.py
│ ├── teacher_view.py
│ └── usertype_view.py
├── core
├── __init__.py
├── asgi.py
├── celery.py
├── mail.py
├── settings
│ ├── common.py
│ ├── dev.py
│ └── prod.py
├── swagger_schema.py
├── urls.py
└── wsgi.py
├── docker-compose.yml
├── docker-entrypoint.sh
├── docs
└── scripts.md
├── manage.py
├── readme
├── BackendAPI_Doc.jpeg
├── CreateCollegePage.jpeg
├── DjangoADMIN.jpeg
├── Docker.png
├── HomePage.jpeg
├── LogInPage.jpeg
├── Major Project Presentation.pptx
├── Major Project SEM_4 Doc Final Group.pdf
├── SignUpPage.jpeg
├── SubjectAddByStudent.jpeg
├── celery.png
├── djangoIcon.png
├── drf.png
├── python.png
├── redis_icon.png
├── smtp.png
└── whole_sw_review.mp4
├── requirements.txt
└── wait-for-it.sh
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/migrations/*
--------------------------------------------------------------------------------
/.github/workflows/fortify.yml:
--------------------------------------------------------------------------------
1 | # This workflow uses actions that are not certified by GitHub.
2 | # They are provided by a third-party and are governed by
3 | # separate terms of service, privacy policy, and support
4 | # documentation.
5 |
6 | ################################################################################################################################################
7 | # Fortify lets you build secure software fast with an appsec platform that automates testing throughout the DevSecOps pipeline. Fortify static,#
8 | # dynamic, interactive, and runtime security testing is available on premises or as a service. To learn more about Fortify, start a free trial #
9 | # or contact our sales team, visit microfocus.com/appsecurity. #
10 | # #
11 | # Use this workflow template as a basis for integrating Fortify on Demand Static Application Security Testing(SAST) into your GitHub workflows.#
12 | # This template demonstrates the steps to prepare the code+dependencies, initiate a scan, download results once complete and import into #
13 | # GitHub Security Code Scanning Alerts. Existing customers should review inputs and environment variables below to configure scanning against #
14 | # an existing application in your Fortify on Demand tenant. Additional information is available in the comments throughout the workflow, the #
15 | # documentation for the Fortify actions used, and the Fortify on Demand / ScanCentral Client product documentation. If you need additional #
16 | # assistance with configuration, feel free to create a help ticket in the Fortify on Demand portal. #
17 | ################################################################################################################################################
18 |
19 | name: Fortify on Demand Scan
20 |
21 | # TODO: Customize trigger events based on your DevSecOps processes and typical FoD SAST scan time
22 | on:
23 | workflow_dispatch:
24 | push:
25 | branches: [ "main" ]
26 | schedule:
27 | - cron: '16 3 * * 0'
28 |
29 | jobs:
30 | FoD-SAST-Scan:
31 | # Use the appropriate runner for building your source code.
32 | # TODO: Use a Windows runner for .NET projects that use msbuild. Additional changes to RUN commands will be required to switch to Windows syntax.
33 | runs-on: ubuntu-latest
34 | permissions:
35 | actions: read
36 | contents: read
37 | security-events: write
38 |
39 | steps:
40 | # Check out source code
41 | - name: Check Out Source Code
42 | uses: actions/checkout@v3
43 |
44 | # Java is required to run the various Fortify utilities.
45 | # When scanning a Java application, please use the appropriate Java version for building your application.
46 | - name: Setup Java
47 | uses: actions/setup-java@v3
48 | with:
49 | java-version: 8
50 | distribution: 'temurin'
51 |
52 | # Prepare source+dependencies for upload. The default example is for a Maven project that uses pom.xml.
53 | # TODO: Update PACKAGE_OPTS based on the ScanCentral Client documentation for your project's included tech stack(s). Helpful hints:
54 | # ScanCentral Client will download dependencies for maven (-bt mvn) and gradle (-bt gradle).
55 | # ScanCentral Client can download dependencies for msbuild projects (-bt msbuild); however, you must convert the workflow to use a Windows runner.
56 | # ScanCentral has additional options that should be set for PHP and Python projects
57 | # For other build tools, add your build commands to download necessary dependencies and prepare according to Fortify on Demand Packaging documentation.
58 | # ScanCentral Client documentation is located at https://www.microfocus.com/documentation/fortify-software-security-center/
59 | - name: Download Fortify ScanCentral Client
60 | uses: fortify/gha-setup-scancentral-client@5b7382f8234fb9840958c49d5f32ae854115f9f3
61 | - name: Package Code + Dependencies
62 | run: scancentral package $PACKAGE_OPTS -o package.zip
63 | env:
64 | PACKAGE_OPTS: "-bt mvn"
65 |
66 | # Start Fortify on Demand SAST scan and wait until results complete. For more information on FoDUploader commands, see https://github.com/fod-dev/fod-uploader-java
67 | # TODO: Update ENV variables for your application and create the necessary GitHub Secrets. Helpful hints:
68 | # Credentials and release ID should be obtained from your FoD tenant (either Personal Access Token or API Key can be used).
69 | # Automated Audit preference should be configured for the release's Static Scan Settings in the Fortify on Demand portal.
70 | - name: Download Fortify on Demand Universal CI Tool
71 | uses: fortify/gha-setup-fod-uploader@6e6bb8a33cb476e240929fa8ebc739ff110e7433
72 | - name: Perform SAST Scan
73 | run: java -jar $FOD_UPLOAD_JAR -z package.zip -aurl $FOD_API_URL -purl $FOD_URL -rid "$FOD_RELEASE_ID" -tc "$FOD_TENANT" -uc "$FOD_USER" "$FOD_PAT" $FOD_UPLOADER_OPTS -n "$FOD_UPLOADER_NOTES"
74 | env:
75 | FOD_URL: "https://ams.fortify.com/"
76 | FOD_API_URL: "https://api.ams.fortify.com/"
77 | FOD_TENANT: ${{ secrets.FOD_TENANT }}
78 | FOD_USER: ${{ secrets.FOD_USER }}
79 | FOD_PAT: ${{ secrets.FOD_PAT }}
80 | FOD_RELEASE_ID: ${{ secrets.FOD_RELEASE_ID }}
81 | FOD_UPLOADER_OPTS: "-ep 2 -pp 0 -I 1 -apf"
82 | FOD_UPLOADER_NOTES: 'Triggered by GitHub Actions (${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})'
83 |
84 | # Once scan completes, pull SAST issues from Fortify on Demand and generate SARIF output.
85 | - name: Export results to GitHub-optimized SARIF
86 | uses: fortify/gha-export-vulnerabilities@fcb374411cff9809028c911dabb8b57dbdae623b
87 | with:
88 | fod_base_url: "https://ams.fortify.com/"
89 | fod_tenant: ${{ secrets.FOD_TENANT }}
90 | fod_user: ${{ secrets.FOD_USER }}
91 | fod_password: ${{ secrets.FOD_PAT }}
92 | fod_release_id: ${{ secrets.FOD_RELEASE_ID }}
93 |
94 | # Import Fortify on Demand results to GitHub Security Code Scanning
95 | - name: Import Results
96 | uses: github/codeql-action/upload-sarif@v2
97 | with:
98 | sarif_file: ./gh-fortify-sast.sarif
99 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | __pycache__
3 | /__pycache__
4 | tokens
5 | /static/
6 | /media/
7 | /**/teachers/
8 | /**/students/
9 | *.csv
10 | *xlsx
11 | .env
12 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.formatting.provider": "black"
3 | }
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.10.4
2 |
3 | ENV PYTHONUNBUFFERED=1
4 | RUN mkdir /app
5 | WORKDIR /app
6 |
7 | # Required to install mysqlclient with Pip
8 | RUN apt-get update \
9 | && apt-get install python3-dev default-libmysqlclient-dev gcc -y
10 | # RUN apt-get update \
11 | # && apt-get install python-dev default-libmysqlclient-dev gcc -y
12 |
13 | # RUN apk update \
14 | # && apk add --virtual build-deps gcc python3-dev musl-dev \
15 | # && apk add --no-cache mariadb-dev
16 |
17 | # Install pipenv
18 | RUN pip install --upgrade pip
19 | RUN pip install pipenv
20 |
21 | # Install application dependencies
22 | COPY Pipfile Pipfile.lock /app/
23 | # We use the --system flag so packages are installed into the system python
24 | # and not into a virtualenv. Docker containers don't need virtual environments.
25 | RUN pipenv install --system --dev --verbose --skip-lock
26 |
27 | # Copy the application files into the image
28 | COPY . /app/
29 |
30 | # Expose port 8000 on the container
31 | EXPOSE 8000
32 | EXPOSE 3306
33 |
34 | # RUN apk del build-deps
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 | django = "*"
8 | djangorestframework = "*"
9 | markdown = "*"
10 | djoser = "*"
11 | djangorestframework-simplejwt = "*"
12 | django-extensions = "*"
13 | drf-yasg = "*"
14 | django-cors-headers = "*"
15 | whitenoise = "*"
16 | gunicorn = "*"
17 | dj-database-url = "*"
18 | psycopg2 = "*"
19 | termcolor = "*"
20 | pandas = "*"
21 | openpyxl = "*"
22 | redis = "*"
23 | celery = "*"
24 | eventlet = "*"
25 | drf-nested-routers = "*"
26 | drf-writable-nested = "*"
27 |
28 | [dev-packages]
29 | black = "*"
30 | django-debug-toolbar = "*"
31 | mysqlclient = "*"
32 | waitress = "*"
33 |
34 | [requires]
35 | python_version = "3.10"
36 |
37 | [pipenv]
38 | allow_prereleases = true
39 |
40 | [scripts]
41 | dev = "docker-compose up -d"
42 | dev-show = "docker-compose up"
43 | logs = 'docker-compose logs -f web'
44 | stop = 'docker-compose down'
45 | db-update = "docker-compose run web python manage.py makemigrations"
46 | db-apply = "docker-compose run web python manage.py migrate"
47 | db-reset = "docker-compose run web python manage.py reset_db"
48 | make-admin = "docker-compose run web python manage.py createsuperuser"
49 | admin-code = 'docker-compose run web python manage.py admin_generator classroom'
50 | cmd = "docker-compose run web python manage.py shell_plus"
51 | lint = "docker-compose run web black ./"
52 |
53 | # dev = "py manage.py runserver 8000"
54 | # serve = "waitress-serve --listen=*:9000 core.wsgi:application"
55 | # db-update = "py manage.py makemigrations"
56 | # db-apply = "py manage.py migrate"
57 | # db_clear = "py manage.py reset_db"
58 | # deploy = "git push heroku main"
59 | # smtp = "docker run -it -p 5000:80 -p 2525:25 rnwood/smtp4dev"
60 | # make-admin = "py manage.py createsuperuser"
61 | # admin-code = 'py manage.py admin_generator classroom'
62 | # redis = "docker run -d -p 6379:6379 redis"
63 | # celery = "celery -A core worker -l info -P eventlet -E"
64 | # cmd = "py manage.py shell_plus"
65 | # lint = "black ./"
66 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | release: python manage.py migrate
2 | web: gunicorn core.wsgi
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Classroom LMS API (Backend)
2 |
3 | ---
4 |
5 | > #### 4 th Sem Major `Project`
6 | >
7 | > - In this Project I have developed this whole API
8 | > - This API has 5 Modules :
9 | > - Authentication
10 | > - Owner Admin
11 | > - Admin with less privileges
12 | > - Teacher
13 | > - Student
14 |
15 | ---
16 |
17 | > **API Doc**
18 | >
19 | >
20 |
21 |
22 | ---
23 |
24 | ### Contributors
25 |
26 | ---
27 |
28 | > - [Pritam Chakraborty (Backend Dev & Frontend Designer)](https://github.com/PritamChk)
29 | > - [Tathagata Das (Frontend Developer)](https://github.com/TathagataDas99/)
30 | > - [Rimi Mondal (Tester)](https://github.com/RimiDeb13)
31 |
32 | ---
33 |
34 | > #### Project Start Date : 5-Feb-2022
35 | >
36 | > ###### Coding Start Date : 26-April-2022
37 |
38 | ---
39 |
40 | ## Technology Stack
41 |
42 | ---
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | > `OS` - `Windows 10`
74 |
75 | ---
76 |
77 | # Recommended Setup
78 |
79 | ---
80 |
81 | > **Install** :
82 |
83 | 1. [VS Code](https://code.visualstudio.com/)
84 | 2.Install [Docker](https://www.docker.com/get-started/) & Run It
85 | 2. Save `docker-entrypoint.sh` & `wait-for-it.sh` with `LF` line feed.
86 | 3. use the following command for first time build:
87 |
88 | ```powershell
89 | docker-compose up --build
90 | ```
91 |
92 | 5. create super user for django admin area
93 | (one time)
94 |
95 | ```bash
96 | docker-compose run web python manage.py createsuperuser
97 | ```
98 |
99 | 6. for other commands check out the `Pipfile`
100 |
101 | ---
102 |
103 | ## Some Glimpse of Frontend :
104 |
105 | ---
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | ---
121 |
122 | ## To Know More About Frontend :
123 | > #### [Click Here [↗]](https://github.com/TathagataDas99/Classroom-Frontend)
124 |
125 | ---
126 |
--------------------------------------------------------------------------------
/accounts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/accounts/__init__.py
--------------------------------------------------------------------------------
/accounts/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.contrib import messages
3 | from django.utils.translation import ngettext
4 | from django.contrib.auth.admin import UserAdmin
5 |
6 |
7 | from .forms import *
8 | from .models import BaseAccount
9 |
10 | admin.site.site_header = "Classroom [LMS]"
11 |
12 |
13 | @admin.register(BaseAccount)
14 | class BaseAccountAdmin(UserAdmin):
15 | form = AccountChangeForm
16 | add_form = AccountCreationForm
17 | add_fieldsets = (
18 | ("Login Info", {"fields": ("email", "password1", "password2")}),
19 | (
20 | "Basic Info",
21 | {
22 | "classes": ("wide",),
23 | "fields": ["first_name", "last_name"],
24 | },
25 | ),
26 | )
27 | fieldsets = (
28 | ("Login Info", {"fields": ("id", "email", "password")}),
29 | ("Personal info", {"fields": ("first_name", "last_name", "contact_no")}),
30 | ("Permissions", {"fields": ("is_superuser", "is_staff", "is_active")}),
31 | (
32 | "Date & Time Info",
33 | {
34 | "fields": ["date_joined", "last_login"],
35 | },
36 | ),
37 | (
38 | "Group Permissions",
39 | {
40 | "fields": (
41 | "groups",
42 | "user_permissions",
43 | ),
44 | },
45 | ),
46 | )
47 | readonly_fields = (
48 | "id",
49 | "date_joined",
50 | )
51 | ordering = ("email", "first_name", "last_name")
52 | list_display = [
53 | "email",
54 | "first_name",
55 | "last_name",
56 | "is_active",
57 | "contact_no",
58 | "is_superuser",
59 | "is_staff",
60 | ]
61 | list_display_links = ["email", "first_name"]
62 | list_editable = ["is_active"]
63 | search_fields = ["email", "first_name", "last_name", "contact_no"]
64 | actions = ["make_users_active", "make_users_inactive"]
65 |
66 | @admin.action(description="make user active")
67 | def make_users_active(self, request, queryset):
68 | updated = queryset.update(is_active=True)
69 | self.message_user(
70 | request,
71 | ngettext(
72 | "%d user was successfully activated.",
73 | "%d users were successfully activated.",
74 | updated,
75 | )
76 | % updated,
77 | messages.SUCCESS,
78 | )
79 |
80 | @admin.action(description="make user inactive")
81 | def make_users_inactive(self, request, queryset):
82 | updated = queryset.update(is_active=False)
83 | self.message_user(
84 | request,
85 | ngettext(
86 | "%d user was successfully deactivated.",
87 | "%d users were successfully deactivated.",
88 | updated,
89 | )
90 | % updated,
91 | messages.SUCCESS,
92 | )
93 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/accounts/forms.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.forms import UserChangeForm, UserCreationForm
2 |
3 | from .models import BaseAccount
4 |
5 |
6 | class AccountCreationForm(UserCreationForm):
7 | class Meta:
8 | mdoel = BaseAccount
9 | fields = ("email", "first_name", "last_name")
10 |
11 |
12 | class AccountChangeForm(UserChangeForm):
13 | class Meta:
14 | mdoel = BaseAccount
15 | fields = ("email", "first_name", "last_name", "contact_no", "date_joined")
16 |
--------------------------------------------------------------------------------
/accounts/managers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import BaseUserManager
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class BaseAccountManager(BaseUserManager):
6 | def create_user(
7 | self, first_name: str, last_name: str, email, password, **extra_fields
8 | ):
9 | """
10 | Create and save a User with the given email and password.
11 | """
12 | if not email:
13 | raise ValueError(_("The Email must be set"))
14 | if not first_name:
15 | raise ValueError(_("The First Name must be set"))
16 | if not last_name:
17 | raise ValueError(_("The Last Name must be set"))
18 | if not password:
19 | raise ValueError(_("The Password must be set"))
20 | email = self.normalize_email(email)
21 | user = self.model(
22 | email=email,
23 | first_name=first_name.title(),
24 | last_name=last_name.title(),
25 | **extra_fields
26 | )
27 | user.set_password(password)
28 | user.save()
29 | return user
30 |
31 | def create_superuser(self, first_name, last_name, email, password, **extra_fields):
32 | """
33 | Create and save a SuperUser with the given email and password.
34 | """
35 | extra_fields.setdefault("is_staff", True)
36 | extra_fields.setdefault("is_superuser", True)
37 | extra_fields.setdefault("is_active", True)
38 |
39 | if extra_fields.get("is_staff") is not True:
40 | raise ValueError(_("Superuser must have is_staff=True."))
41 | if extra_fields.get("is_superuser") is not True:
42 | raise ValueError(_("Superuser must have is_superuser=True."))
43 | return self.create_user(first_name, last_name, email, password, **extra_fields)
44 |
--------------------------------------------------------------------------------
/accounts/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-04-26 19:56
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 | import django.utils.timezone
6 | import uuid
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ("auth", "0012_alter_user_first_name_max_length"),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name="BaseAccount",
20 | fields=[
21 | (
22 | "id",
23 | models.UUIDField(
24 | auto_created=True,
25 | default=uuid.uuid4,
26 | editable=False,
27 | primary_key=True,
28 | serialize=False,
29 | ),
30 | ),
31 | ("password", models.CharField(max_length=128, verbose_name="password")),
32 | (
33 | "last_login",
34 | models.DateTimeField(
35 | blank=True, null=True, verbose_name="last login"
36 | ),
37 | ),
38 | (
39 | "is_staff",
40 | models.BooleanField(
41 | default=False,
42 | help_text="Designates whether the user can log into this admin site.",
43 | verbose_name="staff status",
44 | ),
45 | ),
46 | (
47 | "is_active",
48 | models.BooleanField(
49 | default=True,
50 | help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
51 | verbose_name="active",
52 | ),
53 | ),
54 | (
55 | "date_joined",
56 | models.DateTimeField(
57 | default=django.utils.timezone.now, verbose_name="date joined"
58 | ),
59 | ),
60 | ("first_name", models.CharField(max_length=200)),
61 | ("last_name", models.CharField(max_length=200)),
62 | (
63 | "contact_no",
64 | models.CharField(
65 | blank=True,
66 | db_index=True,
67 | help_text="\n 👌🏻E.g - 9881284481\n ❌ +91 9812300122\n ❌ 09812300122\n ",
68 | max_length=15,
69 | null=True,
70 | unique=True,
71 | validators=[
72 | django.core.validators.RegexValidator(
73 | "^\\d{10}$", "Phone no should contain 10 digits"
74 | )
75 | ],
76 | verbose_name="Phone No(without country code or 0)",
77 | ),
78 | ),
79 | (
80 | "email",
81 | models.EmailField(
82 | help_text="This will be used as username for login",
83 | max_length=350,
84 | unique=True,
85 | verbose_name="Email Id",
86 | ),
87 | ),
88 | (
89 | "is_superuser",
90 | models.BooleanField(
91 | default=False,
92 | help_text="Designates whether the user can edit everything into this admin site.",
93 | verbose_name="admin status",
94 | ),
95 | ),
96 | (
97 | "groups",
98 | models.ManyToManyField(
99 | blank=True,
100 | help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
101 | related_name="user_set",
102 | related_query_name="user",
103 | to="auth.group",
104 | verbose_name="groups",
105 | ),
106 | ),
107 | (
108 | "user_permissions",
109 | models.ManyToManyField(
110 | blank=True,
111 | help_text="Specific permissions for this user.",
112 | related_name="user_set",
113 | related_query_name="user",
114 | to="auth.permission",
115 | verbose_name="user permissions",
116 | ),
117 | ),
118 | ],
119 | options={
120 | "verbose_name": "User",
121 | "verbose_name_plural": "Users",
122 | "ordering": ["first_name", "last_name"],
123 | },
124 | ),
125 | ]
126 |
--------------------------------------------------------------------------------
/accounts/migrations/0002_alter_baseaccount_email.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-04-27 03:46
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("accounts", "0001_initial"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="baseaccount",
15 | name="email",
16 | field=models.EmailField(
17 | help_text="This will be used as username for login",
18 | max_length=250,
19 | unique=True,
20 | verbose_name="Email Id",
21 | ),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/accounts/migrations/0003_alter_baseaccount_contact_no_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-04-27 03:59
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("accounts", "0002_alter_baseaccount_email"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="baseaccount",
16 | name="contact_no",
17 | field=models.CharField(
18 | blank=True,
19 | db_index=True,
20 | help_text="\n 👌🏻E.g - 9881284481\n ❌ +91 9812300122\n ❌ 09812300122\n ",
21 | max_length=15,
22 | null=True,
23 | unique=True,
24 | validators=[
25 | django.core.validators.RegexValidator(
26 | "^\\d{10}$", "Phone no should contain 10 digits"
27 | )
28 | ],
29 | verbose_name="Phone No",
30 | ),
31 | ),
32 | migrations.AlterField(
33 | model_name="baseaccount",
34 | name="is_active",
35 | field=models.BooleanField(
36 | default=False,
37 | help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
38 | verbose_name="active",
39 | ),
40 | ),
41 | ]
42 |
--------------------------------------------------------------------------------
/accounts/migrations/0004_alter_baseaccount_options.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-04-27 06:26
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("accounts", "0003_alter_baseaccount_contact_no_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="baseaccount",
15 | options={
16 | "ordering": ["first_name", "last_name", "email"],
17 | "verbose_name": "User",
18 | "verbose_name_plural": "Users",
19 | },
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/accounts/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/accounts/migrations/__init__.py
--------------------------------------------------------------------------------
/accounts/models.py:
--------------------------------------------------------------------------------
1 | from uuid import uuid4
2 |
3 | from django.contrib.auth.models import AbstractUser, PermissionsMixin
4 | from django.core.validators import RegexValidator
5 | from django.db import models
6 | from django.utils.translation import gettext_lazy as _
7 |
8 | from .managers import *
9 |
10 |
11 | class BaseAccount(AbstractUser):
12 | username = None
13 | id = models.UUIDField(
14 | primary_key=True, editable=False, auto_created=True, default=uuid4
15 | )
16 | first_name = models.CharField(max_length=200)
17 | last_name = models.CharField(max_length=200)
18 | contact_no = models.CharField(
19 | _("Phone No"),
20 | max_length=15,
21 | unique=True,
22 | db_index=True,
23 | blank=True,
24 | null=True,
25 | help_text="""
26 | 👌🏻E.g - 9881284481
27 | ❌ +91 9812300122
28 | ❌ 09812300122
29 | """,
30 | validators=[RegexValidator(r"^\d{10}$", "Phone no should contain 10 digits")],
31 | )
32 | email = models.EmailField(
33 | verbose_name="Email Id",
34 | unique=True,
35 | max_length=250,
36 | help_text="This will be used as username for login",
37 | )
38 |
39 | is_superuser = models.BooleanField(
40 | _("admin status"),
41 | default=False,
42 | help_text=_(
43 | "Designates whether the user can edit everything into this admin site."
44 | ),
45 | )
46 | is_active = models.BooleanField(
47 | _("active"),
48 | default=False,
49 | help_text=_(
50 | "Designates whether this user should be treated as active. "
51 | "Unselect this instead of deleting accounts."
52 | ),
53 | )
54 |
55 | objects = BaseAccountManager()
56 | USERNAME_FIELD = "email"
57 | REQUIRED_FIELDS = ["first_name", "last_name"]
58 |
59 | class Meta:
60 | ordering = ["first_name", "last_name", "email"]
61 | verbose_name = "User"
62 | verbose_name_plural = "Users"
63 |
--------------------------------------------------------------------------------
/accounts/serializers.py:
--------------------------------------------------------------------------------
1 | from djoser.serializers import (
2 | UserSerializer as usz,
3 | UsernameResetConfirmSerializer as uname_reset,
4 | )
5 |
6 |
7 | class UseranmeResetConfirmSerializer(uname_reset):
8 | class Meta(uname_reset.Meta):
9 | fields = ("uid", "token", "email")
10 |
11 |
12 | class CurrentUserSerializer(usz):
13 | """
14 | description: This will be returned after login authentication
15 | """
16 |
17 | class Meta(usz.Meta):
18 | fields = (
19 | "id",
20 | "email",
21 | "first_name",
22 | "last_name",
23 | "contact_no",
24 | "is_active",
25 | "date_joined",
26 | "last_login",
27 | )
28 | read_only_fields = ("id", "email", "is_active", "date_joined", "last_login")
29 |
--------------------------------------------------------------------------------
/accounts/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/accounts/urls.py:
--------------------------------------------------------------------------------
1 | """
2 | CORE URLS
3 | """
4 | from django.urls import path, include
5 | from .views import *
6 |
7 | urlpatterns = [
8 | path("", test, name="test-url"),
9 | ]
10 |
--------------------------------------------------------------------------------
/accounts/views.py:
--------------------------------------------------------------------------------
1 | from http.client import HTTPResponse
2 | from django.shortcuts import render
3 | from django.http import HttpResponse
4 |
5 |
6 | def test(request):
7 | return HttpResponse("hello")
8 |
--------------------------------------------------------------------------------
/class_diagrams/classroomAPI_class_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/class_diagrams/classroomAPI_class_diagram.png
--------------------------------------------------------------------------------
/class_diagrams/img_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/class_diagrams/img_3.png
--------------------------------------------------------------------------------
/class_diagrams/img_accounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/class_diagrams/img_accounts.png
--------------------------------------------------------------------------------
/class_diagrams/img_classroom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/class_diagrams/img_classroom.png
--------------------------------------------------------------------------------
/classroom/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/classroom/__init__.py
--------------------------------------------------------------------------------
/classroom/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ClassroomConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "classroom"
7 |
8 | def ready(self) -> None:
9 | import classroom.signals.handlers
10 |
--------------------------------------------------------------------------------
/classroom/constants.py:
--------------------------------------------------------------------------------
1 | from django.db import models as m
2 |
3 |
4 | class LEVEL_CHOICES(m.TextChoices):
5 | UnderGraduate = "Bachelors"
6 | PostGraduate = "Masters"
7 |
8 |
9 | class SECTION_CHOICES(m.TextChoices):
10 | A = "A"
11 | B = "B"
12 | C = "C"
13 | D = "D"
14 | E = "E"
15 | F = "F"
16 |
--------------------------------------------------------------------------------
/classroom/managers.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.db.models.signals import post_save
3 |
4 |
5 | class AllowedTeacherClassroomLevelManager(models.Manager):
6 | def delete(self):
7 | return super().delete()
8 |
9 | def bulk_create(self, objs, **kwargs):
10 | a = super(models.Manager, self).bulk_create(objs, **kwargs)
11 | for i in objs:
12 | post_save.send(i.__class__, instance=i, created=True)
13 | return a
14 |
--------------------------------------------------------------------------------
/classroom/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 19:34
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 | import django_extensions.db.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = []
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name="College",
17 | fields=[
18 | (
19 | "id",
20 | models.BigAutoField(
21 | auto_created=True,
22 | primary_key=True,
23 | serialize=False,
24 | verbose_name="ID",
25 | ),
26 | ),
27 | (
28 | "slug",
29 | django_extensions.db.fields.AutoSlugField(
30 | blank=True,
31 | editable=False,
32 | populate_from=["name", "state", "city"],
33 | ),
34 | ),
35 | ("name", models.CharField(max_length=255, verbose_name="College Name")),
36 | ("city", models.CharField(max_length=255, verbose_name="City")),
37 | ("state", models.CharField(max_length=255, verbose_name="State")),
38 | ("address", models.TextField(blank=True, null=True)),
39 | (
40 | "allowed_teacher_list",
41 | models.FileField(
42 | blank=True,
43 | null=True,
44 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/teachers/",
45 | validators=[
46 | django.core.validators.FileExtensionValidator(
47 | allowed_extensions=["csv", "xlsx"],
48 | message="Please Upload CSV/XLSX file only",
49 | )
50 | ],
51 | verbose_name="Upload teacher List File(.csv)",
52 | ),
53 | ),
54 | ],
55 | options={
56 | "ordering": ["name", "city", "state"],
57 | },
58 | ),
59 | ]
60 |
--------------------------------------------------------------------------------
/classroom/migrations/0002_allowedteacher.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 19:37
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0001_initial"),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name="AllowedTeacher",
16 | fields=[
17 | (
18 | "id",
19 | models.BigAutoField(
20 | auto_created=True,
21 | primary_key=True,
22 | serialize=False,
23 | verbose_name="ID",
24 | ),
25 | ),
26 | ("email", models.EmailField(max_length=255, verbose_name="Email Id")),
27 | (
28 | "college",
29 | models.ForeignKey(
30 | on_delete=django.db.models.deletion.CASCADE,
31 | related_name="allowed_teachers",
32 | to="classroom.college",
33 | ),
34 | ),
35 | ],
36 | ),
37 | ]
38 |
--------------------------------------------------------------------------------
/classroom/migrations/0003_teacher.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 19:51
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ("classroom", "0002_allowedteacher"),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name="Teacher",
18 | fields=[
19 | (
20 | "id",
21 | models.BigAutoField(
22 | auto_created=True,
23 | primary_key=True,
24 | serialize=False,
25 | verbose_name="ID",
26 | ),
27 | ),
28 | (
29 | "college",
30 | models.ForeignKey(
31 | on_delete=django.db.models.deletion.CASCADE,
32 | related_name="teachers",
33 | to="classroom.college",
34 | ),
35 | ),
36 | (
37 | "user",
38 | models.OneToOneField(
39 | blank=True,
40 | null=True,
41 | on_delete=django.db.models.deletion.CASCADE,
42 | related_name="teacher_profile",
43 | to=settings.AUTH_USER_MODEL,
44 | ),
45 | ),
46 | ],
47 | options={
48 | "ordering": ["user__first_name", "user__last_name"],
49 | },
50 | ),
51 | ]
52 |
--------------------------------------------------------------------------------
/classroom/migrations/0005_teacher_classroom.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 20:20
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0004_classroom"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="teacher",
15 | name="classroom",
16 | field=models.ManyToManyField(
17 | blank=True, related_name="teachers", to="classroom.classroom"
18 | ),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/classroom/migrations/0006_semester.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 20:31
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("classroom", "0005_teacher_classroom"),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name="Semester",
17 | fields=[
18 | (
19 | "id",
20 | models.BigAutoField(
21 | auto_created=True,
22 | primary_key=True,
23 | serialize=False,
24 | verbose_name="ID",
25 | ),
26 | ),
27 | (
28 | "sem_no",
29 | models.PositiveSmallIntegerField(
30 | validators=[
31 | django.core.validators.MinValueValidator(
32 | 1, "sem value > 0"
33 | ),
34 | django.core.validators.MaxValueValidator(
35 | 14, "sem value < 15"
36 | ),
37 | ],
38 | verbose_name="Semester No",
39 | ),
40 | ),
41 | (
42 | "is_current_sem",
43 | models.BooleanField(
44 | default=False, verbose_name="is this sem going on? "
45 | ),
46 | ),
47 | (
48 | "classroom",
49 | models.ForeignKey(
50 | on_delete=django.db.models.deletion.CASCADE,
51 | related_name="semesters",
52 | to="classroom.classroom",
53 | ),
54 | ),
55 | ],
56 | options={
57 | "ordering": ["classroom__title", "sem_no"],
58 | "unique_together": {("classroom", "sem_no")},
59 | },
60 | ),
61 | ]
62 |
--------------------------------------------------------------------------------
/classroom/migrations/0007_student.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 20:38
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ("classroom", "0006_semester"),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name="Student",
18 | fields=[
19 | (
20 | "id",
21 | models.BigAutoField(
22 | auto_created=True,
23 | primary_key=True,
24 | serialize=False,
25 | verbose_name="ID",
26 | ),
27 | ),
28 | (
29 | "university_roll",
30 | models.PositiveBigIntegerField(
31 | blank=True,
32 | help_text="Your University Roll No - (e.g. - 13071020030)",
33 | null=True,
34 | verbose_name="University Roll",
35 | ),
36 | ),
37 | (
38 | "classroom",
39 | models.ForeignKey(
40 | blank=True,
41 | null=True,
42 | on_delete=django.db.models.deletion.CASCADE,
43 | related_name="students",
44 | to="classroom.classroom",
45 | ),
46 | ),
47 | (
48 | "user",
49 | models.OneToOneField(
50 | blank=True,
51 | null=True,
52 | on_delete=django.db.models.deletion.CASCADE,
53 | related_name="student_profile",
54 | to=settings.AUTH_USER_MODEL,
55 | ),
56 | ),
57 | ],
58 | options={
59 | "ordering": ["user__first_name", "user__last_name"],
60 | },
61 | ),
62 | ]
63 |
--------------------------------------------------------------------------------
/classroom/migrations/0008_allowedstudents.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 20:48
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0007_student"),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name="AllowedStudents",
16 | fields=[
17 | (
18 | "id",
19 | models.BigAutoField(
20 | auto_created=True,
21 | primary_key=True,
22 | serialize=False,
23 | verbose_name="ID",
24 | ),
25 | ),
26 | ("email", models.EmailField(max_length=255, verbose_name="Email Id")),
27 | (
28 | "university_roll",
29 | models.PositiveBigIntegerField(
30 | help_text="Your University Roll No - (e.g. - 13071020030)",
31 | verbose_name="University Roll",
32 | ),
33 | ),
34 | (
35 | "classroom",
36 | models.ForeignKey(
37 | on_delete=django.db.models.deletion.CASCADE,
38 | related_name="allowed_students",
39 | to="classroom.classroom",
40 | ),
41 | ),
42 | ],
43 | options={
44 | "verbose_name_plural": "Allowed Students",
45 | "ordering": ["university_roll"],
46 | "unique_together": {
47 | ("university_roll", "email"),
48 | ("university_roll", "classroom"),
49 | ("classroom", "email"),
50 | },
51 | },
52 | ),
53 | ]
54 |
--------------------------------------------------------------------------------
/classroom/migrations/0009_subject.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 21:04
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import django_extensions.db.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("classroom", "0008_allowedstudents"),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name="Subject",
17 | fields=[
18 | (
19 | "id",
20 | models.BigAutoField(
21 | auto_created=True,
22 | primary_key=True,
23 | serialize=False,
24 | verbose_name="ID",
25 | ),
26 | ),
27 | (
28 | "slug",
29 | django_extensions.db.fields.AutoSlugField(
30 | blank=True,
31 | editable=False,
32 | populate_from=[
33 | "title",
34 | "semester__sem_no",
35 | "subject_type",
36 | "credit_points",
37 | "created_by",
38 | ],
39 | ),
40 | ),
41 | ("subject_code", models.CharField(max_length=20)),
42 | ("title", models.CharField(max_length=200)),
43 | (
44 | "subject_type",
45 | models.CharField(
46 | choices=[
47 | ("TH", "Theory"),
48 | ("PRC", "Practical"),
49 | ("ELC", "Elective"),
50 | ("PRJ", "Project"),
51 | ],
52 | default="TH",
53 | max_length=5,
54 | ),
55 | ),
56 | (
57 | "credit_points",
58 | models.PositiveSmallIntegerField(
59 | choices=[
60 | (1, "1"),
61 | (2, "2"),
62 | (3, "3"),
63 | (4, "4"),
64 | (5, "5"),
65 | (6, "6"),
66 | (7, "7"),
67 | (8, "8"),
68 | (9, "9"),
69 | (10, "10"),
70 | (11, "11"),
71 | (12, "12"),
72 | (13, "13"),
73 | (14, "14"),
74 | (15, "15"),
75 | ],
76 | default=1,
77 | ),
78 | ),
79 | ("created_at", models.DateField(auto_now_add=True)),
80 | (
81 | "created_by",
82 | models.ForeignKey(
83 | on_delete=django.db.models.deletion.CASCADE,
84 | related_name="subjects",
85 | to="classroom.teacher",
86 | ),
87 | ),
88 | (
89 | "semester",
90 | models.ForeignKey(
91 | on_delete=django.db.models.deletion.CASCADE,
92 | related_name="subjects",
93 | to="classroom.semester",
94 | ),
95 | ),
96 | ],
97 | options={
98 | "ordering": ["-created_at", "title", "-credit_points"],
99 | },
100 | ),
101 | ]
102 |
--------------------------------------------------------------------------------
/classroom/migrations/0010_announcement.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 21:07
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0009_subject"),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name="Announcement",
16 | fields=[
17 | (
18 | "id",
19 | models.BigAutoField(
20 | auto_created=True,
21 | primary_key=True,
22 | serialize=False,
23 | verbose_name="ID",
24 | ),
25 | ),
26 | (
27 | "heading",
28 | models.TextField(
29 | default="No Heading Given", verbose_name="Heading"
30 | ),
31 | ),
32 | (
33 | "body",
34 | models.TextField(
35 | blank=True, null=True, verbose_name="Description[Optional] "
36 | ),
37 | ),
38 | (
39 | "created_at",
40 | models.DateTimeField(auto_now_add=True, verbose_name="Created At "),
41 | ),
42 | (
43 | "updated_at",
44 | models.DateTimeField(auto_now=True, verbose_name="Updated At "),
45 | ),
46 | (
47 | "posted_by",
48 | models.ForeignKey(
49 | on_delete=django.db.models.deletion.CASCADE,
50 | related_name="announcements",
51 | to="classroom.teacher",
52 | ),
53 | ),
54 | (
55 | "subject",
56 | models.ForeignKey(
57 | on_delete=django.db.models.deletion.CASCADE,
58 | related_name="announcements",
59 | to="classroom.subject",
60 | ),
61 | ),
62 | ],
63 | options={
64 | "ordering": ["-updated_at", "-created_at"],
65 | },
66 | ),
67 | ]
68 |
--------------------------------------------------------------------------------
/classroom/migrations/0011_notes.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 21:08
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import django_extensions.db.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("classroom", "0010_announcement"),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name="Notes",
17 | fields=[
18 | (
19 | "id",
20 | models.BigAutoField(
21 | auto_created=True,
22 | primary_key=True,
23 | serialize=False,
24 | verbose_name="ID",
25 | ),
26 | ),
27 | (
28 | "slug",
29 | django_extensions.db.fields.AutoSlugField(
30 | blank=True,
31 | editable=False,
32 | populate_from=[
33 | "title",
34 | "subject__title",
35 | "posted_by__user__first_name",
36 | ],
37 | ),
38 | ),
39 | ("title", models.CharField(max_length=255, verbose_name="Title")),
40 | (
41 | "description",
42 | models.TextField(
43 | blank=True, null=True, verbose_name="Description[optional]"
44 | ),
45 | ),
46 | (
47 | "created_at",
48 | models.DateTimeField(auto_now_add=True, verbose_name="Created At "),
49 | ),
50 | (
51 | "updated_at",
52 | models.DateTimeField(auto_now=True, verbose_name="Updated At "),
53 | ),
54 | (
55 | "posted_by",
56 | models.ForeignKey(
57 | null=True,
58 | on_delete=django.db.models.deletion.SET_NULL,
59 | related_name="created_notes",
60 | to="classroom.teacher",
61 | ),
62 | ),
63 | (
64 | "subject",
65 | models.ForeignKey(
66 | on_delete=django.db.models.deletion.CASCADE,
67 | related_name="notes",
68 | to="classroom.subject",
69 | ),
70 | ),
71 | ],
72 | options={
73 | "verbose_name_plural": "Notes",
74 | "ordering": ["title", "-created_at"],
75 | },
76 | ),
77 | ]
78 |
--------------------------------------------------------------------------------
/classroom/migrations/0012_notesattachmentfile.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-09 21:10
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import django_extensions.db.fields
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ("classroom", "0011_notes"),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name="NotesAttachmentFile",
18 | fields=[
19 | (
20 | "id",
21 | models.BigAutoField(
22 | auto_created=True,
23 | primary_key=True,
24 | serialize=False,
25 | verbose_name="ID",
26 | ),
27 | ),
28 | (
29 | "title",
30 | django_extensions.db.fields.AutoSlugField(
31 | blank=True,
32 | editable=False,
33 | null=True,
34 | populate_from=[
35 | "notes__title",
36 | "notes__subject__title",
37 | "created_at",
38 | ],
39 | ),
40 | ),
41 | (
42 | "file_path",
43 | models.FileField(
44 | blank=True,
45 | null=True,
46 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/notes/%Y/%m/%d",
47 | validators=[
48 | django.core.validators.FileExtensionValidator(
49 | allowed_extensions=["xlsx", "pdf", "doc"],
50 | message="Please Upload XLSX/PDF/Doc file only",
51 | )
52 | ],
53 | verbose_name="Upload File Here",
54 | ),
55 | ),
56 | (
57 | "created_at",
58 | models.DateTimeField(auto_now_add=True, verbose_name="Created At "),
59 | ),
60 | (
61 | "notes",
62 | models.ForeignKey(
63 | on_delete=django.db.models.deletion.CASCADE,
64 | related_name="attached_files",
65 | to="classroom.notes",
66 | ),
67 | ),
68 | ],
69 | options={
70 | "ordering": ["id"],
71 | },
72 | ),
73 | ]
74 |
--------------------------------------------------------------------------------
/classroom/migrations/0013_alter_college_allowed_teacher_list.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-10 04:55
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0012_notesattachmentfile"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="college",
16 | name="allowed_teacher_list",
17 | field=models.FileField(
18 | blank=True,
19 | null=True,
20 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/teachers/",
21 | validators=[
22 | django.core.validators.FileExtensionValidator(
23 | allowed_extensions=["csv", "xlsx"],
24 | message="Please Upload CSV/XLSX file only",
25 | )
26 | ],
27 | verbose_name="Upload teacher List File(.csv/.xl)",
28 | ),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/classroom/migrations/0014_remove_teacher_classroom_classroom_teachers.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-10 05:47
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0013_alter_college_allowed_teacher_list"),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name="teacher",
15 | name="classroom",
16 | ),
17 | migrations.AddField(
18 | model_name="classroom",
19 | name="teachers",
20 | field=models.ManyToManyField(
21 | related_name="classrooms", to="classroom.teacher"
22 | ),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/classroom/migrations/0015_alter_teacher_user.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-10 07:20
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ("classroom", "0014_remove_teacher_classroom_classroom_teachers"),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name="teacher",
18 | name="user",
19 | field=models.OneToOneField(
20 | default="9d1f0ef716ff4fc1a6380ecdd021e60f",
21 | on_delete=django.db.models.deletion.CASCADE,
22 | related_name="teacher_profile",
23 | to=settings.AUTH_USER_MODEL,
24 | ),
25 | preserve_default=False,
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/classroom/migrations/0016_alter_classroom_teachers.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-11 05:49
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0015_alter_teacher_user"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="classroom",
15 | name="teachers",
16 | field=models.ManyToManyField(
17 | blank=True, related_name="classrooms", to="classroom.teacher"
18 | ),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/classroom/migrations/0017_allowedteacherclassroomlevel.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-11 06:45
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0016_alter_classroom_teachers"),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name="AllowedTeacherClassroomLevel",
16 | fields=[
17 | (
18 | "id",
19 | models.BigAutoField(
20 | auto_created=True,
21 | primary_key=True,
22 | serialize=False,
23 | verbose_name="ID",
24 | ),
25 | ),
26 | ("email", models.EmailField(max_length=255, verbose_name="Email Id")),
27 | (
28 | "classroom",
29 | models.ForeignKey(
30 | on_delete=django.db.models.deletion.CASCADE,
31 | related_name="allowed_teachers",
32 | to="classroom.classroom",
33 | ),
34 | ),
35 | ],
36 | ),
37 | ]
38 |
--------------------------------------------------------------------------------
/classroom/migrations/0018_alter_classroom_teachers.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-11 15:54
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0017_allowedteacherclassroomlevel"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="classroom",
15 | name="teachers",
16 | field=models.ManyToManyField(
17 | blank=True,
18 | editable=False,
19 | related_name="classrooms",
20 | to="classroom.teacher",
21 | ),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/classroom/migrations/0019_alter_notesattachmentfile_title.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-11 18:54
2 |
3 | from django.db import migrations
4 | import django_extensions.db.fields
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0018_alter_classroom_teachers"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="notesattachmentfile",
16 | name="title",
17 | field=django_extensions.db.fields.AutoSlugField(
18 | blank=True,
19 | default="a-1",
20 | editable=False,
21 | populate_from=["notes__title", "notes__subject__title", "created_at"],
22 | ),
23 | preserve_default=False,
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/classroom/migrations/0020_alter_notesattachmentfile_file_path.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-12 12:18
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0019_alter_notesattachmentfile_title"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="notesattachmentfile",
16 | name="file_path",
17 | field=models.FileField(
18 | blank=True,
19 | max_length=400,
20 | null=True,
21 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/notes/%Y/%m/%d",
22 | validators=[
23 | django.core.validators.FileExtensionValidator(
24 | allowed_extensions=["xlsx", "pdf", "doc"],
25 | message="Please Upload XLSX/PDF/Doc file only",
26 | )
27 | ],
28 | verbose_name="Upload File Here",
29 | ),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/classroom/migrations/0021_assignment.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-13 16:42
2 |
3 | import classroom.validators
4 | import datetime
5 | import django.core.validators
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ("classroom", "0020_alter_notesattachmentfile_file_path"),
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name="Assignment",
19 | fields=[
20 | (
21 | "id",
22 | models.BigAutoField(
23 | auto_created=True,
24 | primary_key=True,
25 | serialize=False,
26 | verbose_name="ID",
27 | ),
28 | ),
29 | (
30 | "title",
31 | models.CharField(max_length=300, verbose_name="Assignment Title"),
32 | ),
33 | ("description", models.TextField(blank=True, null=True)),
34 | (
35 | "attached_pdf",
36 | models.FileField(
37 | blank=True,
38 | max_length=500,
39 | null=True,
40 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/assignments/%Y/%m/%d",
41 | validators=[
42 | django.core.validators.FileExtensionValidator(
43 | allowed_extensions=["pdf"],
44 | message="Please Upload PDF file only",
45 | ),
46 | classroom.validators.pdf_file_size_lt_5mb,
47 | ],
48 | verbose_name="Upload File Here",
49 | ),
50 | ),
51 | (
52 | "due_date",
53 | models.DateField(
54 | default=datetime.date(2022, 5, 14),
55 | validators=[classroom.validators.assignment_date_gte_today],
56 | verbose_name="Due by",
57 | ),
58 | ),
59 | (
60 | "due_time",
61 | models.TimeField(
62 | default=datetime.datetime(2022, 5, 13, 22, 12, 6, 961250),
63 | verbose_name="Due time",
64 | ),
65 | ),
66 | (
67 | "created_at",
68 | models.DateTimeField(auto_now_add=True, verbose_name="Created At "),
69 | ),
70 | (
71 | "given_by",
72 | models.ForeignKey(
73 | null=True,
74 | on_delete=django.db.models.deletion.SET_NULL,
75 | related_name="assignments_given",
76 | to="classroom.teacher",
77 | ),
78 | ),
79 | (
80 | "subject",
81 | models.ForeignKey(
82 | on_delete=django.db.models.deletion.CASCADE,
83 | related_name="assignments",
84 | to="classroom.subject",
85 | ),
86 | ),
87 | ],
88 | ),
89 | ]
90 |
--------------------------------------------------------------------------------
/classroom/migrations/0022_alter_assignment_options_alter_assignment_due_time.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-13 16:46
2 |
3 | import datetime
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0021_assignment"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterModelOptions(
15 | name="assignment",
16 | options={"ordering": ["due_date", "due_time", "-created_at"]},
17 | ),
18 | migrations.AlterField(
19 | model_name="assignment",
20 | name="due_time",
21 | field=models.TimeField(
22 | default=datetime.datetime(2022, 5, 13, 22, 16, 11, 407031),
23 | verbose_name="Due time",
24 | ),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/classroom/migrations/0023_alter_assignment_options_assignment_alloted_marks_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-13 16:55
2 |
3 | import classroom.validators
4 | import datetime
5 | import django.core.validators
6 | from django.db import migrations, models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ("classroom", "0022_alter_assignment_options_alter_assignment_due_time"),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterModelOptions(
17 | name="assignment",
18 | options={
19 | "ordering": ["due_date", "due_time", "alloted_marks", "-created_at"]
20 | },
21 | ),
22 | migrations.AddField(
23 | model_name="assignment",
24 | name="alloted_marks",
25 | field=models.PositiveSmallIntegerField(
26 | default=100,
27 | validators=[
28 | django.core.validators.MaxValueValidator(
29 | 100, "so sir you want to take exam more than 100 marks?😑"
30 | )
31 | ],
32 | verbose_name="Marks:",
33 | ),
34 | ),
35 | migrations.AlterField(
36 | model_name="assignment",
37 | name="attached_pdf",
38 | field=models.FileField(
39 | blank=True,
40 | max_length=500,
41 | null=True,
42 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/assignments/%Y/%m/%d",
43 | validators=[
44 | django.core.validators.FileExtensionValidator(
45 | allowed_extensions=["pdf"],
46 | message="Please Upload PDF file only",
47 | ),
48 | classroom.validators.pdf_file_size_lt_5mb,
49 | ],
50 | verbose_name="Upload File Here📁",
51 | ),
52 | ),
53 | migrations.AlterField(
54 | model_name="assignment",
55 | name="due_time",
56 | field=models.TimeField(
57 | default=datetime.datetime(2022, 5, 13, 22, 25, 15, 927058),
58 | verbose_name="Due time",
59 | ),
60 | ),
61 | ]
62 |
--------------------------------------------------------------------------------
/classroom/migrations/0024_delete_assignment.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-14 14:59
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | (
10 | "classroom",
11 | "0023_alter_assignment_options_assignment_alloted_marks_and_more",
12 | ),
13 | ]
14 |
15 | operations = [
16 | migrations.DeleteModel(
17 | name="Assignment",
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/classroom/migrations/0025_collegedba.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-15 12:19
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ("classroom", "0024_delete_assignment"),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name="CollegeDBA",
18 | fields=[
19 | (
20 | "id",
21 | models.BigAutoField(
22 | auto_created=True,
23 | primary_key=True,
24 | serialize=False,
25 | verbose_name="ID",
26 | ),
27 | ),
28 | (
29 | "college",
30 | models.ForeignKey(
31 | on_delete=django.db.models.deletion.CASCADE,
32 | related_name="college_dbas",
33 | to="classroom.college",
34 | unique=True,
35 | ),
36 | ),
37 | (
38 | "user",
39 | models.OneToOneField(
40 | on_delete=django.db.models.deletion.CASCADE,
41 | related_name="college_dba",
42 | to=settings.AUTH_USER_MODEL,
43 | ),
44 | ),
45 | ],
46 | options={
47 | "ordering": ["user__first_name", "user__last_name"],
48 | },
49 | ),
50 | ]
51 |
--------------------------------------------------------------------------------
/classroom/migrations/0026_alter_allowedteacher_email.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-15 12:25
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0025_collegedba"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="allowedteacher",
15 | name="email",
16 | field=models.EmailField(
17 | max_length=255, unique=True, verbose_name="Email Id"
18 | ),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/classroom/migrations/0027_allowedcollegedba.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-15 12:37
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0026_alter_allowedteacher_email"),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name="AllowedCollegeDBA",
16 | fields=[
17 | (
18 | "id",
19 | models.BigAutoField(
20 | auto_created=True,
21 | primary_key=True,
22 | serialize=False,
23 | verbose_name="ID",
24 | ),
25 | ),
26 | (
27 | "email",
28 | models.EmailField(
29 | max_length=255, unique=True, verbose_name="Email Id"
30 | ),
31 | ),
32 | (
33 | "college",
34 | models.ForeignKey(
35 | on_delete=django.db.models.deletion.CASCADE,
36 | related_name="allowed_dbas",
37 | to="classroom.college",
38 | ),
39 | ),
40 | ],
41 | ),
42 | ]
43 |
--------------------------------------------------------------------------------
/classroom/migrations/0028_alter_collegedba_college.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-15 12:39
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0027_allowedcollegedba"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="collegedba",
16 | name="college",
17 | field=models.OneToOneField(
18 | on_delete=django.db.models.deletion.CASCADE,
19 | related_name="college_dbas",
20 | to="classroom.college",
21 | ),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/classroom/migrations/0029_alter_college_allowed_teacher_list.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-16 13:25
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0028_alter_collegedba_college"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="college",
16 | name="allowed_teacher_list",
17 | field=models.FileField(
18 | blank=True,
19 | max_length=500,
20 | null=True,
21 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/classroom/teachers/",
22 | validators=[
23 | django.core.validators.FileExtensionValidator(
24 | allowed_extensions=["csv", "xlsx"],
25 | message="Please Upload CSV/XLSX file only",
26 | )
27 | ],
28 | verbose_name="Upload teacher List File(.csv/.xl)",
29 | ),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/classroom/migrations/0030_college_allowed_dba_list.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-17 12:18
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0029_alter_college_allowed_teacher_list"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name="college",
16 | name="allowed_dba_list",
17 | field=models.FileField(
18 | blank=True,
19 | max_length=500,
20 | null=True,
21 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/college/dbas/%Y/%m/%d",
22 | validators=[
23 | django.core.validators.FileExtensionValidator(
24 | allowed_extensions=["csv", "xlsx"],
25 | message="Please Upload CSV/XLSX file only",
26 | )
27 | ],
28 | verbose_name="Upload teacher List File(.csv/.xl)",
29 | ),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/classroom/migrations/0031_college_owner_email_id_collegedba_is_owner.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-19 06:26
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0030_college_allowed_dba_list"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="college",
15 | name="owner_email_id",
16 | field=models.EmailField(
17 | default=None,
18 | max_length=254,
19 | unique=True,
20 | verbose_name="College Owner DBA Mail [unique]",
21 | ),
22 | preserve_default=False,
23 | ),
24 | migrations.AddField(
25 | model_name="collegedba",
26 | name="is_owner",
27 | field=models.BooleanField(default=False, verbose_name="Owner [✔/❌]"),
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/classroom/migrations/0032_alter_collegedba_college.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-19 07:04
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0031_college_owner_email_id_collegedba_is_owner"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="collegedba",
16 | name="college",
17 | field=models.ForeignKey(
18 | on_delete=django.db.models.deletion.CASCADE,
19 | related_name="college_dbas",
20 | to="classroom.college",
21 | ),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/classroom/migrations/0033_alter_college_allowed_dba_list.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-19 11:13
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0032_alter_collegedba_college"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="college",
16 | name="allowed_dba_list",
17 | field=models.FileField(
18 | blank=True,
19 | max_length=500,
20 | null=True,
21 | upload_to="P:\\Codes\\SEM_4_Major_Project\\Code\\ClassroomBackend\\media/college/dbas/%Y/%m/%d",
22 | validators=[
23 | django.core.validators.FileExtensionValidator(
24 | allowed_extensions=["csv", "xlsx"],
25 | message="Please Upload CSV/XLSX file only",
26 | )
27 | ],
28 | verbose_name="Upload DBA List File(.csv/.xl)",
29 | ),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/classroom/migrations/0034_alter_classroom_slug.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-20 06:01
2 |
3 | from django.db import migrations
4 | import django_extensions.db.fields
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0033_alter_college_allowed_dba_list"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="classroom",
16 | name="slug",
17 | field=django_extensions.db.fields.AutoSlugField(
18 | blank=True,
19 | editable=False,
20 | populate_from=[
21 | "title",
22 | "level",
23 | "stream",
24 | "section",
25 | "start_year",
26 | "end_year",
27 | "college__name",
28 | ],
29 | ),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/classroom/migrations/0035_alter_classroom_allowed_student_list_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-21 14:52
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0034_alter_classroom_slug"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="classroom",
16 | name="allowed_student_list",
17 | field=models.FileField(
18 | blank=True,
19 | null=True,
20 | upload_to="classroom/students/",
21 | validators=[
22 | django.core.validators.FileExtensionValidator(
23 | allowed_extensions=["csv", "xlsx"],
24 | message="Please Upload CSV/XLSX file only",
25 | )
26 | ],
27 | verbose_name="Upload Student List File(.csv/xl)",
28 | ),
29 | ),
30 | migrations.AlterField(
31 | model_name="classroom",
32 | name="allowed_teacher_list",
33 | field=models.FileField(
34 | blank=True,
35 | null=True,
36 | upload_to="classroom/teachers/",
37 | validators=[
38 | django.core.validators.FileExtensionValidator(
39 | allowed_extensions=["csv", "xlsx"],
40 | message="Please Upload CSV/XLSX file only",
41 | )
42 | ],
43 | verbose_name="Upload Teacher List File(.csv/xl)",
44 | ),
45 | ),
46 | migrations.AlterField(
47 | model_name="college",
48 | name="allowed_dba_list",
49 | field=models.FileField(
50 | blank=True,
51 | max_length=500,
52 | null=True,
53 | upload_to="college/dbas/%Y/%m/%d",
54 | validators=[
55 | django.core.validators.FileExtensionValidator(
56 | allowed_extensions=["csv", "xlsx"],
57 | message="Please Upload CSV/XLSX file only",
58 | )
59 | ],
60 | verbose_name="Upload DBA List File(.csv/.xl)",
61 | ),
62 | ),
63 | migrations.AlterField(
64 | model_name="college",
65 | name="allowed_teacher_list",
66 | field=models.FileField(
67 | blank=True,
68 | max_length=500,
69 | null=True,
70 | upload_to="classroom/teachers/",
71 | validators=[
72 | django.core.validators.FileExtensionValidator(
73 | allowed_extensions=["csv", "xlsx"],
74 | message="Please Upload CSV/XLSX file only",
75 | )
76 | ],
77 | verbose_name="Upload teacher List File(.csv/.xl)",
78 | ),
79 | ),
80 | migrations.AlterField(
81 | model_name="notesattachmentfile",
82 | name="file_path",
83 | field=models.FileField(
84 | blank=True,
85 | max_length=400,
86 | null=True,
87 | upload_to="classroom/notes/%Y/%m/%d",
88 | validators=[
89 | django.core.validators.FileExtensionValidator(
90 | allowed_extensions=["xlsx", "pdf", "doc"],
91 | message="Please Upload XLSX/PDF/Doc file only",
92 | )
93 | ],
94 | verbose_name="Upload File Here",
95 | ),
96 | ),
97 | ]
98 |
--------------------------------------------------------------------------------
/classroom/migrations/0036_college_stream_list_stream.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-21 15:53
2 |
3 | import django.core.validators
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("classroom", "0035_alter_classroom_allowed_student_list_and_more"),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name="college",
17 | name="stream_list",
18 | field=models.FileField(
19 | blank=True,
20 | max_length=500,
21 | null=True,
22 | upload_to="college/streams/",
23 | validators=[
24 | django.core.validators.FileExtensionValidator(
25 | allowed_extensions=["csv", "xlsx"],
26 | message="Please Upload CSV/XLSX file only",
27 | )
28 | ],
29 | verbose_name="Upload Stream List File(.csv/.xl)",
30 | ),
31 | ),
32 | migrations.CreateModel(
33 | name="Stream",
34 | fields=[
35 | (
36 | "id",
37 | models.BigAutoField(
38 | auto_created=True,
39 | primary_key=True,
40 | serialize=False,
41 | verbose_name="ID",
42 | ),
43 | ),
44 | ("title", models.CharField(max_length=255)),
45 | (
46 | "college",
47 | models.ForeignKey(
48 | on_delete=django.db.models.deletion.CASCADE,
49 | related_name="streams",
50 | to="classroom.college",
51 | ),
52 | ),
53 | (
54 | "dba",
55 | models.ForeignKey(
56 | blank=True,
57 | null=True,
58 | on_delete=django.db.models.deletion.CASCADE,
59 | related_name="streams",
60 | to="classroom.collegedba",
61 | ),
62 | ),
63 | ],
64 | ),
65 | ]
66 |
--------------------------------------------------------------------------------
/classroom/migrations/0037_alter_stream_options_alter_subject_credit_points.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-21 16:10
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0036_college_stream_list_stream"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="stream",
15 | options={"ordering": ["college__name", "title"]},
16 | ),
17 | migrations.AlterField(
18 | model_name="subject",
19 | name="credit_points",
20 | field=models.PositiveSmallIntegerField(
21 | choices=[
22 | (1, "1"),
23 | (2, "2"),
24 | (3, "3"),
25 | (4, "4"),
26 | (5, "5"),
27 | (6, "6"),
28 | (7, "7"),
29 | (8, "8"),
30 | (9, "9"),
31 | (10, "10"),
32 | (11, "11"),
33 | (12, "12"),
34 | (13, "13"),
35 | (14, "14"),
36 | (15, "15"),
37 | (16, "16"),
38 | (17, "17"),
39 | (18, "18"),
40 | (19, "19"),
41 | (20, "20"),
42 | ],
43 | default=1,
44 | ),
45 | ),
46 | ]
47 |
--------------------------------------------------------------------------------
/classroom/migrations/0038_alter_subject_slug.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 09:59
2 |
3 | from django.db import migrations
4 | import django_extensions.db.fields
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0037_alter_stream_options_alter_subject_credit_points"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="subject",
16 | name="slug",
17 | field=django_extensions.db.fields.AutoSlugField(
18 | blank=True,
19 | editable=False,
20 | populate_from=[
21 | "title",
22 | "semester__sem_no",
23 | "subject_type",
24 | "credit_points",
25 | "created_by__user__first_name",
26 | ],
27 | ),
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/classroom/migrations/0039_alter_subject_slug.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 10:01
2 |
3 | from django.db import migrations
4 | import django_extensions.db.fields
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("classroom", "0038_alter_subject_slug"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="subject",
16 | name="slug",
17 | field=django_extensions.db.fields.AutoSlugField(
18 | blank=True,
19 | editable=False,
20 | populate_from=[
21 | "title",
22 | "semester__sem_no",
23 | "subject_type",
24 | "credit_points",
25 | "created_by__user__first_name",
26 | "created_by__user__last_name",
27 | ],
28 | ),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/classroom/migrations/0040_assignment.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 13:28
2 |
3 | import classroom.validators
4 | import datetime
5 | import django.core.validators
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ("classroom", "0039_alter_subject_slug"),
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name="Assignment",
19 | fields=[
20 | (
21 | "id",
22 | models.BigAutoField(
23 | auto_created=True,
24 | primary_key=True,
25 | serialize=False,
26 | verbose_name="ID",
27 | ),
28 | ),
29 | (
30 | "title",
31 | models.CharField(max_length=300, verbose_name="Assignment Title"),
32 | ),
33 | ("description", models.TextField(blank=True, null=True)),
34 | (
35 | "alloted_marks",
36 | models.PositiveSmallIntegerField(
37 | default=100,
38 | validators=[
39 | django.core.validators.MaxValueValidator(
40 | 100,
41 | "assignments can't be alloted more than 100 marks 😑",
42 | )
43 | ],
44 | verbose_name="Marks:",
45 | ),
46 | ),
47 | (
48 | "attached_pdf",
49 | models.FileField(
50 | blank=True,
51 | max_length=500,
52 | null=True,
53 | upload_to="classroom/assignments/",
54 | validators=[
55 | django.core.validators.FileExtensionValidator(
56 | allowed_extensions=["pdf"],
57 | message="Please Upload PDF file only",
58 | ),
59 | classroom.validators.pdf_file_size_lt_5mb,
60 | ],
61 | verbose_name="Upload File Here📁",
62 | ),
63 | ),
64 | (
65 | "due_date",
66 | models.DateField(
67 | default=datetime.date(2022, 5, 23),
68 | validators=[classroom.validators.assignment_date_gte_today],
69 | verbose_name="Due by",
70 | ),
71 | ),
72 | (
73 | "due_time",
74 | models.TimeField(
75 | default=datetime.time(13, 28, 5, 829323),
76 | verbose_name="Due time",
77 | ),
78 | ),
79 | (
80 | "created_at",
81 | models.DateTimeField(auto_now_add=True, verbose_name="Created At "),
82 | ),
83 | (
84 | "given_by",
85 | models.ForeignKey(
86 | null=True,
87 | on_delete=django.db.models.deletion.SET_NULL,
88 | related_name="assignments_given",
89 | to="classroom.teacher",
90 | ),
91 | ),
92 | (
93 | "subject",
94 | models.ForeignKey(
95 | on_delete=django.db.models.deletion.CASCADE,
96 | related_name="assignments",
97 | to="classroom.subject",
98 | ),
99 | ),
100 | ],
101 | options={
102 | "ordering": ["due_date", "due_time", "alloted_marks", "-created_at"],
103 | },
104 | ),
105 | ]
106 |
--------------------------------------------------------------------------------
/classroom/migrations/0041_alter_assignment_due_time_submittedassignment.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 13:35
2 |
3 | import classroom.validators
4 | import datetime
5 | import django.core.validators
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ("classroom", "0040_assignment"),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name="assignment",
19 | name="due_time",
20 | field=models.TimeField(
21 | default=datetime.time(13, 35, 25, 382904), verbose_name="Due time"
22 | ),
23 | ),
24 | migrations.CreateModel(
25 | name="SubmittedAssignment",
26 | fields=[
27 | (
28 | "id",
29 | models.BigAutoField(
30 | auto_created=True,
31 | primary_key=True,
32 | serialize=False,
33 | verbose_name="ID",
34 | ),
35 | ),
36 | ("answer_section", models.TextField(blank=True, null=True)),
37 | (
38 | "submitted_file",
39 | models.FileField(
40 | blank=True,
41 | max_length=500,
42 | null=True,
43 | upload_to="classroom/assignment_submissions/",
44 | validators=[
45 | django.core.validators.FileExtensionValidator(
46 | allowed_extensions=["pdf"],
47 | message="Please Upload PDF file only",
48 | ),
49 | classroom.validators.pdf_file_size_lt_5mb,
50 | ],
51 | verbose_name="Upload File Here📁",
52 | ),
53 | ),
54 | (
55 | "is_submitted",
56 | models.BooleanField(default=False, verbose_name="submitted : "),
57 | ),
58 | ("submission_date", models.DateField(auto_now_add=True)),
59 | ("submission_time", models.TimeField(auto_now_add=True)),
60 | (
61 | "score",
62 | models.IntegerField(
63 | default=0,
64 | validators=[
65 | django.core.validators.MinValueValidator(
66 | 0, "Score should be >= 0"
67 | ),
68 | django.core.validators.MaxValueValidator(
69 | 100, "score should be <= 100"
70 | ),
71 | ],
72 | verbose_name="0<=x<=100",
73 | ),
74 | ),
75 | (
76 | "has_scored",
77 | models.BooleanField(
78 | default=False, verbose_name="Scored by teacher : "
79 | ),
80 | ),
81 | (
82 | "remarks",
83 | models.TextField(
84 | blank=True, max_length=400, null=True, verbose_name="remarks"
85 | ),
86 | ),
87 | (
88 | "assignment",
89 | models.ForeignKey(
90 | on_delete=django.db.models.deletion.CASCADE,
91 | related_name="submissions",
92 | to="classroom.assignment",
93 | ),
94 | ),
95 | (
96 | "scored_by",
97 | models.ForeignKey(
98 | blank=True,
99 | null=True,
100 | on_delete=django.db.models.deletion.SET_NULL,
101 | related_name="scored_assignments",
102 | to="classroom.teacher",
103 | ),
104 | ),
105 | (
106 | "submitted_by",
107 | models.ForeignKey(
108 | on_delete=django.db.models.deletion.CASCADE,
109 | related_name="attempted_assignments",
110 | to="classroom.student",
111 | ),
112 | ),
113 | ],
114 | options={
115 | "ordering": ["-submission_date", "-submission_time", "-score"],
116 | "unique_together": {("assignment", "submitted_by")},
117 | },
118 | ),
119 | ]
120 |
--------------------------------------------------------------------------------
/classroom/migrations/0042_alter_assignment_due_date_alter_assignment_due_time.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 13:39
2 |
3 | import classroom.validators
4 | from django.db import migrations, models
5 | import django.utils.timezone
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("classroom", "0041_alter_assignment_due_time_submittedassignment"),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name="assignment",
17 | name="due_date",
18 | field=models.DateField(
19 | default=django.utils.timezone.now,
20 | validators=[classroom.validators.assignment_date_gte_today],
21 | verbose_name="Due by",
22 | ),
23 | ),
24 | migrations.AlterField(
25 | model_name="assignment",
26 | name="due_time",
27 | field=models.TimeField(
28 | default=django.utils.timezone.now, verbose_name="Due time"
29 | ),
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/classroom/migrations/0043_rename_submittedassignment_assignmentsubmission.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 13:47
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0042_alter_assignment_due_date_alter_assignment_due_time"),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameModel(
14 | old_name="SubmittedAssignment",
15 | new_name="AssignmentSubmission",
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/classroom/migrations/0044_remove_assignment_given_by_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-22 15:24
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0043_rename_submittedassignment_assignmentsubmission"),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name="assignment",
15 | name="given_by",
16 | ),
17 | migrations.RemoveField(
18 | model_name="assignmentsubmission",
19 | name="scored_by",
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/classroom/migrations/0045_alter_college_options.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-23 11:06
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0044_remove_assignment_given_by_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="college",
15 | options={"ordering": ["name", "city"]},
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/classroom/migrations/0046_alter_subject_options.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-23 12:35
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0045_alter_college_options"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="subject",
15 | options={"ordering": ["subject_code", "title", "-subject_type"]},
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/classroom/migrations/0047_alter_stream_unique_together.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-28 16:41
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0046_alter_subject_options"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterUniqueTogether(
14 | name="stream",
15 | unique_together={("title", "dba")},
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/classroom/migrations/0048_alter_allowedstudents_unique_together_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-05-29 16:28
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0047_alter_stream_unique_together"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterUniqueTogether(
14 | name="allowedstudents",
15 | unique_together=set(),
16 | ),
17 | migrations.AlterField(
18 | model_name="allowedstudents",
19 | name="email",
20 | field=models.EmailField(
21 | max_length=255, unique=True, verbose_name="Email Id"
22 | ),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/classroom/migrations/0049_alter_classroom_section.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.4 on 2022-06-02 18:29
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("classroom", "0048_alter_allowedstudents_unique_together_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="classroom",
15 | name="section",
16 | field=models.CharField(
17 | blank=True,
18 | default="A",
19 | max_length=5,
20 | null=True,
21 | verbose_name="Section(optional)",
22 | ),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/classroom/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/classroom/migrations/__init__.py
--------------------------------------------------------------------------------
/classroom/model.py:
--------------------------------------------------------------------------------
1 | from classroom.models.college import College, AllowedTeacher, AllowedCollegeDBA
2 | from classroom.models.classroom import (
3 | Classroom,
4 | Semester,
5 | AllowedTeacherClassroomLevel,
6 | AllowedStudents,
7 | )
8 | from classroom.models.teacher import Teacher
9 | from classroom.models.student import Student
10 | from classroom.models.subject import Subject
11 | from classroom.models.notes import Notes, NotesAttachmentFile
12 | from classroom.models.announcement import Announcement
13 | from classroom.models.imports import User
14 | from classroom.models.college_dba import CollegeDBA
15 | from classroom.models.assignment import Assignment, AssignmentSubmission
16 |
--------------------------------------------------------------------------------
/classroom/models/__init__.py:
--------------------------------------------------------------------------------
1 | from . import *
2 |
--------------------------------------------------------------------------------
/classroom/models/announcement.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class Announcement(models.Model):
6 | heading = models.TextField(_("Heading"), default="No Heading Given")
7 | body = models.TextField(_("Description[Optional] "), null=True, blank=True)
8 | created_at = models.DateTimeField(_("Created At "), auto_now_add=True)
9 | updated_at = models.DateTimeField(_("Updated At "), auto_now=True)
10 | subject = models.ForeignKey(
11 | "classroom.Subject", on_delete=models.CASCADE, related_name="announcements"
12 | )
13 | posted_by = models.ForeignKey(
14 | "classroom.Teacher", on_delete=models.CASCADE, related_name="announcements"
15 | )
16 |
17 | class Meta:
18 | ordering = ["-updated_at", "-created_at"]
19 |
20 | def __str__(self) -> str:
21 | return f"{self.heading}"
22 |
23 | def heading_short(self):
24 | return f"{self.heading[:10]}..."
25 |
--------------------------------------------------------------------------------
/classroom/models/assignment.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 | from django.utils.timezone import now, timedelta
4 |
5 |
6 | class Assignment(models.Model):
7 | title = models.CharField(_("Assignment Title"), max_length=300)
8 | description = models.TextField(null=True, blank=True)
9 | alloted_marks = models.PositiveSmallIntegerField(
10 | _("Marks:"),
11 | default=100,
12 | validators=[
13 | MaxValueValidator(100, "assignments can't be alloted more than 100 marks 😑")
14 | ],
15 | )
16 | attached_pdf = models.FileField(
17 | _("Upload File Here📁"),
18 | null=True,
19 | blank=True,
20 | max_length=500,
21 | upload_to="classroom/assignments/",
22 | validators=[
23 | FileExtensionValidator(
24 | allowed_extensions=["pdf"],
25 | message="Please Upload PDF file only",
26 | ),
27 | pdf_file_size_lt_5mb,
28 | ],
29 | )
30 | due_date = models.DateField(
31 | _("Due by"),
32 | default=now,
33 | validators=[assignment_date_gte_today],
34 | )
35 | due_time = models.TimeField(_("Due time"), default=now)
36 | subject = models.ForeignKey(
37 | "classroom.Subject", on_delete=models.CASCADE, related_name="assignments"
38 | )
39 | # given_by = models.ForeignKey(
40 | # "classroom.Teacher",
41 | # on_delete=models.SET_NULL,
42 | # null=True,
43 | # related_name="assignments_given",
44 | # )
45 |
46 | created_at = models.DateTimeField(
47 | _("Created At "), auto_now_add=True, editable=False
48 | )
49 |
50 | class Meta:
51 | ordering = ["due_date", "due_time", "alloted_marks", "-created_at"]
52 |
53 | def __str__(self) -> str:
54 | return self.title
55 |
56 | def file_path(self):
57 | return self.attached_pdf.name
58 |
59 | def short_description(self) -> str:
60 | return self.description[:30]
61 |
62 |
63 | class AssignmentSubmission(models.Model):
64 | # FK to assignment
65 | assignment = models.ForeignKey(
66 | "classroom.Assignment", on_delete=models.CASCADE, related_name="submissions"
67 | )
68 | # ----------------FOR STUDENT ----------------------------------------------
69 | submitted_by = models.ForeignKey(
70 | "classroom.Student",
71 | on_delete=models.CASCADE,
72 | related_name="attempted_assignments",
73 | )
74 | answer_section = models.TextField(null=True, blank=True)
75 | submitted_file = models.FileField(
76 | _("Upload File Here📁"),
77 | null=True,
78 | blank=True,
79 | max_length=500,
80 | upload_to=f"classroom/assignment_submissions/",
81 | validators=[
82 | FileExtensionValidator(
83 | allowed_extensions=["pdf"],
84 | message="Please Upload PDF file only",
85 | ),
86 | pdf_file_size_lt_5mb,
87 | ],
88 | )
89 | is_submitted = models.BooleanField(_("submitted : "), default=False)
90 | submission_date = models.DateField(auto_now_add=True, editable=False)
91 | submission_time = models.TimeField(auto_now_add=True, editable=False)
92 |
93 | # ------------- FOR TEACHER Control -------------
94 | score = models.IntegerField(
95 | _("0<=x<=100"),
96 | default=0,
97 | validators=[
98 | MinValueValidator(0, "Score should be >= 0"),
99 | MaxValueValidator(
100 | 100,
101 | "score should be <= 100",
102 | ),
103 | ],
104 | )
105 | has_scored = models.BooleanField(_("Scored by teacher : "), default=False)
106 | remarks = models.TextField(_("remarks"), blank=True, null=True, max_length=400)
107 | # scored_by = models.ForeignKey(
108 | # "classroom.Teacher",
109 | # on_delete=models.SET_NULL,
110 | # related_name="scored_assignments",
111 | # blank=True,
112 | # null=True,
113 | # )
114 |
115 | class Meta:
116 | unique_together = [["assignment", "submitted_by"]]
117 | ordering = ["-submission_date", "-submission_time", "-score"]
118 |
119 | def __str__(self) -> str:
120 | return f"{self.submitted_by}"
121 |
--------------------------------------------------------------------------------
/classroom/models/classroom.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class Classroom(models.Model):
6 | slug = AutoSlugField(
7 | populate_from=[
8 | "title",
9 | "level",
10 | "stream",
11 | "section",
12 | "start_year",
13 | "end_year",
14 | "college__name",
15 | ],
16 | )
17 | title = models.CharField(_("Classroom Name"), max_length=255, null=True, blank=True)
18 | level = models.CharField(
19 | _("Level"),
20 | max_length=40,
21 | choices=LEVEL_CHOICES.choices,
22 | default=LEVEL_CHOICES.UnderGraduate,
23 | help_text="e.g - Masters/Bachelors",
24 | )
25 | stream = models.CharField(
26 | _("Your Stream"),
27 | max_length=255,
28 | ) # TODO: Make Drop Down
29 | start_year = models.PositiveSmallIntegerField(
30 | _("Starting Year"),
31 | # default=2022,
32 | default=date.today().year - 2,
33 | db_index=True,
34 | help_text="Write your session starting year (e.g. - 2020)",
35 | validators=[
36 | MinValueValidator(2000, "You can't select year less than 2000"),
37 | MaxValueValidator(
38 | (date.today().year + 1), # FIXME: Make this dynamic
39 | # 2023,
40 | "Max Year Can be selected only 1 year ahead of current year",
41 | ),
42 | ],
43 | )
44 | end_year = models.PositiveSmallIntegerField(
45 | _("Ending Year"),
46 | db_index=True,
47 | default=date.today().year,
48 | help_text="Write your session ending year (e.g. - 2020)",
49 | validators=[
50 | MinValueValidator(2000, "You can't select year less than 2000"),
51 | MaxValueValidator(
52 | (2200), # FIXME: Make this dynamic
53 | "Max Year Can be selected only 1 year ahead of current year",
54 | ),
55 | ],
56 | )
57 | section = models.CharField(
58 | _("Section(optional)"),
59 | max_length=5,
60 | null=True,
61 | blank=True,
62 | default=SECTION_CHOICES.A,
63 | )
64 | no_of_semesters = models.PositiveSmallIntegerField(
65 | _("Number of Sem"),
66 | default=4,
67 | validators=[
68 | MinValueValidator(4, "Min Course Duration is of 2 Years(4 semesters)"),
69 | MaxValueValidator(14),
70 | is_no_of_sem_even,
71 | ],
72 | )
73 | current_sem = models.PositiveSmallIntegerField(
74 | _("On Going Sem"),
75 | validators=[
76 | MinValueValidator(1),
77 | MaxValueValidator(14),
78 | ],
79 | )
80 | created_at = models.DateTimeField(auto_now_add=True, db_index=True)
81 | college = models.ForeignKey(
82 | "classroom.College", on_delete=models.CASCADE, related_name="classrooms"
83 | )
84 | allowed_student_list = models.FileField(
85 | _("Upload Student List File(.csv/xl)"),
86 | null=True,
87 | blank=True,
88 | upload_to=f"classroom/students/",
89 | validators=[
90 | FileExtensionValidator(
91 | allowed_extensions=["csv", "xlsx"],
92 | message="Please Upload CSV/XLSX file only",
93 | )
94 | ],
95 | )
96 | teachers = models.ManyToManyField(
97 | "classroom.Teacher", related_name="classrooms", blank=True, editable=False
98 | )
99 | # TODO: use this to add teachers in classrooms and vice-versa
100 | allowed_teacher_list = models.FileField(
101 | _("Upload Teacher List File(.csv/xl)"),
102 | upload_to=f"classroom/teachers/",
103 | null=True,
104 | blank=True,
105 | validators=[
106 | FileExtensionValidator(
107 | allowed_extensions=["csv", "xlsx"],
108 | message="Please Upload CSV/XLSX file only",
109 | )
110 | ],
111 | )
112 |
113 | class Meta:
114 | unique_together = [
115 | "level",
116 | "stream",
117 | "start_year",
118 | "end_year",
119 | "section",
120 | "college",
121 | ]
122 | ordering = [
123 | "college__name",
124 | "level",
125 | "-start_year",
126 | "-end_year",
127 | "section",
128 | "stream",
129 | ]
130 |
131 | def __str__(self) -> str:
132 | return str(self.title)
133 |
134 |
135 | class Semester(models.Model):
136 | classroom = models.ForeignKey(
137 | "classroom.Classroom", on_delete=models.CASCADE, related_name="semesters"
138 | )
139 | sem_no = models.PositiveSmallIntegerField(
140 | _("Semester No"),
141 | # editable=False,
142 | validators=[
143 | MinValueValidator(1, "sem value > 0"),
144 | MaxValueValidator(14, "sem value < 15"),
145 | ],
146 | )
147 | is_current_sem = models.BooleanField(_("is this sem going on? "), default=False)
148 |
149 | class Meta:
150 | unique_together = ["classroom", "sem_no"]
151 | ordering = ["classroom__title", "sem_no"]
152 |
153 | def __str__(self) -> str:
154 | return str(self.sem_no)
155 |
156 | @admin.display(ordering=["classroom__title"])
157 | def classroom_name(self):
158 | return self.classroom.title
159 |
160 |
161 | class AllowedTeacherClassroomLevel(models.Model):
162 | objects = AllowedTeacherClassroomLevelManager()
163 | email = models.EmailField(_("Email Id"), max_length=255)
164 | classroom = models.ForeignKey(
165 | "classroom.Classroom", on_delete=models.CASCADE, related_name="allowed_teachers"
166 | )
167 |
168 | def __str__(self) -> str:
169 | return f"{self.email}"
170 |
171 |
172 | class AllowedStudents(models.Model):
173 | email = models.EmailField(_("Email Id"), max_length=255, unique=True)
174 | university_roll = models.PositiveBigIntegerField(
175 | _("University Roll"),
176 | help_text="Your University Roll No - (e.g. - 13071020030)",
177 | )
178 | classroom = models.ForeignKey(
179 | "classroom.Classroom", on_delete=models.CASCADE, related_name="allowed_students"
180 | )
181 |
182 | class Meta:
183 | ordering = ["university_roll"]
184 | verbose_name_plural = "Allowed Students"
185 |
186 | def __str__(self) -> str:
187 | return f"{self.email} || {self.university_roll}"
188 |
189 | @admin.display(ordering=["classroom__title"])
190 | def get_classroom_name(self):
191 | return self.classroom.title
192 |
--------------------------------------------------------------------------------
/classroom/models/college.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class College(models.Model):
6 | """
7 | # College
8 | > `params`:
9 | > ('slug', 'name', 'city', 'state', 'address', 'allowed_teacher_list', 'allowed_dba_list','owner_email_id')
10 | """
11 |
12 | slug = AutoSlugField(
13 | populate_from=["name", "state", "city"],
14 | editable=True,
15 | )
16 | name = models.CharField(_("College Name"), max_length=255)
17 | city = models.CharField(_("City"), max_length=255)
18 | state = models.CharField(_("State"), max_length=255)
19 | address = models.TextField(null=True, blank=True)
20 | owner_email_id = models.EmailField(
21 | _("College Owner DBA Mail [unique]"), unique=True
22 | )
23 | stream_list = models.FileField(
24 | _("Upload Stream List File(.csv/.xl)"),
25 | upload_to="college/streams/",
26 | null=True,
27 | max_length=500,
28 | blank=True,
29 | validators=[
30 | FileExtensionValidator(
31 | allowed_extensions=["csv", "xlsx"],
32 | message="Please Upload CSV/XLSX file only",
33 | )
34 | ],
35 | )
36 | allowed_teacher_list = models.FileField(
37 | _("Upload teacher List File(.csv/.xl)"),
38 | upload_to="classroom/teachers/",
39 | null=True,
40 | max_length=500,
41 | blank=True,
42 | validators=[
43 | FileExtensionValidator(
44 | allowed_extensions=["csv", "xlsx"],
45 | message="Please Upload CSV/XLSX file only",
46 | )
47 | ],
48 | )
49 | allowed_dba_list = (
50 | models.FileField( # TODO: Allowed DBA Auto Create Signal by College
51 | _("Upload DBA List File(.csv/.xl)"),
52 | upload_to="college/dbas/%Y/%m/%d",
53 | null=True,
54 | max_length=500,
55 | blank=True,
56 | validators=[
57 | FileExtensionValidator(
58 | allowed_extensions=["csv", "xlsx"],
59 | message="Please Upload CSV/XLSX file only",
60 | )
61 | ],
62 | )
63 | )
64 |
65 | class Meta:
66 | ordering = ["name", "city"]
67 |
68 | def __str__(self) -> str:
69 | return f"{self.name} - {self.city}"
70 |
71 |
72 | class AllowedTeacher(models.Model):
73 | email = models.EmailField(_("Email Id"), max_length=255, unique=True)
74 | college = models.ForeignKey(
75 | "classroom.College", on_delete=models.CASCADE, related_name="allowed_teachers"
76 | )
77 |
78 | def __str__(self) -> str:
79 | return f"{self.email}"
80 |
81 |
82 | class AllowedCollegeDBA(models.Model):
83 | email = models.EmailField(_("Email Id"), max_length=255, unique=True)
84 | college = models.ForeignKey(
85 | "classroom.College", on_delete=models.CASCADE, related_name="allowed_dbas"
86 | )
87 |
88 | def __str__(self) -> str:
89 | return f"{self.email}"
90 |
91 |
92 | class Stream(models.Model):
93 | title = models.CharField(max_length=255)
94 | college = models.ForeignKey(
95 | "classroom.College", on_delete=models.CASCADE, related_name="streams"
96 | )
97 | dba = models.ForeignKey(
98 | "classroom.CollegeDBA",
99 | on_delete=models.CASCADE,
100 | related_name="streams",
101 | null=True,
102 | blank=True,
103 | )
104 |
105 | class Meta:
106 | ordering = ["college__name", "title"]
107 | unique_together = ["title", "dba"]
108 |
109 | def __str__(self) -> str:
110 | return f"{self.title} || Clg: {self.college.name}"
111 |
--------------------------------------------------------------------------------
/classroom/models/college_dba.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class CollegeDBA(models.Model):
6 | is_owner = models.BooleanField(_("Owner [✔/❌]"), default=False)
7 | user = models.OneToOneField(
8 | User, on_delete=models.CASCADE, related_name="college_dba"
9 | )
10 | college = models.ForeignKey(
11 | "classroom.College",
12 | on_delete=models.CASCADE,
13 | related_name="college_dbas",
14 | )
15 |
16 | class Meta:
17 | ordering = ["user__first_name", "user__last_name"]
18 |
19 | def __str__(self) -> str:
20 | return f"{self.user.first_name} {self.user.last_name} | {self.user.email} |DBA"
21 |
22 | @admin.display(ordering=["user__email"])
23 | def get_email(self):
24 | return self.user.email
25 |
26 | @admin.display(ordering=["user__first_name"])
27 | def get_first_name(self):
28 | return self.user.first_name
29 |
30 | @admin.display(ordering=["user__last_name"])
31 | def get_last_name(self):
32 | return self.user.last_name
33 |
--------------------------------------------------------------------------------
/classroom/models/imports.py:
--------------------------------------------------------------------------------
1 | from datetime import date, datetime, timedelta
2 | from random import randint
3 | from time import time
4 |
5 | from django.conf import settings
6 | from django.contrib import admin
7 | from django.contrib.auth import get_user_model
8 | from django.core.validators import (
9 | FileExtensionValidator,
10 | MaxValueValidator,
11 | MinValueValidator,
12 | )
13 | from django.db import models
14 |
15 | from django.utils.translation import gettext_lazy as _
16 | from django_extensions.db.fields import AutoSlugField
17 |
18 | from classroom.constants import LEVEL_CHOICES, SECTION_CHOICES
19 | from classroom.managers import AllowedTeacherClassroomLevelManager
20 | from classroom.validators import (
21 | assignment_date_gte_today,
22 | is_no_of_sem_even,
23 | pdf_file_size_lt_5mb,
24 | )
25 |
26 |
27 | User = get_user_model()
28 |
--------------------------------------------------------------------------------
/classroom/models/notes.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class Notes(models.Model):
6 | slug = AutoSlugField(
7 | populate_from=["title", "subject__title", "posted_by__user__first_name"]
8 | )
9 | title = models.CharField(_("Title"), max_length=255)
10 | description = models.TextField(_("Description[optional]"), null=True, blank=True)
11 | created_at = models.DateTimeField(_("Created At "), auto_now_add=True)
12 | updated_at = models.DateTimeField(_("Updated At "), auto_now=True)
13 | subject = models.ForeignKey(
14 | "classroom.Subject", on_delete=models.CASCADE, related_name="notes"
15 | )
16 | posted_by = models.ForeignKey(
17 | "classroom.Teacher",
18 | on_delete=models.SET_NULL,
19 | related_name="created_notes",
20 | null=True,
21 | )
22 |
23 | class Meta:
24 | verbose_name_plural = "Notes"
25 | ordering = ["title", "-created_at"]
26 |
27 | def __str__(self) -> str:
28 | return self.title
29 |
30 | @admin.display(ordering="description")
31 | def short_description(self):
32 | return self.description[:40]
33 |
34 |
35 | class NotesAttachmentFile(models.Model):
36 | title = AutoSlugField(
37 | populate_from=["notes__title", "notes__subject__title", "created_at"],
38 | )
39 | file_path = models.FileField(
40 | _("Upload File Here"),
41 | null=True,
42 | blank=True,
43 | max_length=400,
44 | upload_to=f"classroom/notes/%Y/%m/%d",
45 | validators=[
46 | FileExtensionValidator(
47 | allowed_extensions=["xlsx", "pdf", "doc"],
48 | message="Please Upload XLSX/PDF/Doc file only",
49 | )
50 | ],
51 | )
52 | created_at = models.DateTimeField(_("Created At "), auto_now_add=True)
53 | notes = models.ForeignKey(
54 | "classroom.Notes", on_delete=models.CASCADE, related_name="attached_files"
55 | )
56 |
57 | class Meta:
58 | ordering = ["id"]
59 |
60 | def __str__(self) -> str:
61 | return str(self.file_path.name)
62 |
--------------------------------------------------------------------------------
/classroom/models/student.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class Student(models.Model):
6 |
7 | university_roll = models.PositiveBigIntegerField(
8 | _("University Roll"),
9 | help_text="Your University Roll No - (e.g. - 13071020030)",
10 | null=True,
11 | blank=True,
12 | )
13 |
14 | user = models.OneToOneField(
15 | User,
16 | on_delete=models.CASCADE,
17 | null=True,
18 | blank=True,
19 | related_name="student_profile",
20 | )
21 |
22 | classroom = models.ForeignKey(
23 | "classroom.Classroom",
24 | on_delete=models.CASCADE,
25 | related_name="students",
26 | null=True,
27 | blank=True,
28 | )
29 |
30 | class Meta:
31 | ordering = ["user__first_name", "user__last_name"]
32 |
33 | def __str__(self) -> str:
34 | return f"{self.id}"
35 |
36 | @admin.display(ordering=["user__email"])
37 | def get_email(self):
38 | return self.user.email
39 |
40 | @admin.display(ordering="user__first_name")
41 | def first_name(self):
42 | return self.user.first_name
43 |
44 | @admin.display(ordering="user__last_name")
45 | def last_name(self):
46 | return self.user.last_name
47 |
48 | @admin.display(ordering="classroom__college__name")
49 | def college_name(self):
50 | return self.classroom.college.name
51 |
--------------------------------------------------------------------------------
/classroom/models/subject.py:
--------------------------------------------------------------------------------
1 | from django.utils.translation import gettext_lazy as _
2 | from .imports import *
3 |
4 |
5 | class Subject(models.Model):
6 | """
7 | # Subject belongs to semester
8 | - **slug** will be used as filter key
9 | - **subject_code** is [`optional`]
10 | - **title** is required
11 | - **subject_type** is required [Practical/Theory/Elective]
12 | - **credit_points** [`optional`] type: positive small integer
13 | - **credit_points** [`optional`] type: positive small integer
14 | """
15 |
16 | # --------------Choice Fields------------------
17 | THEORY = "TH"
18 | PRACTICAL = "PRC"
19 | ELECTIVE = "ELC"
20 | PROJECT = "PRJ"
21 | SUBJECT_TYPE_CHOICE = [
22 | (THEORY, _("Theory")),
23 | (PRACTICAL, _("Practical")),
24 | (ELECTIVE, _("Elective")),
25 | (PROJECT, _("Project")),
26 | ]
27 | CP_ONE = 1
28 | CP_TEN = 20
29 | CP_CHOICE = [(i, _(str(i))) for i in range(CP_ONE, CP_TEN + 1)]
30 | # ----------------------------------------------
31 | slug = AutoSlugField(
32 | populate_from=[
33 | "title",
34 | "semester__sem_no",
35 | "subject_type",
36 | "credit_points",
37 | "created_by__user__first_name",
38 | "created_by__user__last_name",
39 | ]
40 | )
41 | subject_code = models.CharField(max_length=20)
42 | title = models.CharField(max_length=200)
43 | subject_type = models.CharField(
44 | max_length=5, choices=SUBJECT_TYPE_CHOICE, default=THEORY
45 | )
46 | credit_points = models.PositiveSmallIntegerField(choices=CP_CHOICE, default=CP_ONE)
47 | semester = models.ForeignKey(
48 | "classroom.Semester", on_delete=models.CASCADE, related_name="subjects"
49 | )
50 | created_at = models.DateField(auto_now_add=True)
51 | created_by = models.ForeignKey(
52 | "classroom.Teacher", on_delete=models.CASCADE, related_name="subjects"
53 | )
54 |
55 | class Meta:
56 | ordering = ["subject_code", "title", "-subject_type"]
57 |
58 | def __str__(self) -> str:
59 | return f"{self.title} - {self.subject_code}"
60 |
--------------------------------------------------------------------------------
/classroom/models/teacher.py:
--------------------------------------------------------------------------------
1 | from .imports import *
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class Teacher(models.Model):
6 |
7 | user = models.OneToOneField(
8 | User,
9 | on_delete=models.CASCADE,
10 | # null=True,
11 | # blank=True,
12 | related_name="teacher_profile",
13 | )
14 | college = models.ForeignKey(
15 | "classroom.College", on_delete=models.CASCADE, related_name="teachers"
16 | )
17 |
18 | class Meta:
19 | ordering = ["user__first_name", "user__last_name"]
20 |
21 | def __str__(self) -> str:
22 | return f"{self.user.first_name} {self.user.last_name} | {self.user.email} "
23 |
24 | @admin.display(ordering=["user__email"])
25 | def get_email(self):
26 | return self.user.email
27 |
28 | @admin.display(ordering=["user__first_name"])
29 | def get_first_name(self):
30 | return self.user.first_name
31 |
32 | @admin.display(ordering=["user__last_name"])
33 | def get_last_name(self):
34 | return self.user.last_name
35 |
--------------------------------------------------------------------------------
/classroom/routers/dba_urls.py:
--------------------------------------------------------------------------------
1 | from rest_framework_nested.routers import DefaultRouter, NestedDefaultRouter
2 | from classroom.views.college_dba_view import (
3 | AddOrDeleteOtherDBAViewSet,
4 | AllDbaProfiles,
5 | AllowedStudentManagementClassroomLevel,
6 | CollegeCreateViewSet,
7 | CollegeRetrieveForDBAViewSet,
8 | DBAProfileViewSet,
9 | ManageClassroomByDBAViewSet,
10 | StreamManagementViewSet,
11 | TeacherManagementClassroomLevel,
12 | TeacherManagementCollegeLevel,
13 | )
14 | from termcolor import cprint
15 |
16 | college_create_router = DefaultRouter()
17 | college_create_router.register(
18 | "college-create", CollegeCreateViewSet, basename="college"
19 | )
20 |
21 | all_dbas_router = NestedDefaultRouter(
22 | college_create_router, "college-create", lookup="college"
23 | )
24 | all_dbas_router.register("dbas", AllDbaProfiles, basename="dbas")
25 |
26 | dba_root_router = DefaultRouter()
27 | dba_root_router.register("dba", DBAProfileViewSet, basename="dba")
28 |
29 | college_for_dba_router = DefaultRouter()
30 | college_for_dba_router.register(
31 | "college-dba", CollegeRetrieveForDBAViewSet, basename="college"
32 | )
33 |
34 | college_for_stream = DefaultRouter()
35 | college_for_stream.register(
36 | "college-streams", CollegeRetrieveForDBAViewSet, basename="college"
37 | )
38 |
39 | stream_router = NestedDefaultRouter(
40 | college_for_stream, "college-streams", lookup="college"
41 | )
42 | stream_router.register("stream", StreamManagementViewSet, basename="stream")
43 |
44 | create_other_dba_router = NestedDefaultRouter(
45 | college_for_dba_router, "college-dba", lookup="college"
46 | )
47 | create_other_dba_router.register(
48 | "manage-dba", AddOrDeleteOtherDBAViewSet, basename="manage_dba"
49 | )
50 |
51 | classroom_create_router = NestedDefaultRouter(
52 | college_for_dba_router, "college-dba", lookup="college"
53 | )
54 | classroom_create_router.register(
55 | "classroom", ManageClassroomByDBAViewSet, basename="classroom"
56 | )
57 |
58 | allowed_teacher_college_router = NestedDefaultRouter(
59 | college_for_dba_router, "college-dba", lookup="college"
60 | )
61 | allowed_teacher_college_router.register(
62 | "manage-teacher-college", TeacherManagementCollegeLevel, basename="teacher"
63 | )
64 |
65 | allowed_teacher_classlevel_router = NestedDefaultRouter(
66 | classroom_create_router, "classroom", lookup="classroom"
67 | )
68 | allowed_teacher_classlevel_router.register(
69 | "manage-teacher",
70 | TeacherManagementClassroomLevel,
71 | basename="teacher_classroom",
72 | )
73 | allowed_student_classlevel_router = NestedDefaultRouter(
74 | classroom_create_router, "classroom", lookup="classroom"
75 | )
76 | allowed_student_classlevel_router.register(
77 | "manage-student",
78 | AllowedStudentManagementClassroomLevel,
79 | basename="student_classroom",
80 | )
81 |
82 |
83 | dba_urlpatterns = []
84 | dba_urlpatterns += (
85 | college_create_router.urls
86 | + stream_router.urls
87 | + dba_root_router.urls
88 | + college_for_dba_router.urls
89 | + create_other_dba_router.urls
90 | + classroom_create_router.urls
91 | + allowed_teacher_college_router.urls
92 | + allowed_teacher_classlevel_router.urls
93 | + allowed_student_classlevel_router.urls
94 | + all_dbas_router.urls
95 | )
96 |
97 | # cprint("-------------------------------------------", "green")
98 | # cprint("DBA URLs -", "green")
99 | # cprint("-------------------------------------------", "green")
100 | # for url in dba_urlpatterns:
101 | # cprint(url, "green")
102 |
--------------------------------------------------------------------------------
/classroom/routers/students_urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from rest_framework_nested.routers import DefaultRouter, NestedDefaultRouter
3 | from termcolor import cprint
4 |
5 | from ..views.student_view import (
6 | AnnouncementForStudentsViewSet,
7 | AssignmentSubmissionByStudentViewSet,
8 | AssignmentViewForStudentViewSet,
9 | ClassroomForStudentViewSet,
10 | NotesForStudentViewSet,
11 | SemesterForStudentViewSet,
12 | StudentProfileViewSet,
13 | SubjectsForStudentsViewSet,
14 | )
15 |
16 | student_router = DefaultRouter()
17 | student_router.register("student", StudentProfileViewSet, "student")
18 |
19 |
20 | classroom_router = DefaultRouter()
21 | classroom_router.register("classroom", ClassroomForStudentViewSet, "classroom")
22 |
23 | classroom_sems_router = NestedDefaultRouter(
24 | classroom_router, "classroom", lookup="classroom"
25 | )
26 | classroom_sems_router.register("semester", SemesterForStudentViewSet, basename="sem")
27 |
28 | sem_subjects_router = NestedDefaultRouter(
29 | classroom_sems_router, "semester", lookup="semester"
30 | )
31 | sem_subjects_router.register("subject", SubjectsForStudentsViewSet, basename="subject")
32 |
33 |
34 | subject_announcement_router = NestedDefaultRouter(
35 | sem_subjects_router, "subject", lookup="subject"
36 | )
37 | subject_announcement_router.register(
38 | "announcement", AnnouncementForStudentsViewSet, basename="announcement"
39 | )
40 |
41 | subject_notes_router = NestedDefaultRouter(
42 | sem_subjects_router, "subject", lookup="subject"
43 | )
44 | subject_notes_router.register("notes", NotesForStudentViewSet, basename="notes")
45 |
46 | subject_assignment_router = NestedDefaultRouter(
47 | sem_subjects_router, "subject", lookup="subject"
48 | )
49 | subject_assignment_router.register(
50 | "assignment", AssignmentViewForStudentViewSet, basename="assignment"
51 | )
52 |
53 | assignment_submission_router = NestedDefaultRouter(
54 | subject_assignment_router, "assignment", lookup="assignment"
55 | )
56 | assignment_submission_router.register(
57 | "submission", AssignmentSubmissionByStudentViewSet, basename="submission"
58 | )
59 |
60 | stud_urlpatterns = []
61 |
62 | stud_urlpatterns += (
63 | student_router.urls
64 | + classroom_router.urls
65 | + classroom_sems_router.urls
66 | + sem_subjects_router.urls
67 | + subject_announcement_router.urls
68 | + subject_notes_router.urls
69 | + subject_assignment_router.urls
70 | + assignment_submission_router.urls
71 | )
72 |
73 | # cprint("-------------------------------------------", "green")
74 | # cprint("Student URLs -", "green")
75 | # cprint("-------------------------------------------", "green")
76 | # for url in stud_urlpatterns:
77 | # cprint(url, "cyan")
78 |
--------------------------------------------------------------------------------
/classroom/routers/teacher_urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from rest_framework_nested.routers import DefaultRouter, NestedDefaultRouter
3 | from termcolor import cprint
4 |
5 | from classroom.views.teacher_view import (
6 | AnnouncementPostByTeacherViewSet,
7 | AssignmentEvaluationViewSet,
8 | AssignmentPostViewSet,
9 | ClassroomsForTeacherViewSet,
10 | FileUploadDeleteViewSet,
11 | SemesterForTeacherViewSet,
12 | SubjectForTeacherViewSet,
13 | TeacherNotesUploadViewSet,
14 | TeacherProfileViewSet,
15 | TeacherProfilesForDBAViewSet,
16 | )
17 | from .dba_urls import college_create_router
18 |
19 | teacher_router = DefaultRouter()
20 | teacher_router.register("teacher", TeacherProfileViewSet, basename="teacher")
21 |
22 | all_teacher_profiles_for_dba_router = NestedDefaultRouter(
23 | college_create_router, "college-create", lookup="college"
24 | )
25 | all_teacher_profiles_for_dba_router.register(
26 | "teacher-profiles", TeacherProfilesForDBAViewSet, basename="teachers"
27 | )
28 |
29 | teacher_classrooms = NestedDefaultRouter(teacher_router, "teacher", lookup="teacher")
30 | teacher_classrooms.register(
31 | "teacher-classrooms", ClassroomsForTeacherViewSet, basename="teacher-classrooms"
32 | )
33 |
34 | teacher_sem = NestedDefaultRouter(teacher_router, "teacher", lookup="teacher")
35 | teacher_sem.register("sem", SemesterForTeacherViewSet, basename="sem")
36 | # TODO: implement announcement routers
37 |
38 | teacher_subject = NestedDefaultRouter(teacher_sem, "sem", lookup="sem")
39 | teacher_subject.register("subject", SubjectForTeacherViewSet, basename="subject")
40 |
41 | teacher_subject_sub_urls = NestedDefaultRouter(
42 | teacher_router, "teacher", lookup="teacher"
43 | )
44 | teacher_subject_sub_urls.register(
45 | "subject", SubjectForTeacherViewSet, basename="subject"
46 | )
47 |
48 | teacher_subject_announcement = NestedDefaultRouter(
49 | teacher_subject_sub_urls, "subject", lookup="subject"
50 | )
51 | teacher_subject_announcement.register(
52 | "announcement", AnnouncementPostByTeacherViewSet, basename="announcement"
53 | )
54 |
55 | teacher_subject_notes = NestedDefaultRouter(
56 | teacher_subject_sub_urls, "subject", lookup="subject"
57 | )
58 | teacher_subject_notes.register("notes", TeacherNotesUploadViewSet, basename="notes")
59 |
60 | teacher_notes_file_upload = NestedDefaultRouter(
61 | teacher_subject_notes, "notes", lookup="notes"
62 | )
63 | teacher_notes_file_upload.register(
64 | "notes-files", FileUploadDeleteViewSet, basename="notes_files"
65 | )
66 | teacher_assignment_router = NestedDefaultRouter(
67 | teacher_subject_sub_urls, "subject", lookup="subject"
68 | )
69 | teacher_assignment_router.register(
70 | "assignment", AssignmentPostViewSet, basename="assignment"
71 | )
72 |
73 | teacher_assignment_evaluation_router = NestedDefaultRouter(
74 | teacher_assignment_router, "assignment", lookup="assignment"
75 | )
76 | teacher_assignment_evaluation_router.register(
77 | "submission", AssignmentEvaluationViewSet, basename="submission"
78 | )
79 |
80 | teacher_urlpatterns = []
81 | teacher_urlpatterns += (
82 | teacher_router.urls
83 | + teacher_classrooms.urls
84 | + teacher_sem.urls
85 | + teacher_subject.urls
86 | + teacher_subject_announcement.urls
87 | + teacher_subject_notes.urls
88 | + teacher_notes_file_upload.urls
89 | + teacher_assignment_router.urls
90 | + teacher_assignment_evaluation_router.urls
91 | + all_teacher_profiles_for_dba_router.urls
92 | )
93 |
94 | # cprint("-------------------------------------------", "red")
95 | # cprint("Teacher URLs -", "red")
96 | # cprint("-------------------------------------------", "red")
97 | # for url in teacher_urlpatterns:
98 | # cprint(url, "red")
99 |
--------------------------------------------------------------------------------
/classroom/serializers/__init__.py:
--------------------------------------------------------------------------------
1 | from . import *
2 |
--------------------------------------------------------------------------------
/classroom/serializers/student.py:
--------------------------------------------------------------------------------
1 | from rest_framework.serializers import ModelSerializer as ms, FileField
2 | from classroom.model import Student, User
3 | from classroom.models.assignment import Assignment, AssignmentSubmission
4 | from .classroom import ClassroomReadForStudentSerializer
5 | from accounts.serializers import CurrentUserSerializer
6 | from rest_framework.exceptions import ValidationError
7 | from rest_framework import status as code
8 |
9 |
10 | class StudentUserReadSerializer(ms):
11 | class Meta:
12 | model = User
13 | fields = ("first_name", "last_name", "contact_no", "email")
14 |
15 |
16 | class StudentReadSerializer(ms):
17 | """
18 | Returns Student ID & User Profile along with Classroom Details
19 | """
20 |
21 | user = StudentUserReadSerializer()
22 | classroom = ClassroomReadForStudentSerializer()
23 |
24 | class Meta:
25 | model = Student
26 | fields = ("university_roll", "user", "classroom")
27 | # read_only_fields = "__all__"
28 | # depth = 1
29 |
30 |
31 | # ---------------assignment read by student --------------------
32 | class AssignmentReadByStudentSerializer(ms):
33 | """
34 | # Student can only view assignment and download the file
35 | """
36 |
37 | class Meta:
38 | model = Assignment
39 | fields = (
40 | "id",
41 | "title",
42 | "description",
43 | "alloted_marks",
44 | "attached_pdf",
45 | "due_date",
46 | "due_time",
47 | "created_at",
48 | )
49 |
50 |
51 | # ---------------Assignment Submission Serializers ---------------
52 | class AssignmentSubmissionReadByStudent(ms):
53 | submitted_file = FileField(max_length=None, use_url=True, required=False)
54 |
55 | class Meta:
56 | model = AssignmentSubmission
57 | fields = (
58 | "id",
59 | "answer_section",
60 | "submitted_file",
61 | "is_submitted",
62 | "submission_date",
63 | "submission_time",
64 | "score",
65 | "has_scored",
66 | "remarks",
67 | "assignment",
68 | # "submitted_by",
69 | # "scored_by",
70 | )
71 | read_only_fields = [
72 | "id",
73 | "submission_date",
74 | "submission_time",
75 | "score",
76 | "has_scored",
77 | "remarks",
78 | "assignment",
79 | ]
80 |
81 |
82 | class AssignmentSubmissionWriteByStudent(ms):
83 | submitted_file = FileField(max_length=None, use_url=True, required=False)
84 |
85 | class Meta:
86 | model = AssignmentSubmission
87 | fields = (
88 | "id",
89 | "answer_section",
90 | "submitted_file",
91 | "is_submitted",
92 | "submission_date",
93 | "submission_time",
94 | "has_scored",
95 | "submitted_by",
96 | # "score",
97 | # "assignment",
98 | # "remarks",
99 | # "scored_by",
100 | )
101 | read_only_fields = [
102 | "id",
103 | "submission_date",
104 | "submission_time",
105 | "has_scored",
106 | "submitted_by",
107 | ]
108 |
109 | def create(self, validated_data):
110 | assignment_pk = self.context.get("assignment_pk")
111 | submitted_by = self.context.get("user_id")
112 | try:
113 | assignment = Assignment.objects.get(id=assignment_pk)
114 | except:
115 | raise ValidationError("Assignment Not Found", code=code.HTTP_404_NOT_FOUND)
116 | try:
117 | student: Student = Student.objects.select_related("user").get(
118 | user__id=submitted_by
119 | )
120 | except:
121 | raise ValidationError(
122 | "Student Profile Not Found", code=code.HTTP_404_NOT_FOUND
123 | )
124 | if AssignmentSubmission.objects.filter(
125 | assignment=assignment, submitted_by=student
126 | ).exists():
127 | raise ValidationError(
128 | "1 Submission Allowed Per Student", code=code.HTTP_400_BAD_REQUEST
129 | )
130 |
131 | try:
132 | self.instance = AssignmentSubmission.objects.create(
133 | assignment=assignment, submitted_by=student, **validated_data
134 | )
135 | except:
136 | raise ValidationError(
137 | "Assignment Submission Failed", code=code.HTTP_304_NOT_MODIFIED
138 | )
139 | return self.instance
140 |
--------------------------------------------------------------------------------
/classroom/serializers/teacher.py:
--------------------------------------------------------------------------------
1 | from rest_framework.serializers import ModelSerializer as ms, IntegerField, FileField
2 | from accounts.serializers import CurrentUserSerializer
3 | from classroom.model import Classroom, Teacher, User
4 |
5 |
6 | class MinimalUserDetailsSerializer(ms):
7 | class Meta:
8 | model = User
9 | fields = ["first_name", "last_name", "email", "contact_no"]
10 |
11 |
12 | class MinimalTeacherDetailsSerializer(ms):
13 | user = MinimalUserDetailsSerializer()
14 | teacher_id = IntegerField(source="id")
15 |
16 | class Meta:
17 | model = Teacher
18 | fields = ["user", "teacher_id"]
19 |
20 |
21 | class TeacherReadForSubjectSerializer(ms):
22 | user = MinimalUserDetailsSerializer()
23 |
24 | class Meta:
25 | model = Teacher
26 | fields = "__all__"
27 | depth = 1
28 | select_related_fields = ["user"]
29 |
30 |
31 | class TeacherClassroomsGetSerializer(ms):
32 | class Meta:
33 | model = Classroom
34 | fields = (
35 | "slug",
36 | "title",
37 | "level",
38 | "stream",
39 | "start_year",
40 | "end_year",
41 | "section",
42 | "no_of_semesters",
43 | "current_sem",
44 | )
45 |
46 |
47 | # ----------------- teacher view serializers -----------------
48 | class TeacherProfileSerializer(ms):
49 | user = MinimalUserDetailsSerializer()
50 | teacher_id = IntegerField(source="id")
51 | classroom_list = TeacherClassroomsGetSerializer(many=True, source="classrooms")
52 |
53 | class Meta:
54 | model = Teacher
55 | fields = ["teacher_id", "user", "classroom_list"]
56 |
57 |
58 | class TeacherProfileForDBASerializer(ms):
59 | user = MinimalUserDetailsSerializer()
60 | teacher_id = IntegerField(source="id")
61 | # classroom_list = TeacherClassroomsGetSerializer(many=True, source="classrooms")
62 |
63 | class Meta:
64 | model = Teacher
65 | fields = ["teacher_id", "user"]
66 |
--------------------------------------------------------------------------------
/classroom/serializers/usertype.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 |
3 |
4 | class UserTypeSerializer(serializers.Serializer):
5 | # user_type = serializers.CharField(trim_whitespace=True)
6 | user_type = serializers.ChoiceField(
7 | choices=[
8 | ("student", "student"),
9 | ("teacher", "teacher"),
10 | ("college_dba", "college_dba"),
11 | ]
12 | )
13 | usertype_id = serializers.IntegerField()
14 |
--------------------------------------------------------------------------------
/classroom/signals/__init__.py:
--------------------------------------------------------------------------------
1 | from django.dispatch import Signal
2 |
3 | # classroom_updated = Signal() #FIXME: Not working
4 |
--------------------------------------------------------------------------------
/classroom/signals/common_imports.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | import pandas as pd
4 | from celery import shared_task
5 | from classroom.model import (
6 | AllowedStudents,
7 | AllowedTeacher,
8 | AllowedTeacherClassroomLevel,
9 | Classroom,
10 | College,
11 | Semester,
12 | Student,
13 | Teacher,
14 | User,
15 | )
16 | from classroom.models.college import AllowedCollegeDBA
17 | from classroom.models.college_dba import CollegeDBA
18 | from classroom.serializers import teacher
19 | from classroom.tasks import send_email_after_bulk_object_creation
20 | from django.conf import settings
21 | from django.core.mail import send_mail
22 | from django.db.models.signals import post_delete, post_save, pre_save
23 | from django.dispatch import receiver
24 | from django.http import BadHeaderError
25 | from rest_framework.exceptions import ValidationError
26 | from rest_framework import status
27 | from rest_framework.response import Response
28 | from termcolor import cprint
29 | from django.db.transaction import atomic
30 |
31 |
32 | def delete_college_on_any_failure(id):
33 | try:
34 | College.objects.filter(pk=id).delete()
35 | cprint(f"College Deleted with ID:[{id}]")
36 | except:
37 | cprint(f"No College Found with ID:[{id}]")
38 |
--------------------------------------------------------------------------------
/classroom/signals/dba_handlers.py:
--------------------------------------------------------------------------------
1 | from .common_imports import *
2 | from rest_framework import status
3 |
4 |
5 | @shared_task
6 | @receiver(post_save, sender=College)
7 | def create_allowed_dba(sender, instance: College, created, **kwargs):
8 | if created:
9 | is_owner_of_college_exists = AllowedCollegeDBA.objects.filter(
10 | email=instance.owner_email_id
11 | ).exists()
12 | if is_owner_of_college_exists:
13 | delete_college_on_any_failure(instance.id)
14 | raise ValidationError(
15 | detail=f"""
16 | college owner {instance.owner_email_id} already associated with
17 | college - {instance.name}""",
18 | code=status.HTTP_400_BAD_REQUEST,
19 | )
20 | else:
21 | AllowedCollegeDBA.objects.create(
22 | college=instance, email=instance.owner_email_id
23 | )
24 | subject = f"Welcome to {instance.name}"
25 | body = f"""
26 | You are the owner admin of college {instance.name}
27 | Now you can sign up with mail id - {instance.owner_email_id}
28 | ----------------
29 | NB: Only you will be able to add other DBAs or remove them
30 | """
31 | send_mail(
32 | subject, body, settings.EMAIL_HOST_USER, [instance.owner_email_id]
33 | )
34 | if instance.allowed_dba_list == None:
35 | delete_college_on_any_failure(instance.id)
36 | raise ValidationError(
37 | detail="Allowed Admin List Not Found,College Creation Failed",
38 | code=status.HTTP_404_NOT_FOUND,
39 | )
40 | file_abs_path = None
41 | dba_file_path = os.path.join(
42 | settings.BASE_DIR,
43 | settings.MEDIA_ROOT,
44 | instance.allowed_dba_list.name,
45 | )
46 | if os.path.exists(dba_file_path):
47 | file_abs_path = os.path.abspath(dba_file_path)
48 | else:
49 | delete_college_on_any_failure(instance.id)
50 | raise ValidationError(
51 | detail="Stream List Not Found,College Creation Failed",
52 | code=status.HTTP_404_NOT_FOUND,
53 | )
54 |
55 | df = None
56 | if str(file_abs_path).split(".")[-1] == "csv":
57 | df: pd.DataFrame = pd.read_csv(file_abs_path)
58 | elif str(file_abs_path).split(".")[-1] == "xlsx":
59 | df = pd.read_excel(file_abs_path)
60 | else:
61 | delete_college_on_any_failure(instance.id)
62 | raise ValidationError(
63 | detail="Allowed Admin List Not of Type [Xl/CSV] ,College Creation Failed",
64 | code=status.HTTP_400_BAD_REQUEST,
65 | )
66 |
67 | if df.shape[1] != 1:
68 | delete_college_on_any_failure(instance.id)
69 | raise ValidationError(
70 | f"{instance.allowed_dba_list.name} file should contain ONE Column namely email",
71 | code=status.HTTP_400_BAD_REQUEST,
72 | )
73 | if not df.shape[0] > 0:
74 | delete_college_on_any_failure(instance.id)
75 | raise ValidationError(
76 | f"{instance.allowed_dba_list.name} file can not be empty",
77 | code=status.HTTP_400_BAD_REQUEST,
78 | )
79 |
80 | if not "email" in df.columns:
81 | delete_college_on_any_failure(instance.id)
82 | raise ValidationError(
83 | f"{instance.stream_list.name} file should contain ONE Column namely email",
84 | code=status.HTTP_400_BAD_REQUEST,
85 | )
86 | # df_dict = df.to_dict("records")
87 | # if len(df_dict) < 1:
88 |
89 | # raise ValidationError(
90 | # detail="Please give at least one mail-id in the fail "
91 | # )
92 |
93 | # print(df_dict)
94 | try:
95 | list_of_teachers = [
96 | AllowedCollegeDBA(college=instance, **args)
97 | for args in df.to_dict("records")
98 | ]
99 | with atomic():
100 | AllowedCollegeDBA.objects.bulk_create(list_of_teachers)
101 | except:
102 | delete_college_on_any_failure(instance.id)
103 | raise ValidationError(
104 | f"Bulk Allowed College DBA creation failed, College Creation Failed",
105 | code=status.HTTP_400_BAD_REQUEST,
106 | )
107 | email_list = df["email"].to_list()
108 | subject = "Open Your DBA Account"
109 | prompt = "please use your following mail id to sign up in the Classroom[LMS]"
110 | try:
111 | send_email_after_bulk_object_creation.delay(subject, prompt, email_list)
112 | except BadHeaderError:
113 | print("Could not able to send emails to DBAs")
114 | os.remove(file_abs_path)
115 | College.objects.update(allowed_dba_list="")
116 |
117 |
118 | @receiver(post_delete, sender=AllowedCollegeDBA)
119 | def delete_dba_account_on_deletion_of_allowed_cllg_dba(
120 | sender, instance: AllowedCollegeDBA, **kwargs
121 | ):
122 | try:
123 | college_dba = CollegeDBA.objects.select_related("user").filter(
124 | user__email=instance.email
125 | )
126 | if college_dba.exists():
127 | try:
128 | college_dba.delete()
129 | except:
130 | pass
131 | except:
132 | pass
133 |
--------------------------------------------------------------------------------
/classroom/signals/handlers.py:
--------------------------------------------------------------------------------
1 | from .classroom_handlers import (
2 | create_allowed_students,
3 | create_allowed_teacher_for_classroom_level,
4 | create_allowed_teacher_for_classroom_level_with_check,
5 | create_sems_for_new_classroom,
6 | )
7 | from .college_handlers import (
8 | create_allowed_teacher,
9 | remove_teacher_profile_after_allowed_teacher_deletion,
10 | send_mail_after_create_allowed_teacher,
11 | )
12 | from .dba_handlers import create_allowed_dba
13 | from .profile_handlers import create_profile
14 | from .teacher_classroom_handlers import (
15 | assign_classroom_to_existing_teacher,
16 | auto_join_teacher_to_classes,
17 | remove_class_after_removal_of_assigned_teacher,
18 | )
19 | from .user_handlers import (
20 | delete_user_on_dba_delete,
21 | delete_user_on_student_delete,
22 | delete_user_on_teacher_delete, # FIXME: not working
23 | )
24 |
--------------------------------------------------------------------------------
/classroom/signals/profile_handlers.py:
--------------------------------------------------------------------------------
1 | from classroom.models.college import Stream
2 | from .common_imports import *
3 |
4 |
5 | @receiver(post_save, sender=settings.AUTH_USER_MODEL)
6 | def create_profile(sender, instance: settings.AUTH_USER_MODEL, created, **kwargs):
7 | if created:
8 | if (
9 | AllowedStudents.objects.filter(email=instance.email).exists()
10 | and not Student.objects.select_related("user")
11 | .filter(user=instance)
12 | .exists()
13 | ):
14 | classroom: Classroom = AllowedStudents.objects.get(
15 | email=instance.email
16 | ).classroom
17 | university_roll = AllowedStudents.objects.get(
18 | email=instance.email
19 | ).university_roll
20 | s = Student.objects.create(
21 | university_roll=university_roll, user=instance, classroom=classroom
22 | )
23 | # TODO: Add some other info also in the mail
24 | subject = "Your Student Profile Has Been Created Successfully"
25 | msg = f"""
26 | Student ID :{s.id}
27 | mail : {instance.email}
28 | classroom : {classroom.title}
29 |
30 | You Can Login After Activation Of your account
31 | """
32 | send_mail(subject, msg, settings.EMAIL_HOST_USER, [instance.email])
33 | elif (
34 | AllowedTeacher.objects.filter(email=instance.email).exists()
35 | and not Teacher.objects.select_related("user")
36 | .filter(user=instance)
37 | .exists()
38 | ):
39 | college_detail = College.objects.get(
40 | pk=(
41 | AllowedTeacher.objects.filter(email=instance.email)
42 | .select_related("college")
43 | .values_list("college", flat=True)
44 | )[0]
45 | )
46 | from termcolor import cprint
47 |
48 | cprint(college_detail, "red")
49 | t = Teacher.objects.create(user=instance, college=college_detail)
50 | subject = "Your Teacher Profile Has Been Created Successfully"
51 | msg = f"""
52 | Teacher ID :{t.id}
53 | mail : {instance.email}
54 |
55 | You Can Login After Activation Of your account
56 | """
57 | send_mail(subject, msg, settings.EMAIL_HOST_USER, [instance.email])
58 | elif (
59 | AllowedCollegeDBA.objects.filter(email=instance.email).exists()
60 | and not CollegeDBA.objects.select_related("user")
61 | .filter(user=instance)
62 | .exists()
63 | ):
64 | from termcolor import cprint
65 |
66 | # cprint("In DBA Creation", "red")
67 | college_detail: College = College.objects.get(
68 | pk=(
69 | AllowedCollegeDBA.objects.filter(email=instance.email)
70 | .select_related("college")
71 | .values_list("college", flat=True)
72 | )[0]
73 | )
74 |
75 | # cprint(college_detail, "red")
76 | is_owner = False
77 | if instance.email == college_detail.owner_email_id:
78 | is_owner = True
79 | # cprint(f"is owner --> [ {is_owner} ]", "red")
80 | t: CollegeDBA = CollegeDBA.objects.create(
81 | user=instance, college=college_detail, is_owner=is_owner
82 | )
83 | if is_owner:
84 | Stream.objects.select_related("college").filter(
85 | college__id=college_detail.id
86 | ).update(dba=t)
87 | subject = "Your DBA Profile Has Been Created Successfully"
88 | msg = f"""
89 | COLLEGE DBA ID :{t.id}
90 | mail : {instance.email}
91 |
92 | You Can Login After Activation Of your account
93 | """
94 | send_mail(subject, msg, settings.EMAIL_HOST_USER, [instance.email])
95 | elif instance.is_superuser or instance.is_staff: # ADMIN
96 | print("Admin")
97 | else:
98 | subject = "Profile Creation Failed"
99 | msg = f"""
100 | You have not been assigned any profile for any college
101 |
102 | contact mail id: {settings.EMAIL_HOST_USER}
103 | """
104 | send_mail(subject, msg, settings.EMAIL_HOST_USER, [instance.email])
105 | # User.objects.filter(pk=instance.id).delete()
106 | raise ValidationError(
107 | "Profile creation failed, as you have no profile attached with any college",
108 | code=status.HTTP_401_UNAUTHORIZED,
109 | )
110 |
--------------------------------------------------------------------------------
/classroom/signals/teacher_classroom_handlers.py:
--------------------------------------------------------------------------------
1 | from .common_imports import *
2 |
3 |
4 | @shared_task
5 | @receiver(post_save, sender=Teacher)
6 | def auto_join_teacher_to_classes(sender, instance: Teacher, created, **kwargs):
7 | # from termcolor import cprint
8 |
9 | if created:
10 | qset = Classroom.objects.prefetch_related("allowed_teachers").filter(
11 | allowed_teachers__email=instance.user.email
12 | )
13 | # cprint(list(qset), "blue")
14 | try:
15 | instance.classrooms.add(*qset)
16 | except:
17 | pass
18 | # cprint("already assigned classrooms to teacher ", "yellow")
19 |
20 |
21 | @receiver(
22 | post_save, sender=AllowedTeacherClassroomLevel
23 | ) # FIXME: classroom pre signed up teachers are not saving
24 | def assign_classroom_to_existing_teacher(
25 | sender, instance: AllowedTeacherClassroomLevel, created, **kwargs
26 | ):
27 | from termcolor import cprint
28 |
29 | t = (
30 | "assign_classroom_to_existing_teacher "
31 | + instance.email
32 | + "\n---> "
33 | + str(created)
34 | )
35 | cprint(t, "red")
36 | if created:
37 | classroom: Classroom = Classroom.objects.select_related("college").get(
38 | pk=instance.classroom.id
39 | )
40 | cprint(classroom, "red")
41 | teacher_query = Teacher.objects.select_related("user").filter(
42 | user__email=instance.email
43 | )
44 | cprint(str(teacher_query.exists()) + " -> " + instance.email, "blue")
45 | if teacher_query.exists():
46 | teacher = teacher_query.first()
47 | from django.db import transaction
48 |
49 | with transaction.atomic():
50 | classroom.teachers.add(teacher)
51 | classroom.save(force_update=True)
52 | for tchr in classroom.teachers.all():
53 | cprint("Classrooms of teacher -> ", "cyan")
54 | cprint(tchr, "cyan")
55 | owner_mail_id = classroom.college.owner_email_id
56 | cprint(f"owner mail id --> {owner_mail_id}", "red")
57 | subject = "Sir You have been Assigned A new Class"
58 | msg = f"Classroom - {classroom.title}"
59 | send_mail(subject, msg, owner_mail_id, [instance.email])
60 |
61 |
62 | @shared_task
63 | @receiver(post_delete, sender=AllowedTeacherClassroomLevel)
64 | def remove_class_after_removal_of_assigned_teacher(
65 | sender, instance: AllowedTeacherClassroomLevel, **kwargs
66 | ):
67 | """
68 | this removes the classroom from the teacher if teacher
69 | has been removed from allowed class room level
70 | """
71 | classroom: Classroom = Classroom.objects.select_related("college").get(
72 | pk=instance.classroom.id
73 | )
74 | teacher_query = Teacher.objects.select_related("user").filter(
75 | user__email=instance.email
76 | )
77 | if teacher_query.exists():
78 | teacher_query.first().classrooms.remove(classroom)
79 | subject = "Sir You have been Removed From A Class"
80 | msg = f"Classroom - {classroom.title}"
81 | owner_mail_id = classroom.college.owner_email_id
82 | cprint(f"owner mail id --> {owner_mail_id}", "red")
83 | send_mail(subject, msg, owner_mail_id, [instance.email])
84 |
--------------------------------------------------------------------------------
/classroom/signals/user_handlers.py:
--------------------------------------------------------------------------------
1 | from .common_imports import *
2 |
3 |
4 | # @shared_task
5 | @receiver(post_delete, sender=Student)
6 | def delete_user_on_student_delete(sender, instance: Student, **kwargs):
7 | try:
8 | user = User.objects.filter(pk=instance.user.id)
9 | if user.exists():
10 | user.delete()
11 | try:
12 | subject = "You Have Been Removed from Classroom"
13 | body = f"""
14 | your mail - {instance.user.email} is no more associated with any college or classroom
15 | """
16 | except:
17 | cprint(
18 | "student profile deletion confirmation mail sending failed", "red"
19 | )
20 | except:
21 | pass
22 |
23 |
24 | # @shared_task
25 | @receiver(post_delete, sender=CollegeDBA)
26 | def delete_user_on_dba_delete(sender, instance: CollegeDBA, **kwargs):
27 | try:
28 | user = User.objects.filter(pk=instance.user.id)
29 | if user.exists():
30 | user.delete()
31 | try:
32 | subject = "Your DBA Profile Have Been Removed from College"
33 | body = f"""
34 | your mail - {instance.user.email} is no more associated with any college or classroom
35 | """
36 | except:
37 | cprint("admin profile deletion confirmation mail sending failed", "red")
38 | except:
39 | pass
40 |
41 |
42 | # @shared_task
43 | @receiver(
44 | post_delete, sender=Teacher
45 | ) # FIXME: Off this code if teacher removal from class deletes user
46 | def delete_user_on_teacher_delete(sender, instance: Teacher, **kwargs):
47 | try:
48 | if instance.user:
49 | user = User.objects.filter(pk=instance.user.id)
50 | if user.exists():
51 | user.delete()
52 | try:
53 | subject = "Your Teacher Profile Have Been Removed from College"
54 | body = f"""
55 | your mail - {instance.user.email} is no more associated with any college or classroom
56 | """
57 | except:
58 | cprint(
59 | "teacher profile deletion confirmation mail sending failed",
60 | "red",
61 | )
62 | except:
63 | pass
64 |
--------------------------------------------------------------------------------
/classroom/tasks.py:
--------------------------------------------------------------------------------
1 | from celery import shared_task
2 | from django.core.mail import send_mass_mail, send_mail
3 | from .model import AllowedTeacher
4 | import os
5 | from django.conf import settings
6 | import pandas as pd
7 |
8 |
9 | @shared_task
10 | def send_email_after_bulk_object_creation(subject: str, prompt: str, email_list):
11 | """
12 | # It sends mass mail
13 | - takes 3 `parameter`
14 | - subject
15 | - common prompt
16 | - email_list
17 | """
18 | mails = [
19 | (
20 | subject,
21 | f"{prompt} - \nUSED MAIL ID : {m}",
22 | settings.EMAIL_HOST_USER,
23 | [m],
24 | )
25 | for m in email_list
26 | ]
27 | send_mass_mail(mails, fail_silently=True)
28 |
--------------------------------------------------------------------------------
/classroom/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/classroom/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from .routers.students_urls import stud_urlpatterns
3 | from .routers.teacher_urls import teacher_urlpatterns
4 | from .routers.dba_urls import dba_urlpatterns
5 | from classroom.views.usertype_view import UserTypeAPIView
6 |
7 | urlpatterns = [
8 | path("user-type/", UserTypeAPIView.as_view(), name="user-category")
9 | ]
10 | urlpatterns += stud_urlpatterns + teacher_urlpatterns + dba_urlpatterns
11 |
--------------------------------------------------------------------------------
/classroom/validators.py:
--------------------------------------------------------------------------------
1 | from datetime import date
2 | from django.core.exceptions import ValidationError
3 |
4 |
5 | def is_no_of_sem_even(value):
6 | if value % 2 == 0:
7 | return value
8 | else:
9 | raise ValidationError("for a course number of sems have to be even")
10 |
11 |
12 | def pdf_file_size_lt_5mb(value):
13 | limit_no = 5
14 | limit = limit_no * 1024 * 1024
15 | if value.size > limit:
16 | raise ValidationError(f"File too large. Size should not exceed {limit_no} Mb.")
17 |
18 |
19 | def assignment_date_gte_today(date_val: date):
20 | if date_val < date.today():
21 | raise ValidationError(
22 | f"Assignment due date- [{date_val:%d-%m-%Y}] can not be less than current date - {(date.today()):%d-%m-%Y}"
23 | )
24 | return date_val
25 |
--------------------------------------------------------------------------------
/classroom/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/classroom/views/__init__.py
--------------------------------------------------------------------------------
/classroom/views/student_view.py:
--------------------------------------------------------------------------------
1 | from classroom.models.assignment import Assignment, AssignmentSubmission
2 | from classroom.serializers.classroom import (
3 | AnnouncementsReadSerializer,
4 | ClassroomReadForStudentSerializer,
5 | NotesReadForStudentSerializer,
6 | SemesterReadSerializer,
7 | SubjectReadSerializer,
8 | )
9 | from classroom.serializers.student import (
10 | AssignmentReadByStudentSerializer,
11 | AssignmentSubmissionReadByStudent,
12 | AssignmentSubmissionWriteByStudent,
13 | StudentReadSerializer,
14 | )
15 | from rest_framework.mixins import (
16 | ListModelMixin,
17 | RetrieveModelMixin,
18 | UpdateModelMixin,
19 | CreateModelMixin,
20 | DestroyModelMixin,
21 | )
22 | from rest_framework.viewsets import GenericViewSet, ModelViewSet
23 | from rest_framework.parsers import FormParser, MultiPartParser
24 | from ..model import Announcement, Classroom, Notes, Semester, Student, Subject
25 |
26 |
27 | class StudentProfileViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
28 | """
29 | Student End point will return student profile details along with classroom details
30 | """
31 |
32 | my_tags = ["[student] 1. profile"]
33 | queryset = (
34 | Student.objects.select_related("user")
35 | .select_related("classroom")
36 | .select_related("classroom__college")
37 | .all()
38 | )
39 | serializer_class = StudentReadSerializer
40 |
41 | def get_serializer_context(self):
42 | return {"request": self.request}
43 |
44 |
45 | class ClassroomForStudentViewSet(RetrieveModelMixin, GenericViewSet):
46 | """
47 | This view is used by student only
48 | student can only retrieve but won't be able to see the other classrooms
49 | """
50 |
51 | my_tags = ["[student] -. classroom"]
52 | swagger_schema = None
53 | serializer_class = ClassroomReadForStudentSerializer
54 | lookup_field = "slug"
55 |
56 | def get_queryset(self):
57 | classroom = (
58 | Classroom.objects.select_related("college")
59 | .prefetch_related("students")
60 | .filter(slug=self.kwargs.get("slug"))
61 | )
62 | return classroom
63 |
64 |
65 | class SemesterForStudentViewSet(
66 | ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericViewSet
67 | ):
68 | http_method_names = ["get", "patch", "head", "options"]
69 | my_tags = ["[student] 2. semester"]
70 | serializer_class = SemesterReadSerializer
71 | # lookup_field = 'id'
72 | def get_queryset(self):
73 | sem = Semester.objects.select_related("classroom").filter(
74 | classroom__slug=self.kwargs.get("classroom_slug")
75 | )
76 | return sem
77 |
78 |
79 | class SubjectsForStudentsViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
80 | my_tags = ["[student] 3. subjects"]
81 | serializer_class = SubjectReadSerializer
82 | lookup_field = "slug"
83 |
84 | def get_queryset(self):
85 | return (
86 | Subject.objects.select_related("semester")
87 | .select_related("semester__classroom")
88 | .filter(semester__id=self.kwargs.get("semester_pk"))
89 | ) # FIXME: This might be slow in future. Req: Optimization
90 |
91 |
92 | class AnnouncementForStudentsViewSet(ListModelMixin, GenericViewSet):
93 | """
94 | Announcements for the particular subject will be shown in decreasing order
95 | """
96 |
97 | my_tags = ["[student] 4. announcements/subject "]
98 | serializer_class = AnnouncementsReadSerializer
99 |
100 | def get_queryset(self):
101 | return Announcement.objects.select_related("subject").filter(
102 | subject__slug=self.kwargs.get("subject_slug")
103 | )
104 |
105 |
106 | class NotesForStudentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
107 | """
108 | Notes for the particular subject will be shown in decreasing order
109 | """
110 |
111 | my_tags = ["[student] 5. notes/subject"]
112 | serializer_class = NotesReadForStudentSerializer
113 | lookup_field = "slug"
114 |
115 | def get_queryset(self):
116 | return (
117 | Notes.objects.select_related("subject", "posted_by", "posted_by__user")
118 | .prefetch_related("attached_files")
119 | .filter(subject__slug=self.kwargs.get("subject_slug"))
120 | )
121 |
122 |
123 | # TODO: Add Assignment View
124 | class AssignmentViewForStudentViewSet(
125 | ListModelMixin, RetrieveModelMixin, GenericViewSet
126 | ):
127 | my_tags = ["[student] 6. assignments/subject"]
128 | serializer_class = AssignmentReadByStudentSerializer
129 | # parser_classes = [FormParser,MultiPartParser]
130 |
131 | def get_queryset(self):
132 | return Assignment.objects.select_related("subject").filter(
133 | subject__slug=self.kwargs.get("subject_slug")
134 | )
135 |
136 |
137 | class AssignmentSubmissionByStudentViewSet(ModelViewSet):
138 | http_method_names = ["get", "post", "delete", "head", "options"]
139 | my_tags = ["[student] 7. assignment submission"]
140 | serializer_class = AssignmentSubmissionWriteByStudent
141 | parser_classes = [
142 | FormParser,
143 | MultiPartParser,
144 | ]
145 |
146 | def get_queryset(self):
147 | # TODO:Dynamically find student id before submission
148 | student: Student = Student.objects.select_related("user").get(
149 | user__id=self.request.user.id
150 | )
151 | return AssignmentSubmission.objects.filter(
152 | assignment__id=self.kwargs.get("assignment_pk"), submitted_by=student.id
153 | )
154 |
155 | def get_serializer_context(self):
156 | return {
157 | "assignment_pk": self.kwargs.get("assignment_pk"),
158 | "user_id": self.request.user.id,
159 | }
160 |
161 | def get_serializer_class(self):
162 | method = self.request.method
163 | if method == "POST":
164 | return AssignmentSubmissionWriteByStudent
165 | return AssignmentSubmissionReadByStudent
166 |
--------------------------------------------------------------------------------
/classroom/views/usertype_view.py:
--------------------------------------------------------------------------------
1 | from rest_framework.generics import RetrieveAPIView as _rav
2 | from rest_framework import status
3 | from classroom.models.college_dba import CollegeDBA
4 | from classroom.models.student import Student
5 |
6 | from classroom.models.teacher import Teacher
7 | from rest_framework.response import Response
8 |
9 | from classroom.serializers.usertype import UserTypeSerializer
10 |
11 | # hello
12 | class UserTypeAPIView(_rav):
13 | my_tags = ["auth [user category]"]
14 |
15 | def get_serializer_class(self):
16 | return UserTypeSerializer
17 |
18 | def get(self, request, id, **kwargs):
19 | if Teacher.objects.select_related("user").filter(user__id=id).exists():
20 | teacher = (
21 | Teacher.objects.select_related("user").filter(user__id=id)
22 | # .only("id")
23 | .first()
24 | )
25 | return Response(
26 | {"user_type": "teacher", "teacher_id": teacher.id},
27 | status=status.HTTP_200_OK,
28 | )
29 |
30 | elif Student.objects.select_related("user").filter(user__id=id).exists():
31 | student = (
32 | Student.objects.select_related("user").filter(user__id=id)
33 | # .only("id")
34 | .first()
35 | )
36 | return Response(
37 | {"user_type": "student", "student_id": student.id},
38 | status=status.HTTP_200_OK,
39 | )
40 |
41 | elif CollegeDBA.objects.select_related("user").filter(user__id=id).exists():
42 |
43 | dba = (
44 | CollegeDBA.objects.select_related("user").filter(user__id=id)
45 | # .only("id")
46 | .first()
47 | )
48 | return Response(
49 | {"user_type": "college_dba", "dba_id": dba.id},
50 | status=status.HTTP_200_OK,
51 | )
52 |
53 | else:
54 |
55 | return Response(
56 | {"error": {"user_type": "user unknown"}},
57 | status=status.HTTP_404_NOT_FOUND,
58 | )
59 |
--------------------------------------------------------------------------------
/core/__init__.py:
--------------------------------------------------------------------------------
1 | from .celery import celery
2 |
--------------------------------------------------------------------------------
/core/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for core 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/4.0/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", "core.settings.dev")
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/core/celery.py:
--------------------------------------------------------------------------------
1 | import os
2 | from celery import Celery
3 | import celery
4 |
5 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings.dev")
6 |
7 | celery = Celery("core")
8 | celery.config_from_object("django.conf:settings", namespace="CELERY")
9 | celery.autodiscover_tasks()
10 |
--------------------------------------------------------------------------------
/core/mail.py:
--------------------------------------------------------------------------------
1 | class EmailCreds:
2 | def __init__(self) -> None:
3 | self.__mail_id__ = "django.dev.tmsl@gmail.com"
4 | self.EMAIL_HOST = "smtp.gmail.com"
5 | self.EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
6 | self.PORT = 587
7 |
8 | def get_mail_id(self) -> str:
9 | return self.__mail_id__
10 |
--------------------------------------------------------------------------------
/core/settings/common.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for core project.
3 |
4 | Generated by 'django-admin startproject' using Django 4.0.4.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.0/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/4.0/ref/settings/
11 | """
12 | import os
13 | from ..mail import EmailCreds
14 | from datetime import timedelta
15 | from pathlib import Path
16 |
17 | EMAIL_CREDS = EmailCreds()
18 |
19 |
20 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
21 | BASE_DIR = Path(__file__).resolve().parent.parent.parent # FIXME: .parent added
22 |
23 | # Application definition
24 |
25 | INSTALLED_APPS = [
26 | "django.contrib.admin",
27 | "django.contrib.auth",
28 | "django.contrib.contenttypes",
29 | "django.contrib.sessions",
30 | "django.contrib.messages",
31 | "django.contrib.staticfiles",
32 | ]
33 | THIRD_PARTY_APP = [
34 | "corsheaders",
35 | "rest_framework",
36 | "djoser",
37 | "django_extensions",
38 | "drf_yasg",
39 | ]
40 |
41 | OWN_APP = ["accounts", "classroom"]
42 |
43 | INSTALLED_APPS += THIRD_PARTY_APP + OWN_APP
44 |
45 | MIDDLEWARE = [
46 | "django.middleware.security.SecurityMiddleware",
47 | "whitenoise.middleware.WhiteNoiseMiddleware", # TODO: 3rd party middleware
48 | "django.contrib.sessions.middleware.SessionMiddleware",
49 | "django.middleware.common.CommonMiddleware",
50 | # "django.middleware.csrf.CsrfViewMiddleware",
51 | "django.contrib.auth.middleware.AuthenticationMiddleware",
52 | "django.contrib.messages.middleware.MessageMiddleware",
53 | "django.middleware.clickjacking.XFrameOptionsMiddleware",
54 | ]
55 |
56 |
57 | THIRD_PARTY_MIDDLEWARE = [
58 | "corsheaders.middleware.CorsMiddleware",
59 | ]
60 |
61 | MIDDLEWARE = THIRD_PARTY_MIDDLEWARE + MIDDLEWARE
62 |
63 |
64 | ROOT_URLCONF = "core.urls"
65 |
66 | TEMPLATES = [
67 | {
68 | "BACKEND": "django.template.backends.django.DjangoTemplates",
69 | "DIRS": [],
70 | "APP_DIRS": True,
71 | "OPTIONS": {
72 | "context_processors": [
73 | "django.template.context_processors.debug",
74 | "django.template.context_processors.request",
75 | "django.contrib.auth.context_processors.auth",
76 | "django.contrib.messages.context_processors.messages",
77 | ],
78 | },
79 | },
80 | ]
81 |
82 | WSGI_APPLICATION = "core.wsgi.application"
83 |
84 |
85 | # Password validation
86 | # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
87 |
88 | AUTH_PASSWORD_VALIDATORS = [
89 | {
90 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
91 | },
92 | {
93 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
94 | },
95 | {
96 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
97 | },
98 | {
99 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
100 | },
101 | ]
102 |
103 |
104 | # Internationalization
105 | # https://docs.djangoproject.com/en/4.0/topics/i18n/
106 |
107 | LANGUAGE_CODE = "en-us"
108 |
109 | TIME_ZONE = "Asia/Kolkata"
110 | # TIME_ZONE = "UTC"
111 |
112 | USE_I18N = True
113 |
114 | # USE_L10N = True
115 |
116 | USE_TZ = True
117 |
118 |
119 | # Static files (CSS, JavaScript, Images)
120 | # https://docs.djangoproject.com/en/4.0/howto/static-files/
121 |
122 | STATIC_URL = "static/"
123 | STATIC_ROOT = os.path.join(
124 | BASE_DIR, "static"
125 | ) # This line will collect all static files
126 |
127 | MEDIA_URL = "media/"
128 | MEDIA_ROOT = os.path.join(BASE_DIR, "media")
129 | # STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" # FIXME: if not works, delete these line
130 |
131 | # Default primary key field type
132 | # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
133 |
134 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
135 |
136 | # -----------------------------#3-RD PARTY APPS CONFIG -------------------
137 |
138 |
139 | # TODO: Djoser Settings Open
140 | DJOSER = {
141 | # "SET_PASSWORD_RETYPE": True, #TODO: Confirm Password [Djoser]
142 | "SEND_CONFIRMATION_EMAIL": True,
143 | "SEND_ACTIVATION_EMAIL": True,
144 | "ACTIVATION_URL": "activate/{uid}/{token}",
145 | "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True,
146 | "PASSWORD_RESET_CONFIRM_URL": "reset_password_confirm/{uid}/{token}",
147 | "USERNAME_CHANGED_EMAIL_CONFIRMATION": True,
148 | "USERNAME_RESET_CONFIRM_URL": "reset_email_confirm/{uid}/{token}",
149 | # "PASSWORD_RESET_SHOW_EMAIL_NOT_FOUND": False, #TODO: if want to expose details about email then open it else leave it
150 | "SERIALIZERS": {
151 | "current_user": "accounts.serializers.CurrentUserSerializer",
152 | "username_reset_confirm": "accounts.serializers.UseranmeResetConfirmSerializer",
153 | },
154 | }
155 |
156 | SWAGGER_SETTINGS = {
157 | "DEFAULT_AUTO_SCHEMA_CLASS": "core.swagger_schema.CustomAutoSchema",
158 | "LOGIN_URL": "admin/",
159 | "LOGOUT_URL": "admin/logout",
160 | "OPERATIONS_SORTER": "method",
161 | "TAGS_SORTER": "alpha",
162 | "DOC_EXPANSION": "none",
163 | }
164 |
165 | SIMPLE_JWT = {
166 | "AUTH_HEADER_TYPES": ("JWT",),
167 | "ACCESS_TOKEN_LIFETIME": timedelta(days=7),
168 | "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
169 | "UPDATE_LAST_LOGIN": True,
170 | }
171 |
172 | REST_FRAMEWORK = {
173 | "DEFAULT_AUTHENTICATION_CLASSES": (
174 | "rest_framework_simplejwt.authentication.JWTAuthentication",
175 | ),
176 | "DEFAULT_PERMISSION_CLASSES": [
177 | "rest_framework.permissions.AllowAny",
178 | ],
179 | # "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", #TODO: Pagination Setting Open 1
180 | # "PAGE_SIZE": 5, # TODO: default pagination value:5
181 | }
182 | # -----------------------------END of #3-RD PARTY APPS CONFIG -------------------
183 |
184 | # ======== CUSTOM CONSTANTS --------------------------------------------------
185 |
186 | CORS_ALLOW_ALL_ORIGINS = True
187 | # CORS_ALLOW_CREDENTIALS = False
188 | CORS_ALLOW_HEADERS = [
189 | "accept",
190 | "filename",
191 | "accept-encoding",
192 | "authorization",
193 | "content-type",
194 | "content-disposition",
195 | "dnt",
196 | "boundary",
197 | "origin",
198 | "user-agent",
199 | "x-csrftoken",
200 | "x-requested-with",
201 | "csrfmiddlewaretoken",
202 | ]
203 |
204 |
205 | AUTH_USER_MODEL = "accounts.BaseAccount"
206 |
207 | DOMAIN = "localhost:8081" # TODO: change after frontend deployment
208 | SITE_NAME = "Classroom[LMS]"
209 |
--------------------------------------------------------------------------------
/core/settings/dev.py:
--------------------------------------------------------------------------------
1 | from .common import *
2 |
3 | # SECURITY WARNING: keep the secret key used in production secret!
4 | SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "Not Exists")
5 |
6 | # TODO: Make this false in production
7 | DEBUG = True
8 |
9 | ALLOWED_HOSTS = ["*", "localhost"]
10 |
11 | DATABASES = {
12 | "default": {
13 | "ENGINE": "django.db.backends.mysql",
14 | "NAME": "classroom",
15 | "USER": "root",
16 | "PASSWORD": "Abcd_1234",
17 | "HOST": "mysql",
18 | "PORT": "3306",
19 | }
20 | # 'default': {
21 | # 'ENGINE': 'django.db.backends.postgresql',
22 | # 'NAME': os.environ.get('POSTGRES_NAME','classroom'),
23 | # 'USER': os.environ.get('POSTGRES_USER','postgres'),
24 | # 'PASSWORD': os.environ.get('POSTGRES_PASSWORD','postgres'),
25 | # 'HOST': 'postgres',
26 | # 'PORT': 5432,
27 | # }
28 | }
29 |
30 | DEV_APPS = [
31 | "debug_toolbar",
32 | ]
33 |
34 | DEV_MIDDLEWARE = [
35 | "debug_toolbar.middleware.DebugToolbarMiddleware",
36 | ]
37 | MIDDLEWARE += DEV_MIDDLEWARE
38 | INSTALLED_APPS += DEV_APPS
39 | INTERNAL_IPS = [
40 | "127.0.0.1",
41 | ]
42 |
43 | # TODO: FAKE-MAIL Comment this in production
44 | # TODO:Mail Settings for Fake Mail
45 | EMAIL_BACKEND = EMAIL_CREDS.EMAIL_BACKEND
46 | # EMAIL_HOST = "localhost"
47 | EMAIL_HOST = "smtp4dev"
48 | EMAIL_PORT = 25
49 | EMAIL_HOST_USER = "classroom@lms.com"
50 | EMAIL_HOST_PASSWORD = ""
51 | # DEFAULT_FROM_EMAIL = "classroom@lms.com"
52 |
53 | # CELERY_BROKER_URL = "redis://localhost:6379/1"
54 | CELERY_BROKER_URL = "redis://redis:6379/1"
55 |
56 | DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": lambda request: True}
57 |
--------------------------------------------------------------------------------
/core/settings/prod.py:
--------------------------------------------------------------------------------
1 | from .common import *
2 | import os
3 | import dj_database_url
4 |
5 |
6 | # SECURITY WARNING: keep the secret key used in production secret!
7 | SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY") # TODO: SET DJANGO_SECRET_KEY
8 |
9 | DEBUG = False
10 |
11 | ALLOWED_HOSTS = [
12 | "lms-classroom-api.herokuapp.com"
13 | ] # TODO: allow only localhost , port-> 3000,8000,5000,8081
14 |
15 | DATABASES = {"default": dj_database_url.config()}
16 |
17 |
18 | # TODO: Mail Settings for Original GMAIL
19 | EMAIL_BACKEND = EMAIL_CREDS.EMAIL_BACKEND
20 | EMAIL_HOST = EMAIL_CREDS.EMAIL_HOST
21 | EMAIL_PORT = EMAIL_CREDS.PORT
22 | EMAIL_HOST_USER = EMAIL_CREDS.get_mail_id()
23 | EMAIL_HOST_PASSWORD = os.environ.get("EMAIL_HOST_PASSWORD")
24 | EMAIL_USE_TLS = True
25 | TIME_ZONE = "Asia/Kolkata"
26 |
27 | CELERY_BROKER_URL = os.environ["REDIS_URL"]
28 |
--------------------------------------------------------------------------------
/core/swagger_schema.py:
--------------------------------------------------------------------------------
1 | from drf_yasg.inspectors import SwaggerAutoSchema
2 |
3 |
4 | class CustomAutoSchema(SwaggerAutoSchema):
5 | def get_tags(self, operation_keys=None):
6 | tags = self.overrides.get("tags", None) or getattr(self.view, "my_tags", [])
7 | if not tags:
8 | tags = [operation_keys[0]]
9 |
10 | return tags
11 |
--------------------------------------------------------------------------------
/core/urls.py:
--------------------------------------------------------------------------------
1 | # CORE>URLS
2 | from django.conf import settings
3 | from django.conf.urls.static import static
4 | from django.contrib import admin
5 | from django.urls import path, include
6 |
7 | # DRF_YASG
8 | from rest_framework import permissions
9 | from drf_yasg.views import get_schema_view
10 | from drf_yasg import openapi
11 |
12 |
13 | schema_view = get_schema_view(
14 | openapi.Info(
15 | title="Classroom(LMS) API",
16 | default_version="1.0.0",
17 | description="""
18 | ## This is the **`Classroom API`** documentation
19 |
20 | > - ### Here all the api routes are grouped by tags
21 | > - ### Classroom, Teachers, DBA, Students, Routers configured
22 | """,
23 | contact=openapi.Contact(email="django.dev.tmsl@gmail.com"),
24 | ),
25 | public=True,
26 | permission_classes=[permissions.AllowAny],
27 | )
28 | # DRF_YASG
29 |
30 |
31 | urlpatterns = [
32 | path("classroom-app/", include("classroom.urls")),
33 | path("admin/", admin.site.urls),
34 | path("auth/", include("djoser.urls")),
35 | path("login/", include("djoser.urls.jwt")),
36 | path("", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
37 | ]
38 |
39 | if settings.DEBUG:
40 | import debug_toolbar
41 |
42 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
43 | urlpatterns += (path("__debug__/", include(debug_toolbar.urls)),)
44 |
45 | # from termcolor import cprint
46 | # for url in urlpatterns:
47 | # cprint(url,'blue')
48 | # cprint('-------------------------------','blue')
49 |
--------------------------------------------------------------------------------
/core/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for core 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/4.0/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", "core.settings.dev")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '4.8.2'
2 | services:
3 | web:
4 | build: .
5 | command: ./wait-for-it.sh mysql:3306 -- ./docker-entrypoint.sh
6 | ports:
7 | - 8000:8000
8 | depends_on:
9 | # - mysql
10 | - redis
11 | - celery
12 | - smtp4dev
13 | restart: on-failure
14 | container_name: classroom_api
15 | volumes:
16 | - .:/app
17 | mysql:
18 | image: mysql:8.0
19 | container_name: mysql
20 | ports:
21 | - 3306:3306
22 | restart: always
23 | cap_add:
24 | - SYS_NICE # CAP_SYS_NICE
25 | environment:
26 | - MYSQL_DATABASE=classroom
27 | - MYSQL_ROOT_PASSWORD=Abcd_1234
28 | volumes:
29 | - mysqldata:/var/lib/mysql
30 | redis:
31 | image: redis:6.2-alpine
32 | container_name: redis
33 | ports:
34 | - 6379:6379
35 | restart: always
36 | volumes:
37 | - redisdata:/data
38 | smtp4dev:
39 | image: rnwood/smtp4dev
40 | container_name: email
41 | ports:
42 | - 5000:80
43 | - 25:25
44 | restart: always
45 | celery:
46 | build: .
47 | command: celery -A core worker -l info -P eventlet -E
48 | depends_on:
49 | - redis
50 | container_name: celery
51 | volumes:
52 | - .:/app
53 | volumes:
54 | mysqldata:
55 | redisdata:
56 | # pgdata:
57 |
58 | # celery-beat:
59 | # build: .
60 | # command: celery -A storefront beat --loglevel=info
61 | # depends_on:
62 | # - redis
63 | # volumes:
64 | # - .:/app
65 | # flower:
66 | # build: .
67 | # command: celery -A storefront flower
68 | # depends_on:
69 | # - web
70 | # - redis
71 | # - celery
72 | # environment:
73 | # - DEBUG=1
74 | # - CELERY_BROKER=redis://redis:6379/0
75 | # - CELERY_BACKEND=redis://redis:6379/0
76 | # ports:
77 | # - 5555:5555
78 | # tests:
79 | # build: .
80 | # command: ./wait-for-it.sh mysql:3306 -- ptw
81 | # depends_on:
82 | # - redis
83 | # - mysql
84 | # tty: true
85 | # volumes:
86 | # - .:/app
87 | # postgres:
88 | # image: postgres
89 | # ports:
90 | # - 5432:5432
91 | # volumes:
92 | # - pgdata:/var/lib/postgresql/data
93 | # environment:
94 | # - POSTGRES_DB=classroom
95 | # - POSTGRES_USER=postgres
96 | # - POSTGRES_PASSWORD=postgres
--------------------------------------------------------------------------------
/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Apply database migrations
4 | echo "Apply database migrations"
5 | python manage.py migrate
6 |
7 | # Start server
8 | echo "Starting server"
9 | python manage.py runserver 0.0.0.0:8000
10 |
11 |
--------------------------------------------------------------------------------
/docs/scripts.md:
--------------------------------------------------------------------------------
1 | ### CMD to Run Server for Frontend
2 |
3 | ---
4 |
5 | ```bash
6 | ngrok http 8000
7 | ```
8 |
9 | ### FAKE SMTP MAIL
10 |
11 | ---
12 |
13 | ```docker
14 | docker run --rm -it -p 5000:80 -p 2525:25 rnwood/smtp4dev
15 | ```
16 |
17 | ## Celery - Redis CMDs
18 |
19 | - pipenv run redis
20 | - celery -A core worker -l info -P eventlet
21 | - or pipenv run celery
22 |
23 | ## HEROKU CMDS
24 |
25 | ---
26 |
27 | ```bash
28 | heroku create app-name
29 | ```
30 |
31 | output:
32 |
33 | ```bash
34 | https://app-url.com/ | https://git.heroku.com/app.git
35 | ```
36 |
37 | > **`lms-classroom-api.herokuapp.com`** add this in allowed_host of prod.py
38 |
39 | > **`https://git.heroku.com/lms-classroom-api.git`** heroku git repo
40 |
41 | #### config environment variables in heroku
42 |
43 | ---
44 |
45 | ```bash
46 | heroku config:set VARIABLE_NAME='value'
47 | ```
48 |
49 | > ### deploy app
50 |
51 | ---
52 |
53 | - #### step 1: `git remote -vv`
54 | - #### step 2: `git branch`
55 | - #### step 3: push branch to heroku `git push heroku main`
56 |
57 | # Docker Build & Start
58 |
59 | ```bash
60 | docker-compose up --build
61 | ```
62 |
63 | ```docker
64 | docker-compose run web python manage.py createsuperuser
65 | ```
66 |
67 | ---
68 |
69 | ```bash
70 | docker-compose up
71 | ```
72 |
--------------------------------------------------------------------------------
/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", "core.settings.dev")
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 |
--------------------------------------------------------------------------------
/readme/BackendAPI_Doc.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/BackendAPI_Doc.jpeg
--------------------------------------------------------------------------------
/readme/CreateCollegePage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/CreateCollegePage.jpeg
--------------------------------------------------------------------------------
/readme/DjangoADMIN.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/DjangoADMIN.jpeg
--------------------------------------------------------------------------------
/readme/Docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/Docker.png
--------------------------------------------------------------------------------
/readme/HomePage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/HomePage.jpeg
--------------------------------------------------------------------------------
/readme/LogInPage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/LogInPage.jpeg
--------------------------------------------------------------------------------
/readme/Major Project Presentation.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/Major Project Presentation.pptx
--------------------------------------------------------------------------------
/readme/Major Project SEM_4 Doc Final Group.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/Major Project SEM_4 Doc Final Group.pdf
--------------------------------------------------------------------------------
/readme/SignUpPage.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/SignUpPage.jpeg
--------------------------------------------------------------------------------
/readme/SubjectAddByStudent.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/SubjectAddByStudent.jpeg
--------------------------------------------------------------------------------
/readme/celery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/celery.png
--------------------------------------------------------------------------------
/readme/djangoIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/djangoIcon.png
--------------------------------------------------------------------------------
/readme/drf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/drf.png
--------------------------------------------------------------------------------
/readme/python.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/python.png
--------------------------------------------------------------------------------
/readme/redis_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/redis_icon.png
--------------------------------------------------------------------------------
/readme/smtp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/smtp.png
--------------------------------------------------------------------------------
/readme/whole_sw_review.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PritamChk/ClassroomBackend/58a15ca67248e126e92bb9f340b39eff86705a5d/readme/whole_sw_review.mp4
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # These requirements were autogenerated by pipenv
3 | # To regenerate from the project's Pipfile, run:
4 | #
5 | # pipenv lock --requirements
6 | #
7 |
8 | -i https://pypi.org/simple
9 | amqp==5.1.1; python_version >= '3.6'
10 | asgiref==3.5.1; python_version >= '3.7'
11 | async-timeout==4.0.2; python_version >= '3.6'
12 | billiard==3.6.4.0
13 | celery==5.2.6
14 | certifi==2021.10.8
15 | cffi==1.15.0
16 | charset-normalizer==2.0.12; python_version >= '3'
17 | click-didyoumean==0.3.0; python_full_version >= '3.6.2' and python_full_version < '4.0.0'
18 | click-plugins==1.1.1
19 | click-repl==0.2.0
20 | click==8.1.3; python_version >= '3.7'
21 | colorama==0.4.4; platform_system == 'Windows'
22 | coreapi==2.3.3
23 | coreschema==0.0.4
24 | cryptography==37.0.2; python_version >= '3.6'
25 | defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
26 | deprecated==1.2.13; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
27 | dj-database-url==0.5.0
28 | django-cors-headers==3.12.0
29 | django-extensions==3.1.5
30 | django-templated-mail==1.1.1
31 | django==4.0.4
32 | djangorestframework-simplejwt==4.8.0
33 | djangorestframework==3.13.1
34 | djoser==2.1.0
35 | dnspython==2.2.1; python_version >= '3.6' and python_full_version < '4.0.0'
36 | drf-nested-routers==0.93.4
37 | drf-writable-nested==0.6.3
38 | drf-yasg==1.20.0
39 | et-xmlfile==1.1.0; python_version >= '3.6'
40 | eventlet==0.33.1
41 | greenlet==2.0.0a2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
42 | gunicorn==20.1.0
43 | idna==3.3; python_version >= '3'
44 | inflection==0.5.1; python_version >= '3.5'
45 | itypes==1.2.0
46 | jinja2==3.1.2; python_version >= '3.7'
47 | kombu==5.2.4; python_version >= '3.7'
48 | markdown==3.3.7
49 | markupsafe==2.1.1; python_version >= '3.7'
50 | numpy==1.22.3; python_version >= '3.10'
51 | oauthlib==3.2.0; python_version >= '3.6'
52 | openpyxl==3.0.9
53 | packaging==21.3; python_version >= '3.6'
54 | pandas==1.4.2
55 | prompt-toolkit==3.0.29; python_full_version >= '3.6.2'
56 | psycopg2==2.9.3
57 | pycparser==2.21
58 | pyjwt==2.4.0; python_version >= '3.6'
59 | pyparsing==3.0.9; python_full_version >= '3.6.8'
60 | python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
61 | python3-openid==3.2.0
62 | pytz==2022.1
63 | redis==4.3.1
64 | requests-oauthlib==1.3.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
65 | requests==2.27.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
66 | ruamel.yaml.clib==0.2.6; python_version < '3.11' and platform_python_implementation == 'CPython'
67 | ruamel.yaml==0.17.21; python_version >= '3'
68 | setuptools==62.2.0; python_version >= '3.7'
69 | six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
70 | social-auth-app-django==4.0.0
71 | social-auth-core==4.2.0; python_version >= '3.6'
72 | sqlparse==0.4.2; python_version >= '3.5'
73 | termcolor==1.1.0
74 | tzdata==2022.1; sys_platform == 'win32'
75 | uritemplate==4.1.1; python_version >= '3.6'
76 | urllib3==1.26.9; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_full_version < '4.0.0'
77 | vine==5.0.0; python_version >= '3.6'
78 | wcwidth==0.2.5
79 | whitenoise==6.1.0
80 | wrapt==1.14.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
81 |
--------------------------------------------------------------------------------
/wait-for-it.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | # Use this script to test if a given TCP host/port are available
3 |
4 | WAITFORIT_cmdname=${0##*/}
5 |
6 | echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
7 |
8 | usage()
9 | {
10 | cat << USAGE >&2
11 | Usage:
12 | $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
13 | -h HOST | --host=HOST Host or IP under test
14 | -p PORT | --port=PORT TCP port under test
15 | Alternatively, you specify the host and port as host:port
16 | -s | --strict Only execute subcommand if the test succeeds
17 | -q | --quiet Don't output any status messages
18 | -t TIMEOUT | --timeout=TIMEOUT
19 | Timeout in seconds, zero for no timeout
20 | -- COMMAND ARGS Execute command with args after the test finishes
21 | USAGE
22 | exit 1
23 | }
24 |
25 |
26 | wait_for()
27 | {
28 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
29 | echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
30 | else
31 | echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
32 | fi
33 | WAITFORIT_start_ts=$(date +%s)
34 | while :
35 | do
36 | if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
37 | nc -z $WAITFORIT_HOST $WAITFORIT_PORT
38 | WAITFORIT_result=$?
39 | else
40 | (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
41 | WAITFORIT_result=$?
42 | fi
43 | if [[ $WAITFORIT_result -eq 0 ]]; then
44 | WAITFORIT_end_ts=$(date +%s)
45 | echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
46 | break
47 | fi
48 | sleep 1
49 | done
50 | return $WAITFORIT_result
51 | }
52 |
53 | wait_for_wrapper()
54 | {
55 | # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
56 | if [[ $WAITFORIT_QUIET -eq 1 ]]; then
57 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
58 | else
59 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
60 | fi
61 | WAITFORIT_PID=$!
62 | trap "kill -INT -$WAITFORIT_PID" INT
63 | wait $WAITFORIT_PID
64 | WAITFORIT_RESULT=$?
65 | if [[ $WAITFORIT_RESULT -ne 0 ]]; then
66 | echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
67 | fi
68 | return $WAITFORIT_RESULT
69 | }
70 |
71 | # process arguments
72 | while [[ $# -gt 0 ]]
73 | do
74 | case "$1" in
75 | *:* )
76 | WAITFORIT_hostport=(${1//:/ })
77 | WAITFORIT_HOST=${WAITFORIT_hostport[0]}
78 | WAITFORIT_PORT=${WAITFORIT_hostport[1]}
79 | shift 1
80 | ;;
81 | --child)
82 | WAITFORIT_CHILD=1
83 | shift 1
84 | ;;
85 | -q | --quiet)
86 | WAITFORIT_QUIET=1
87 | shift 1
88 | ;;
89 | -s | --strict)
90 | WAITFORIT_STRICT=1
91 | shift 1
92 | ;;
93 | -h)
94 | WAITFORIT_HOST="$2"
95 | if [[ $WAITFORIT_HOST == "" ]]; then break; fi
96 | shift 2
97 | ;;
98 | --host=*)
99 | WAITFORIT_HOST="${1#*=}"
100 | shift 1
101 | ;;
102 | -p)
103 | WAITFORIT_PORT="$2"
104 | if [[ $WAITFORIT_PORT == "" ]]; then break; fi
105 | shift 2
106 | ;;
107 | --port=*)
108 | WAITFORIT_PORT="${1#*=}"
109 | shift 1
110 | ;;
111 | -t)
112 | WAITFORIT_TIMEOUT="$2"
113 | if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
114 | shift 2
115 | ;;
116 | --timeout=*)
117 | WAITFORIT_TIMEOUT="${1#*=}"
118 | shift 1
119 | ;;
120 | --)
121 | shift
122 | WAITFORIT_CLI=("$@")
123 | break
124 | ;;
125 | --help)
126 | usage
127 | ;;
128 | *)
129 | echoerr "Unknown argument: $1"
130 | usage
131 | ;;
132 | esac
133 | done
134 |
135 | if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
136 | echoerr "Error: you need to provide a host and port to test."
137 | usage
138 | fi
139 |
140 | WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
141 | WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
142 | WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
143 | WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
144 |
145 | # Check to see if timeout is from busybox?
146 | WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
147 | WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
148 |
149 | WAITFORIT_BUSYTIMEFLAG=""
150 | if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
151 | WAITFORIT_ISBUSY=1
152 | # Check if busybox timeout uses -t flag
153 | # (recent Alpine versions don't support -t anymore)
154 | if timeout &>/dev/stdout | grep -q -e '-t '; then
155 | WAITFORIT_BUSYTIMEFLAG="-t"
156 | fi
157 | else
158 | WAITFORIT_ISBUSY=0
159 | fi
160 |
161 | if [[ $WAITFORIT_CHILD -gt 0 ]]; then
162 | wait_for
163 | WAITFORIT_RESULT=$?
164 | exit $WAITFORIT_RESULT
165 | else
166 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
167 | wait_for_wrapper
168 | WAITFORIT_RESULT=$?
169 | else
170 | wait_for
171 | WAITFORIT_RESULT=$?
172 | fi
173 | fi
174 |
175 | if [[ $WAITFORIT_CLI != "" ]]; then
176 | if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
177 | echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
178 | exit $WAITFORIT_RESULT
179 | fi
180 | exec "${WAITFORIT_CLI[@]}"
181 | else
182 | exit $WAITFORIT_RESULT
183 | fi
--------------------------------------------------------------------------------