Merge branch 'main' into feature/auto-populate-data
This commit is contained in:
commit
14448249c4
78
README.md
78
README.md
@ -6,39 +6,39 @@ Let's enhance the project by contributing! 👩💻👩💻
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Current features
|
## Current features
|
||||||
----------------
|
|
||||||
* Dashboard: School demographics and analytics. Restricted to only admins
|
- Dashboard: School demographics and analytics. Restricted to only admins
|
||||||
* News And Events: All users can access this page
|
- News And Events: All users can access this page
|
||||||
* Admin manages students(Add, Update, Delete)
|
- Admin manages students(Add, Update, Delete)
|
||||||
* Admin manages lecturers(Add, Update, Delete)
|
- Admin manages lecturers(Add, Update, Delete)
|
||||||
* Students can Add and Drop courses
|
- Students can Add and Drop courses
|
||||||
* Lecturers submit students' scores: _Attendance, Mid exam, Final exam, assignment_
|
- Lecturers submit students' scores: _Attendance, Mid exam, Final exam, assignment_
|
||||||
* The system calculates students' _Total, average, point, and grades automatically_
|
- The system calculates students' _Total, average, point, and grades automatically_
|
||||||
* Grade comment for each student with a **pass**, **fail**, or **pass with a warning**
|
- Grade comment for each student with a **pass**, **fail**, or **pass with a warning**
|
||||||
* Assessment result page for students
|
- Assessment result page for students
|
||||||
* Grade result page for students
|
- Grade result page for students
|
||||||
* Session/year and semester management
|
- Session/year and semester management
|
||||||
* Assessments and grades will be grouped by semester
|
- Assessments and grades will be grouped by semester
|
||||||
* Upload video and documentation for each course
|
- Upload video and documentation for each course
|
||||||
* PDF generator for students' registration slip and grade result
|
- PDF generator for students' registration slip and grade result
|
||||||
* Page access restriction
|
- Page access restriction
|
||||||
* Storing of quiz results under each user
|
- Storing of quiz results under each user
|
||||||
* Question order randomization
|
- Question order randomization
|
||||||
* Previous quiz scores can be viewed on the category page
|
- Previous quiz scores can be viewed on the category page
|
||||||
* Correct answers can be shown after each question or all at once at the end
|
- Correct answers can be shown after each question or all at once at the end
|
||||||
* Logged-in users can return to an incomplete quiz to finish it and non-logged-in users can complete a quiz if their session persists
|
- Logged-in users can return to an incomplete quiz to finish it and non-logged-in users can complete a quiz if their session persists
|
||||||
* The quiz can be limited to one attempt per user
|
- The quiz can be limited to one attempt per user
|
||||||
* Questions can be given a category
|
- Questions can be given a category
|
||||||
* Success rate for each category can be monitored on a progress page
|
- Success rate for each category can be monitored on a progress page
|
||||||
* Explanation for each question result can be given
|
- Explanation for each question result can be given
|
||||||
* Pass marks can be set
|
- Pass marks can be set
|
||||||
* Multiple choice question type
|
- Multiple choice question type
|
||||||
* True/False question type
|
- True/False question type
|
||||||
* Essay question type
|
- Essay question type
|
||||||
* Custom message displayed for those that pass or fail a quiz
|
- Custom message displayed for those that pass or fail a quiz
|
||||||
* Custom permission (view_sittings) added, allowing users with that permission to view quiz results from users
|
- Custom permission (view_sittings) added, allowing users with that permission to view quiz results from users
|
||||||
* A marking page which lists completed quizzes, can be filtered by quiz or user, and is used to mark essay questions
|
- A marking page which lists completed quizzes, can be filtered by quiz or user, and is used to mark essay questions
|
||||||
|
|
||||||
# Quick note for future contributors
|
# Quick note for future contributors
|
||||||
|
|
||||||
@ -68,14 +68,19 @@ pip install -r requirements.txt
|
|||||||
- Create `.env` file inside the root directory and include the following variables
|
- Create `.env` file inside the root directory and include the following variables
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Database config
|
||||||
DB_NAME=[YOUR_DB_NAME]
|
DB_NAME=[YOUR_DB_NAME]
|
||||||
DB_USER=[DB_ADMIN_NAME]
|
DB_USER=[DB_ADMIN_NAME]
|
||||||
DB_PASSWORD=[DB_ADMIN_PASSWORD]
|
DB_PASSWORD=[DB_ADMIN_PASSWORD]
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_PORT=[YOUR_POSTGRES_PORT default is 5432]
|
DB_PORT=[YOUR_POSTGRES_PORT default is 5432]
|
||||||
USER_EMAIL=[YOUR_EMAIL]
|
|
||||||
USER_PASSWORD=[EMAIL_PASSWORD]
|
# Email config
|
||||||
EMAIL_FROM_ADDRESS=[THE DEFAULT FROM ADDRESS FOR SENT EMAILS]
|
EMAIL_FROM_ADDRESS=Django LMS <youremail@example.com>
|
||||||
|
EMAIL_HOST_USER=[YOUR_EMAIL]
|
||||||
|
EMAIL_HOST_PASSWORD=[YOUR_EMAIL_PASSWORD]
|
||||||
|
|
||||||
|
# Other
|
||||||
DEBUG=True
|
DEBUG=True
|
||||||
SECRET_KEY=[YOUR_SECRET_KEY]
|
SECRET_KEY=[YOUR_SECRET_KEY]
|
||||||
```
|
```
|
||||||
@ -91,6 +96,7 @@ python manage.py runserver
|
|||||||
Last but not least, go to this address http://127.0.0.1:8000
|
Last but not least, go to this address http://127.0.0.1:8000
|
||||||
|
|
||||||
### References
|
### References
|
||||||
|
|
||||||
- Quiz part: https://github.com/tomwalker/django_quiz
|
- Quiz part: https://github.com/tomwalker/django_quiz
|
||||||
|
|
||||||
# Connect with me
|
# Connect with me
|
||||||
|
|||||||
@ -33,34 +33,42 @@ class LecturerFilter(django_filters.FilterSet):
|
|||||||
|
|
||||||
|
|
||||||
class StudentFilter(django_filters.FilterSet):
|
class StudentFilter(django_filters.FilterSet):
|
||||||
student__username = django_filters.CharFilter(lookup_expr="exact", label="")
|
id_no = django_filters.CharFilter(
|
||||||
student__name = django_filters.CharFilter(method="filter_by_name", label="")
|
field_name="student__username", lookup_expr="exact", label=""
|
||||||
student__email = django_filters.CharFilter(lookup_expr="icontains", label="")
|
)
|
||||||
program__title = django_filters.CharFilter(lookup_expr="icontains", label="")
|
name = django_filters.CharFilter(
|
||||||
|
field_name="student__name", method="filter_by_name", label=""
|
||||||
|
)
|
||||||
|
email = django_filters.CharFilter(
|
||||||
|
field_name="student__email", lookup_expr="icontains", label=""
|
||||||
|
)
|
||||||
|
program = django_filters.CharFilter(
|
||||||
|
field_name="student__program", lookup_expr="icontains", label=""
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Student
|
model = Student
|
||||||
fields = [
|
fields = [
|
||||||
"student__username",
|
"id_no",
|
||||||
"student__name",
|
"name",
|
||||||
"student__email",
|
"email",
|
||||||
"program__title",
|
"program",
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
# Change html classes and placeholders
|
# Change html classes and placeholders
|
||||||
self.filters["student__username"].field.widget.attrs.update(
|
self.filters["id_no"].field.widget.attrs.update(
|
||||||
{"class": "au-input", "placeholder": "ID No."}
|
{"class": "au-input", "placeholder": "ID No."}
|
||||||
)
|
)
|
||||||
self.filters["student__name"].field.widget.attrs.update(
|
self.filters["name"].field.widget.attrs.update(
|
||||||
{"class": "au-input", "placeholder": "Name"}
|
{"class": "au-input", "placeholder": "Name"}
|
||||||
)
|
)
|
||||||
self.filters["student__email"].field.widget.attrs.update(
|
self.filters["email"].field.widget.attrs.update(
|
||||||
{"class": "au-input", "placeholder": "Email"}
|
{"class": "au-input", "placeholder": "Email"}
|
||||||
)
|
)
|
||||||
self.filters["program__title"].field.widget.attrs.update(
|
self.filters["program"].field.widget.attrs.update(
|
||||||
{"class": "au-input", "placeholder": "Program"}
|
{"class": "au-input", "placeholder": "Program"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -118,22 +118,16 @@ class StaffAddForm(UserCreationForm):
|
|||||||
user.address = self.cleaned_data.get("address")
|
user.address = self.cleaned_data.get("address")
|
||||||
user.email = self.cleaned_data.get("email")
|
user.email = self.cleaned_data.get("email")
|
||||||
|
|
||||||
# Generate a username based on first and last name and registration date
|
# Generate a username
|
||||||
registration_date = datetime.now().strftime("%Y%m%d%H%M")
|
registration_date = datetime.now().strftime("%Y")
|
||||||
|
total_lecturers_count = User.objects.filter(is_lecturer=True).count()
|
||||||
generated_username = (
|
generated_username = (
|
||||||
f"{user.first_name.lower()}{user.last_name.lower()}{registration_date}"
|
f"{settings.LECTURER_ID_PREFIX}-{registration_date}-{total_lecturers_count}"
|
||||||
)
|
)
|
||||||
|
# Generate a password
|
||||||
# Check if the generated username already exists, and regenerate if needed
|
generated_password = User.objects.make_random_password()
|
||||||
while User.objects.filter(username=generated_username).exists():
|
|
||||||
registration_date = datetime.now().strftime("%Y%m%d%H%M")
|
|
||||||
generated_username = f"{user.first_name.lower()}{user.last_name.lower()}{registration_date}".replace(
|
|
||||||
" ", ""
|
|
||||||
)
|
|
||||||
|
|
||||||
user.username = generated_username
|
user.username = generated_username
|
||||||
|
|
||||||
generated_password = User.objects.make_random_password()
|
|
||||||
user.set_password(generated_password)
|
user.set_password(generated_password)
|
||||||
|
|
||||||
if commit:
|
if commit:
|
||||||
@ -141,7 +135,7 @@ class StaffAddForm(UserCreationForm):
|
|||||||
|
|
||||||
# Send email with the generated credentials
|
# Send email with the generated credentials
|
||||||
send_mail(
|
send_mail(
|
||||||
"Your account credentials",
|
"Your Django LMS account credentials",
|
||||||
f"Your username: {generated_username}\nYour password: {generated_password}",
|
f"Your username: {generated_username}\nYour password: {generated_password}",
|
||||||
"from@example.com",
|
"from@example.com",
|
||||||
[user.email],
|
[user.email],
|
||||||
@ -275,7 +269,7 @@ class StudentAddForm(UserCreationForm):
|
|||||||
@transaction.atomic()
|
@transaction.atomic()
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
user = super().save(commit=False)
|
user = super().save(commit=False)
|
||||||
user.is_lecturer = True
|
user.is_student = True
|
||||||
user.first_name = self.cleaned_data.get("first_name")
|
user.first_name = self.cleaned_data.get("first_name")
|
||||||
user.last_name = self.cleaned_data.get("last_name")
|
user.last_name = self.cleaned_data.get("last_name")
|
||||||
user.gender = self.cleaned_data.get("gender")
|
user.gender = self.cleaned_data.get("gender")
|
||||||
@ -285,30 +279,29 @@ class StudentAddForm(UserCreationForm):
|
|||||||
user.email = self.cleaned_data.get("email")
|
user.email = self.cleaned_data.get("email")
|
||||||
|
|
||||||
# Generate a username based on first and last name and registration date
|
# Generate a username based on first and last name and registration date
|
||||||
registration_date = datetime.now().strftime("%Y%m%d%H%M")
|
registration_date = datetime.now().strftime("%Y")
|
||||||
|
total_students_count = Student.objects.count()
|
||||||
generated_username = (
|
generated_username = (
|
||||||
f"{user.first_name.lower()}{user.last_name.lower()}{registration_date}"
|
f"{settings.STUDENT_ID_PREFIX}-{registration_date}-{total_students_count}"
|
||||||
)
|
)
|
||||||
|
# Generate a password
|
||||||
# Check if the generated username already exists, and regenerate if needed
|
generated_password = User.objects.make_random_password()
|
||||||
while User.objects.filter(username=generated_username).exists():
|
|
||||||
registration_date = datetime.now().strftime("%Y%m%d%H%M")
|
|
||||||
generated_username = f"{user.first_name.lower()}{user.last_name.lower()}{registration_date}".replace(
|
|
||||||
" ", ""
|
|
||||||
)
|
|
||||||
|
|
||||||
user.username = generated_username
|
user.username = generated_username
|
||||||
|
|
||||||
generated_password = User.objects.make_random_password()
|
|
||||||
user.set_password(generated_password)
|
user.set_password(generated_password)
|
||||||
|
|
||||||
if commit:
|
if commit:
|
||||||
user.save()
|
user.save()
|
||||||
|
Student.objects.create(
|
||||||
|
student=user,
|
||||||
|
level=self.cleaned_data.get("level"),
|
||||||
|
program=self.cleaned_data.get("program"),
|
||||||
|
)
|
||||||
|
|
||||||
# Send email with the generated credentials
|
# Send email with the generated credentials
|
||||||
send_mail(
|
send_mail(
|
||||||
"Your account credentials",
|
"Your Django LMS account credentials",
|
||||||
f"Your username: {generated_username}\nYour password: {generated_password}",
|
f"Your ID: {generated_username}\nYour password: {generated_password}",
|
||||||
settings.EMAIL_FROM_ADDRESS,
|
settings.EMAIL_FROM_ADDRESS,
|
||||||
[user.email],
|
[user.email],
|
||||||
fail_silently=False,
|
fail_silently=False,
|
||||||
@ -348,6 +341,15 @@ class ProfileUpdateForm(UserChangeForm):
|
|||||||
label="Last Name",
|
label="Last Name",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
gender = forms.CharField(
|
||||||
|
widget=forms.Select(
|
||||||
|
choices=GENDERS,
|
||||||
|
attrs={
|
||||||
|
"class": "browser-default custom-select form-control",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
phone = forms.CharField(
|
phone = forms.CharField(
|
||||||
widget=forms.TextInput(
|
widget=forms.TextInput(
|
||||||
attrs={
|
attrs={
|
||||||
@ -370,7 +372,15 @@ class ProfileUpdateForm(UserChangeForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ["email", "phone", "address", "picture", "first_name", "last_name"]
|
fields = [
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"gender",
|
||||||
|
"email",
|
||||||
|
"phone",
|
||||||
|
"address",
|
||||||
|
"picture",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class EmailValidationOnForgotPassword(PasswordResetForm):
|
class EmailValidationOnForgotPassword(PasswordResetForm):
|
||||||
|
|||||||
0
accounts/signals.py
Normal file
0
accounts/signals.py
Normal file
@ -354,6 +354,7 @@ def edit_student(request, pk):
|
|||||||
|
|
||||||
@method_decorator([login_required, admin_required], name="dispatch")
|
@method_decorator([login_required, admin_required], name="dispatch")
|
||||||
class StudentListView(FilterView):
|
class StudentListView(FilterView):
|
||||||
|
queryset = Student.objects.all()
|
||||||
filterset_class = StudentFilter
|
filterset_class = StudentFilter
|
||||||
template_name = "accounts/student_list.html"
|
template_name = "accounts/student_list.html"
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
|
|||||||
@ -193,13 +193,11 @@ MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
|||||||
EMAIL_BACKEND = config(
|
EMAIL_BACKEND = config(
|
||||||
"EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend"
|
"EMAIL_BACKEND", default="django.core.mail.backends.smtp.EmailBackend"
|
||||||
)
|
)
|
||||||
EMAIL_HOST = config(
|
EMAIL_HOST = config("EMAIL_HOST", default="smtp.gmail.com")
|
||||||
"EMAIL_HOST", default="smtp.gmail.com"
|
|
||||||
) # Gmail as the email host, but you can change it
|
|
||||||
EMAIL_PORT = config("EMAIL_PORT", default=587)
|
EMAIL_PORT = config("EMAIL_PORT", default=587)
|
||||||
EMAIL_USE_TLS = True
|
EMAIL_USE_TLS = True
|
||||||
EMAIL_HOST_USER = config("USER_EMAIL")
|
EMAIL_HOST_USER = config("EMAIL_HOST_USER")
|
||||||
EMAIL_HOST_PASSWORD = config("USER_PASSWORD")
|
EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD")
|
||||||
EMAIL_FROM_ADDRESS = config("EMAIL_FROM_ADDRESS")
|
EMAIL_FROM_ADDRESS = config("EMAIL_FROM_ADDRESS")
|
||||||
|
|
||||||
# crispy config
|
# crispy config
|
||||||
@ -250,3 +248,6 @@ LOGGING = {
|
|||||||
|
|
||||||
# WhiteNoise configuration
|
# WhiteNoise configuration
|
||||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||||
|
|
||||||
|
STUDENT_ID_PREFIX = config("STUDENT_ID_PREFIX", "ugr")
|
||||||
|
LECTURER_ID_PREFIX = config("LECTURER_ID_PREFIX", "lec")
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
from django.shortcuts import render, redirect, get_object_or_404
|
from django.shortcuts import render, redirect, get_object_or_404
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from accounts.decorators import admin_required, lecturer_required
|
from accounts.decorators import admin_required, lecturer_required
|
||||||
from accounts.models import User, Student
|
from accounts.models import User, Student
|
||||||
from .forms import SessionForm, SemesterForm, NewsAndEventsForm
|
from .forms import SessionForm, SemesterForm, NewsAndEventsForm
|
||||||
from .models import *
|
from .models import NewsAndEvents, ActivityLog, Session, Semester
|
||||||
|
|
||||||
|
|
||||||
# ########################################################
|
# ########################################################
|
||||||
@ -28,9 +27,9 @@ def dashboard_view(request):
|
|||||||
logs = ActivityLog.objects.all().order_by("-created_at")[:10]
|
logs = ActivityLog.objects.all().order_by("-created_at")[:10]
|
||||||
gender_count = Student.get_gender_count()
|
gender_count = Student.get_gender_count()
|
||||||
context = {
|
context = {
|
||||||
"student_count": User.get_student_count(),
|
"student_count": User.objects.get_student_count(),
|
||||||
"lecturer_count": User.get_lecturer_count(),
|
"lecturer_count": User.objects.get_lecturer_count(),
|
||||||
"superuser_count": User.get_superuser_count(),
|
"superuser_count": User.objects.get_superuser_count(),
|
||||||
"males_count": gender_count["M"],
|
"males_count": gender_count["M"],
|
||||||
"females_count": gender_count["F"],
|
"females_count": gender_count["F"],
|
||||||
"logs": logs,
|
"logs": logs,
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
{{ form.email|as_crispy_field }}
|
{{ form.email|as_crispy_field }}
|
||||||
{{ form.first_name|as_crispy_field }}
|
{{ form.first_name|as_crispy_field }}
|
||||||
{{ form.last_name|as_crispy_field }}
|
{{ form.last_name|as_crispy_field }}
|
||||||
|
{{ form.gender|as_crispy_field }}
|
||||||
{{ form.phone|as_crispy_field }}
|
{{ form.phone|as_crispy_field }}
|
||||||
{{ form.address|as_crispy_field }}
|
{{ form.address|as_crispy_field }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -18,20 +18,7 @@
|
|||||||
|
|
||||||
<p class="title-1"><i class="fas fa-chalkboard-teacher"></i>Lecturers</p>
|
<p class="title-1"><i class="fas fa-chalkboard-teacher"></i>Lecturers</p>
|
||||||
|
|
||||||
{% if messages %}
|
{% include 'snippets/messages.html' %}
|
||||||
{% for message in messages %}
|
|
||||||
{% if message.tags == 'error' %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<i class="fas fa-exclamation-circle"></i>{{ message }}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="alert alert-success">
|
|
||||||
<i class="fas fa-check-circle"></i>{{ message }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% include 'snippets/filter_form.html' %}
|
{% include 'snippets/filter_form.html' %}
|
||||||
|
|
||||||
<div class="table-responsive table-shadow table-light table-striped m-0 mt-4">
|
<div class="table-responsive table-shadow table-light table-striped m-0 mt-4">
|
||||||
|
|||||||
@ -22,20 +22,8 @@
|
|||||||
<div class="title-1"><i class="fas fa-user-graduate"></i>Students</div>
|
<div class="title-1"><i class="fas fa-user-graduate"></i>Students</div>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
{% if messages %}
|
|
||||||
{% for message in messages %}
|
|
||||||
{% if message.tags == 'error' %}
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<i class="fas fa-exclamation-circle"></i>{{ message }}
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="alert alert-success">
|
|
||||||
<i class="fas fa-check-circle"></i>{{ message }}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
{% include 'snippets/messages.html' %}
|
||||||
{% include 'snippets/filter_form.html' %}
|
{% include 'snippets/filter_form.html' %}
|
||||||
|
|
||||||
<div class="table-responsive table-shadow table-light table-striped m-0 mt-4">
|
<div class="table-responsive table-shadow table-light table-striped m-0 mt-4">
|
||||||
|
|||||||
@ -305,6 +305,9 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
const malesCount = {{ males_count }}
|
||||||
|
const femalesCount = {{ females_count }}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
||||||
// Setup
|
// Setup
|
||||||
@ -478,7 +481,7 @@
|
|||||||
],
|
],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: "Students Gender Dataset",
|
label: "Students Gender Dataset",
|
||||||
data: [{{ males_count }}, {{ females_count }}],
|
data: [malesCount, femalesCount],
|
||||||
backgroundColor: [
|
backgroundColor: [
|
||||||
'rgb(255, 99, 132)',
|
'rgb(255, 99, 132)',
|
||||||
'rgb(54, 162, 235)'
|
'rgb(54, 162, 235)'
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
{{ form.email|as_crispy_field }}
|
{{ form.email|as_crispy_field }}
|
||||||
{{ form.first_name|as_crispy_field }}
|
{{ form.first_name|as_crispy_field }}
|
||||||
{{ form.last_name|as_crispy_field }}
|
{{ form.last_name|as_crispy_field }}
|
||||||
|
{{ form.gender|as_crispy_field }}
|
||||||
{{ form.phone|as_crispy_field }}
|
{{ form.phone|as_crispy_field }}
|
||||||
{{ form.address|as_crispy_field }}
|
{{ form.address|as_crispy_field }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user