add django extensions, add faker, add Django model factories for accounts

This commit is contained in:
WajahatKanju 2024-02-06 22:31:13 +05:00
parent bce9774594
commit 2d1be28a4c
5 changed files with 391 additions and 222 deletions

View File

@ -1,215 +1,215 @@
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import AbstractUser, UserManager
from django.conf import settings
from django.db.models import Q
from PIL import Image
from course.models import Program
from .validators import ASCIIUsernameValidator
# LEVEL_COURSE = "Level course"
BACHLOAR_DEGREE = "Bachloar"
MASTER_DEGREE = "Master"
LEVEL = (
# (LEVEL_COURSE, "Level course"),
(BACHLOAR_DEGREE, "Bachloar Degree"),
(MASTER_DEGREE, "Master Degree"),
)
FATHER = "Father"
MOTHER = "Mother"
BROTHER = "Brother"
SISTER = "Sister"
GRAND_MOTHER = "Grand mother"
GRAND_FATHER = "Grand father"
OTHER = "Other"
RELATION_SHIP = (
(FATHER, "Father"),
(MOTHER, "Mother"),
(BROTHER, "Brother"),
(SISTER, "Sister"),
(GRAND_MOTHER, "Grand mother"),
(GRAND_FATHER, "Grand father"),
(OTHER, "Other"),
)
class CustomUserManager(UserManager):
def search(self, query=None):
queryset = self.get_queryset()
if query is not None:
or_lookup = (
Q(username__icontains=query)
| Q(first_name__icontains=query)
| Q(last_name__icontains=query)
| Q(email__icontains=query)
)
queryset = queryset.filter(
or_lookup
).distinct() # distinct() is often necessary with Q lookups
return queryset
GENDERS = (("M", "Male"), ("F", "Female"))
class User(AbstractUser):
is_student = models.BooleanField(default=False)
is_lecturer = models.BooleanField(default=False)
is_parent = models.BooleanField(default=False)
is_dep_head = models.BooleanField(default=False)
gender = models.CharField(max_length=1, choices=GENDERS, blank=True, null=True)
phone = models.CharField(max_length=60, blank=True, null=True)
address = models.CharField(max_length=60, blank=True, null=True)
picture = models.ImageField(
upload_to="profile_pictures/%y/%m/%d/", default="default.png", null=True
)
email = models.EmailField(blank=True, null=True)
username_validator = ASCIIUsernameValidator()
objects = CustomUserManager()
class Meta:
ordering = ("-date_joined",)
@property
def get_full_name(self):
full_name = self.username
if self.first_name and self.last_name:
full_name = self.first_name + " " + self.last_name
return full_name
@classmethod
def get_student_count(cls):
return cls.objects.filter(is_student=True).count()
@classmethod
def get_lecturer_count(cls):
return cls.objects.filter(is_lecturer=True).count()
@classmethod
def get_superuser_count(cls):
return cls.objects.filter(is_superuser=True).count()
def __str__(self):
return "{} ({})".format(self.username, self.get_full_name)
@property
def get_user_role(self):
if self.is_superuser:
role = "Admin"
elif self.is_student:
role = "Student"
elif self.is_lecturer:
role = "Lecturer"
elif self.is_parent:
role = "Parent"
return role
def get_picture(self):
try:
return self.picture.url
except:
no_picture = settings.MEDIA_URL + "default.png"
return no_picture
def get_absolute_url(self):
return reverse("profile_single", kwargs={"id": self.id})
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
try:
img = Image.open(self.picture.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.picture.path)
except:
pass
def delete(self, *args, **kwargs):
if self.picture.url != settings.MEDIA_URL + "default.png":
self.picture.delete()
super().delete(*args, **kwargs)
class StudentManager(models.Manager):
def search(self, query=None):
qs = self.get_queryset()
if query is not None:
or_lookup = Q(level__icontains=query) | Q(program__icontains=query)
qs = qs.filter(
or_lookup
).distinct() # distinct() is often necessary with Q lookups
return qs
class Student(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
# id_number = models.CharField(max_length=20, unique=True, blank=True)
level = models.CharField(max_length=25, choices=LEVEL, null=True)
program = models.ForeignKey(Program, on_delete=models.CASCADE, null=True)
objects = StudentManager()
class Meta:
ordering = ("-student__date_joined",)
def __str__(self):
return self.student.get_full_name
@classmethod
def get_gender_count(cls):
males_count = Student.objects.filter(student__gender="M").count()
females_count = Student.objects.filter(student__gender="F").count()
return {"M": males_count, "F": females_count}
def get_absolute_url(self):
return reverse("profile_single", kwargs={"id": self.id})
def delete(self, *args, **kwargs):
self.student.delete()
super().delete(*args, **kwargs)
class Parent(models.Model):
"""
Connect student with their parent, parents can
only view their connected students information
"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
student = models.OneToOneField(Student, null=True, on_delete=models.SET_NULL)
first_name = models.CharField(max_length=120)
last_name = models.CharField(max_length=120)
phone = models.CharField(max_length=60, blank=True, null=True)
email = models.EmailField(blank=True, null=True)
# What is the relationship between the student and
# the parent (i.e. father, mother, brother, sister)
relation_ship = models.TextField(choices=RELATION_SHIP, blank=True)
class Meta:
ordering = ("-user__date_joined",)
def __str__(self):
return self.user.username
class DepartmentHead(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.ForeignKey(Program, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ("-user__date_joined",)
def __str__(self):
return "{}".format(self.user)
from django.db import models
from django.urls import reverse
from django.contrib.auth.models import AbstractUser, UserManager
from django.conf import settings
from django.db.models import Q
from PIL import Image
from course.models import Program
from .validators import ASCIIUsernameValidator
# LEVEL_COURSE = "Level course"
BACHLOAR_DEGREE = "Bachloar"
MASTER_DEGREE = "Master"
LEVEL = (
# (LEVEL_COURSE, "Level course"),
(BACHLOAR_DEGREE, "Bachloar Degree"),
(MASTER_DEGREE, "Master Degree"),
)
FATHER = "Father"
MOTHER = "Mother"
BROTHER = "Brother"
SISTER = "Sister"
GRAND_MOTHER = "Grand mother"
GRAND_FATHER = "Grand father"
OTHER = "Other"
RELATION_SHIP = (
(FATHER, "Father"),
(MOTHER, "Mother"),
(BROTHER, "Brother"),
(SISTER, "Sister"),
(GRAND_MOTHER, "Grand mother"),
(GRAND_FATHER, "Grand father"),
(OTHER, "Other"),
)
class CustomUserManager(UserManager):
def search(self, query=None):
queryset = self.get_queryset()
if query is not None:
or_lookup = (
Q(username__icontains=query)
| Q(first_name__icontains=query)
| Q(last_name__icontains=query)
| Q(email__icontains=query)
)
queryset = queryset.filter(
or_lookup
).distinct() # distinct() is often necessary with Q lookups
return queryset
GENDERS = (("M", "Male"), ("F", "Female"))
class User(AbstractUser):
is_student = models.BooleanField(default=False)
is_lecturer = models.BooleanField(default=False)
is_parent = models.BooleanField(default=False)
is_dep_head = models.BooleanField(default=False)
gender = models.CharField(max_length=1, choices=GENDERS, blank=True, null=True)
phone = models.CharField(max_length=60, blank=True, null=True)
address = models.CharField(max_length=60, blank=True, null=True)
picture = models.ImageField(
upload_to="profile_pictures/%y/%m/%d/", default="default.png", null=True
)
email = models.EmailField(blank=True, null=True)
username_validator = ASCIIUsernameValidator()
objects = CustomUserManager()
class Meta:
ordering = ("-date_joined",)
@property
def get_full_name(self):
full_name = self.username
if self.first_name and self.last_name:
full_name = self.first_name + " " + self.last_name
return full_name
@classmethod
def get_student_count(cls):
return cls.objects.filter(is_student=True).count()
@classmethod
def get_lecturer_count(cls):
return cls.objects.filter(is_lecturer=True).count()
@classmethod
def get_superuser_count(cls):
return cls.objects.filter(is_superuser=True).count()
def __str__(self):
return "{} ({})".format(self.username, self.get_full_name)
@property
def get_user_role(self):
if self.is_superuser:
role = "Admin"
elif self.is_student:
role = "Student"
elif self.is_lecturer:
role = "Lecturer"
elif self.is_parent:
role = "Parent"
return role
def get_picture(self):
try:
return self.picture.url
except:
no_picture = settings.MEDIA_URL + "default.png"
return no_picture
def get_absolute_url(self):
return reverse("profile_single", kwargs={"id": self.id})
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
try:
img = Image.open(self.picture.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.picture.path)
except:
pass
def delete(self, *args, **kwargs):
if self.picture.url != settings.MEDIA_URL + "default.png":
self.picture.delete()
super().delete(*args, **kwargs)
class StudentManager(models.Manager):
def search(self, query=None):
qs = self.get_queryset()
if query is not None:
or_lookup = Q(level__icontains=query) | Q(program__icontains=query)
qs = qs.filter(
or_lookup
).distinct() # distinct() is often necessary with Q lookups
return qs
class Student(models.Model):
student = models.OneToOneField(User, on_delete=models.CASCADE)
# id_number = models.CharField(max_length=20, unique=True, blank=True)
level = models.CharField(max_length=25, choices=LEVEL, null=True)
program = models.ForeignKey(Program, on_delete=models.CASCADE, null=True)
objects = StudentManager()
class Meta:
ordering = ("-student__date_joined",)
def __str__(self):
return self.student.get_full_name
@classmethod
def get_gender_count(cls):
males_count = Student.objects.filter(student__gender="M").count()
females_count = Student.objects.filter(student__gender="F").count()
return {"M": males_count, "F": females_count}
def get_absolute_url(self):
return reverse("profile_single", kwargs={"id": self.id})
def delete(self, *args, **kwargs):
self.student.delete()
super().delete(*args, **kwargs)
class Parent(models.Model):
"""
Connect student with their parent, parents can
only view their connected students information
"""
user = models.OneToOneField(User, on_delete=models.CASCADE)
student = models.OneToOneField(Student, null=True, on_delete=models.SET_NULL)
first_name = models.CharField(max_length=120)
last_name = models.CharField(max_length=120)
phone = models.CharField(max_length=60, blank=True, null=True)
email = models.EmailField(blank=True, null=True)
# What is the relationship between the student and
# the parent (i.e. father, mother, brother, sister)
relation_ship = models.TextField(choices=RELATION_SHIP, blank=True)
class Meta:
ordering = ("-user__date_joined",)
def __str__(self):
return self.user.username
class DepartmentHead(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.ForeignKey(Program, on_delete=models.CASCADE, null=True)
class Meta:
ordering = ("-user__date_joined",)
def __str__(self):
return "{}".format(self.user)

View File

@ -37,6 +37,7 @@ AUTH_USER_MODEL = "accounts.User"
DJANGO_APPS = [
"jet.dashboard",
"jet",
"django_extensions",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",

View File

@ -1,7 +1,10 @@
-r base.txt
psycopg2-binary==2.9.5 # https://github.com/psycopg/psycopg2
# Code quality
# ------------------------------------------------------------------------------
black==22.12.0 # https://github.com/psf/black
-r base.txt
psycopg2-binary==2.9.5 # https://github.com/psycopg/psycopg2
# Code quality
# ------------------------------------------------------------------------------
black==22.12.0 # https://github.com/psf/black
# Django Extensions
django-extensions==3.1.3 # https://github.com/django-extensions/django-extensions

0
scripts/__init__.py Normal file
View File

View File

@ -0,0 +1,165 @@
import os
import django
from typing import List, Tuple
from django.utils import timezone
from faker import Faker
from factory.django import DjangoModelFactory
from factory import SubFactory, LazyAttribute, Iterator
from django_extensions.management.commands import runscript
# Set up Django environment
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
django.setup()
from accounts.models import User, Student, Parent, DepartmentHead, LEVEL, RELATION_SHIP
from course.models import Program
fake = Faker()
class UserFactory(DjangoModelFactory):
"""
Factory for creating User instances with optional flags.
Attributes:
username (str): The generated username.
first_name (str): The generated first name.
last_name (str): The generated last name.
email (str): The generated email.
date_joined (datetime): The current date and time.
phone (str): The generated phone number.
address (str): The generated address.
is_student (bool): Flag indicating if the user is a student.
is_lecturer (bool): Flag indicating if the user is a lecturer.
is_parent (bool): Flag indicating if the user is a parent.
is_dep_head (bool): Flag indicating if the user is a department head.
"""
class Meta:
model = User
username: str = LazyAttribute(lambda x: fake.user_name())
first_name: str = LazyAttribute(lambda x: fake.first_name())
last_name: str = LazyAttribute(lambda x: fake.last_name())
email: str = LazyAttribute(lambda x: fake.email())
date_joined: timezone.datetime = timezone.now()
phone: str = LazyAttribute(lambda x: fake.phone_number())
address: str = LazyAttribute(lambda x: fake.address())
is_student: bool = False
is_lecturer: bool = False
is_parent: bool = False
is_dep_head: bool = False
@classmethod
def _create(cls, model_class: type, *args, **kwargs) -> User:
"""
Create a User instance with optional flags.
Args:
model_class (type): The class of the model to create.
Returns:
User: The created User instance.
"""
user: User = super()._create(model_class, *args, **kwargs)
# Set the appropriate flags based on the user type
if cls.is_student:
user.is_student = True
elif cls.is_parent:
user.is_parent = True
user.save()
return user
class ProgramFactory(DjangoModelFactory):
"""
Factory for creating Program instances.
Attributes:
title (str): The generated program title.
summary (str): The generated summary.
"""
class Meta:
model = Program
title: str = LazyAttribute(lambda x: fake.sentence(nb_words=3))
summary: str = LazyAttribute(lambda x: fake.text())
@classmethod
def _create(cls, model_class: type, *args, **kwargs) -> Program:
"""
Create a Program instance using get_or_create to avoid duplicates.
Args:
model_class (type): The class of the model to create.
Returns:
Program: The created Program instance.
"""
program, created = Program.objects.get_or_create(title=kwargs.get("title"), defaults=kwargs)
return program
class StudentFactory(DjangoModelFactory):
"""
Factory for creating Student instances with associated User and Program.
Attributes:
student (User): The associated User instance.
level (str): The level of the student.
program (Program): The associated Program instance.
"""
class Meta:
model = Student
student: User = SubFactory(UserFactory, is_student=True)
level: str = Iterator([choice[0] for choice in LEVEL])
program: Program = SubFactory(ProgramFactory)
class ParentFactory(DjangoModelFactory):
"""
Factory for creating Parent instances with associated User, Student, and Program.
Attributes:
user (User): The associated User instance.
student (Student): The associated Student instance.
first_name (str): The generated first name.
last_name (str): The generated last name.
phone (str): The generated phone number.
email (str): The generated email.
relation_ship (str): The relationship with the student.
"""
class Meta:
model = Parent
user: User = SubFactory(UserFactory, is_parent=True)
student: Student = SubFactory(StudentFactory)
first_name: str = LazyAttribute(lambda x: fake.first_name())
last_name: str = LazyAttribute(lambda x: fake.last_name())
phone: str = LazyAttribute(lambda x: fake.phone_number())
email: str = LazyAttribute(lambda x: fake.email())
relation_ship: str = Iterator([choice[0] for choice in RELATION_SHIP])
def generate_fake_accounts_data(num_programs: int, num_students: int, num_parents: int) -> None:
"""
Generate fake data for Programs, Students, Parents, and DepartmentHeads.
Args:
num_programs (int): Number of programs to generate.
num_students (int): Number of students to generate.
num_parents (int): Number of parents to generate.
"""
programs: List[Program] = ProgramFactory.create_batch(num_programs)
students: List[Student] = StudentFactory.create_batch(num_students)
parents: List[Parent] = ParentFactory.create_batch(num_parents)
print(f"Created {len(programs)} programs.")
print(f"Created {len(students)} students.")
print(f"Created {len(parents)} parents.")
generate_fake_accounts_data(10, 10, 10)