Files
goodtimes/accounts/models.py

508 lines
16 KiB
Python

from collections.abc import Iterable
import datetime
import random
import string
# from manage_wallets.models import Wallet, Transaction, TransactionStatus, TransactionType
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
from phonenumber_field.modelfields import PhoneNumberField
# from manage_subscriptions.models import Subscription
from goodtimes.utils import RandomGenerator
from .resource_action import (
PRINCIPAL_TYPE_EVENT_USER,
PRINCIPAL_TYPE_EVENT_MANAGER,
PRINCIPAL_TYPE_FREE_USER,
)
from .utils import UserContext
from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator
class BaseModel(models.Model):
active = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="%(class)s_created",
on_delete=models.CASCADE,
blank=True,
null=True,
)
created_on = models.DateTimeField(auto_now_add=True)
modified_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="%(class)s_modified",
on_delete=models.CASCADE,
blank=True,
null=True,
)
modified_on = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class MasterModel(models.Model):
name = models.CharField(max_length=255)
label = models.CharField(max_length=255, null=True, blank=True)
slug = models.SlugField(max_length=255, null=True, blank=True)
sort_order = models.IntegerField(blank=True, null=True)
small_image_url = models.ImageField(blank=True, null=True)
large_image_url = models.ImageField(blank=True, null=True)
active = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
created_by = models.SmallIntegerField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
modified_by = models.SmallIntegerField(blank=True, null=True)
modified_on = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
def __str__(self):
return f"{self.name}"
def save(self, *args, **kwargs):
# Generate a slug from the name field
self.slug = slugify(self.name)
return super().save(*args, **kwargs)
class IAmPrincipalType(MasterModel):
class Meta:
db_table = "iam_principal_type"
@classmethod
def get_principal_type(cls, type):
return cls.objects.filter(name=type).first()
class IAmPrincipalSource(MasterModel):
class Meta:
db_table = "iam_principal_source"
class IAmAppAction(MasterModel):
class Meta:
db_table = "iam_app_action"
class IAmAppResource(MasterModel):
action = models.ManyToManyField(
IAmAppAction,
through="IAmAppResourceActionLink",
related_name="app_resource_action",
)
class Meta:
db_table = "iam_app_resource"
class IAmRoleAppResourceActionLinkManager(models.Manager):
def generate_app_resource_action_data(self):
"""
Generate a dictionary mapping resource names to associated actions.
Returns:
dict: A dictionary with resource names as keys and nested dictionaries
where action IDs are keys and action names are values.
Example:
{
"res1": {1: "a1", 2: "a2"},
"res2": {3: "a1", 4: "a2"}
}
"""
app_resource_action = self.select_related("app_resource", "app_action").all()
resource_action_link = {}
for item in app_resource_action:
resource = item.app_resource.name
action = item.app_action.name
id = item.id
if resource in resource_action_link:
resource_action_link[resource][id] = action
else:
resource_action_link[resource] = {id: action}
# print(resource_action_link)
return resource_action_link
class IAmAppResourceActionLink(models.Model):
app_resource = models.ForeignKey(
IAmAppResource,
related_name="resource_action_link_app_resource",
on_delete=models.CASCADE,
)
app_action = models.ForeignKey(
IAmAppAction,
related_name="resource_action_link_app_action",
on_delete=models.CASCADE,
)
objects = IAmRoleAppResourceActionLinkManager()
class Meta:
db_table = "iam_app_resource_action_link"
def __str__(self):
return f"{self.app_resource.name}: {self.app_action.name}"
class IAmRole(MasterModel):
app_resource_action = models.ManyToManyField(
IAmAppResourceActionLink,
through="IAmRoleAppResourceActionLink",
related_name="role_app_resource_action",
)
class Meta:
db_table = "iam_role"
class IAmRoleAppResourceActionLink(models.Model):
role = models.ForeignKey(
IAmRole,
related_name="role_app_resource_action_link_role",
on_delete=models.CASCADE,
)
app_resource_action = models.ForeignKey(
IAmAppResourceActionLink,
related_name="role_app_resource_action_link_app_resource_action",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_role_app_resource_action_link"
class IAmPrincipalGroup(MasterModel):
role = models.ManyToManyField(
IAmRole, through="IAmPricipalGroupRoleLink", related_name="principal_group_role"
)
class Meta:
db_table = "iam_principal_group"
class IAmPricipalGroupRoleLink(models.Model):
principal_group = models.ForeignKey(
IAmPrincipalGroup,
related_name="role_link_principal_group",
on_delete=models.CASCADE,
)
role = models.ForeignKey(
IAmRole, related_name="role_link_role", on_delete=models.CASCADE
)
class Meta:
db_table = "iam_principal_group_role_link"
class IAmPrincipalManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError("The Email field must be set")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault("username", email)
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
# extra_fields.setdefault("phone_no", "+919978895465")
extra_fields.setdefault("gender", "M")
extra_fields.setdefault("referral_code", f"admin_{random.randint(10, 100000)}")
extra_fields.setdefault("date_of_birth", timezone.now())
extra_fields.setdefault("created_by", None)
extra_fields.setdefault("created_on", timezone.now())
extra_fields.setdefault("modified_by", None)
extra_fields.setdefault("modified_on", timezone.now())
return self.create_user(email, password, **extra_fields)
class IAmPrincipal(AbstractUser):
principal_type = models.ForeignKey(
IAmPrincipalType,
related_name="principals_type",
null=True,
on_delete=models.PROTECT,
)
principal_source = models.ForeignKey(
IAmPrincipalSource,
related_name="principals_source",
on_delete=models.CASCADE,
null=True,
)
principal_resource = models.ManyToManyField(
IAmAppResource,
through="IAmPrincipalResourceLink",
related_name="principal_resources",
)
email = models.EmailField(unique=True)
gender = models.CharField(max_length=5, blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
phone_no = PhoneNumberField(blank=True, null=True)
address_line1 = models.TextField(blank=True, null=True)
address_line2 = models.TextField(blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
state = models.CharField(max_length=100, blank=True, null=True)
country = models.CharField(max_length=100, blank=True, null=True)
post_code = models.CharField(max_length=100, blank=True, null=True)
profile_photo = models.ImageField(upload_to="profile", blank=True, null=True)
phone_verified = models.BooleanField(default=False)
email_verified = models.BooleanField(default=False)
referral_code = models.CharField(max_length=50, null=True, blank=True)
apple_id = models.CharField(max_length=255, null=True, blank=True)
player_id = models.CharField(max_length=255, null=True, blank=True)
referred_by = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="referrals",
on_delete=models.SET_NULL,
)
created_by = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="creations",
on_delete=models.SET_NULL,
)
created_on = models.DateTimeField(auto_now_add=True)
modified_by = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="modifications",
on_delete=models.SET_NULL,
)
modified_on = models.DateTimeField(auto_now=True)
deleted = models.BooleanField(default=False)
principal_group = models.ManyToManyField(
IAmPrincipalGroup,
through="IAmPrincipalGroupLink",
related_name="principal_groups",
)
register_complete = models.BooleanField(default=False)
linkedin_profile = models.URLField(
verbose_name="Principal LinkedIn",
max_length=200,
blank=True,
null=True,
)
instagram_profile = models.URLField(
verbose_name="Principal Instagram",
max_length=200,
blank=True,
null=True,
)
youtube_profile = models.URLField(
verbose_name="Principal Youtube",
max_length=200,
blank=True,
null=True,
)
facebook_profile = models.URLField(
verbose_name="Principal Facebook",
max_length=200,
blank=True,
null=True,
)
website = models.URLField(
verbose_name="Website",
max_length=200,
blank=True,
null=True,
)
business_name = models.CharField(verbose_name="Business Name", max_length=200, blank=True, null=True)
twitter_profile = models.URLField(verbose_name="Principal Twitter", max_length=255, null=True, blank=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
objects = IAmPrincipalManager()
class Meta:
db_table = "iam_principal"
def __str__(self):
return f"{self.email}"
@staticmethod
def generate_random_password():
"""Generate a password in the format 'GoodTimes@xxxx'."""
random_number = random.randint(1000, 9999) # Generate a 4-digit random number
return f"GoodTimes@{random_number}"
class IAmPrincipalExtendedData(models.Model):
principal = models.OneToOneField(
IAmPrincipal,
related_name="extended_data",
on_delete=models.CASCADE,
help_text="The principal user to which this extended data is related."
)
is_onboarded = models.BooleanField(
default=False,
help_text="Indicates whether the user was onboarded by an admin."
)
is_transferred = models.BooleanField(
default=False,
help_text="Indicates whether the account has been transferred to the user."
)
transferred_on = models.DateTimeField(
null=True,
blank=True,
help_text="The date and time when the account was transferred to the user."
)
pwd_changed_post_transfer = models.BooleanField(default=False, help_text="Indicates if the user changed their password after the account was transferred.")
encrypted_pass = models.TextField(blank=True, null=True)
class Meta:
db_table = "iam_principal_extended_data"
def __str__(self):
return f"Extended Data for {self.principal}"
def save(self, *args, **kwargs):
if self.is_transferred and self.transferred_on is None:
self.transferred_on = datetime.datetime.now()
super().save(*args, **kwargs)
@property
def decrypted_field(self):
from goodtimes.services import Encryptor
encryptor = Encryptor()
return encryptor.decrypt(self.encrypted_pass)
class IAmPrincipalResourceLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,
related_name="principal_resource_link_principal",
on_delete=models.CASCADE,
)
principal_resource = models.ForeignKey(
IAmAppResource,
related_name="principal_resource_link_resource",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_principal_resource_group_link"
class IAmPrincipalGroupLink(models.Model):
principal = models.ForeignKey(
IAmPrincipal,
related_name="principal_group_link_principal",
on_delete=models.CASCADE,
)
principal_group = models.ForeignKey(
IAmPrincipalGroup,
related_name="principal_group_link_group",
on_delete=models.CASCADE,
)
class Meta:
db_table = "iam_principal_principal_group_link"
class IAmPrincipalMerchant(BaseModel):
principal = models.OneToOneField(
IAmPrincipal, related_name="principal_merchant", on_delete=models.CASCADE
)
def __str__(self):
return self.principal.email
class Meta:
db_table = "iam_principal_merchant"
class IAmPrincipalOtp(models.Model):
principal = models.ForeignKey(
IAmPrincipal, related_name="principal_otp", on_delete=models.CASCADE
)
otp_code = models.CharField(max_length=4)
otp_purpose = models.CharField(max_length=50, null=True, blank=True)
valid_till = models.DateTimeField()
is_used = models.BooleanField(default=False)
class Meta:
db_table = "iam_principal_otp"
def __str__(self):
return f"{self.principal.phone_no}:{self.otp_code} : {self.otp_purpose}"
def save(self, *args, **kwargs):
if not self.pk:
self.otp_code = RandomGenerator.random_otp()
self.valid_till = timezone.now() + timezone.timedelta(
minutes=settings.OTP_EXPIRE_TIME
)
super(IAmPrincipalOtp, self).save(*args, **kwargs)
def is_expired(self):
return timezone.now() >= self.valid_till
class IAmPrincipalBiometric(BaseModel):
principal = models.ForeignKey(
IAmPrincipal, related_name="principal_biometric", on_delete=models.CASCADE
)
biometric_type = models.CharField(max_length=100)
biometric_data = models.CharField(max_length=255)
class Meta:
db_table = "iam_principal_biometric"
def __str__(self):
return f"{self.principal.first_name}:{self.biometric_type}"
class IAmPrincipalLocation(BaseModel):
principal = models.OneToOneField(
IAmPrincipal, related_name="principal_location", on_delete=models.CASCADE
)
latitude = models.DecimalField(max_digits=18, decimal_places=15)
longitude = models.DecimalField(max_digits=18, decimal_places=15)
class Meta:
db_table = "iam_principal_location"
def __str__(self):
return f"{self.principal.first_name}:{self.latitude}, {self.longitude}"
class AppType(models.TextChoices):
ANDROID = "android", "android"
IOS = "ios", "ios"
class AppVersion(models.Model):
version = models.CharField(
max_length=10, validators=[RegexValidator(r"^\d+\.\d+\.\d+$")]
)
force_upgrade = models.BooleanField(
default=False,
help_text="Indicates whether a force upgrade is needed for this app version.",
)
recommend_upgrade = models.BooleanField(
default=False,
help_text="Indicates whether a recommend upgrade is needed for this app version.",
)
app_type = models.CharField(max_length=10, choices=AppType.choices)
class Meta:
db_table = "app_version"
def __str__(self):
return self.version