Replace persisted event statuses with publication and cancellation flags, and compute public lifecycle labels from event timing and registration capacity. Keep filters, registration checks, and profile responses aligned with the derived status.
378 lines
9.6 KiB
Python
378 lines
9.6 KiB
Python
"""Authentication-related API schemas."""
|
|
|
|
from datetime import datetime
|
|
from typing import List, Optional, Literal
|
|
|
|
from ninja import ModelSchema, Schema
|
|
from pydantic import Field
|
|
|
|
from apps.gamification.api.schemas import PublicGamificationBadgeSchema
|
|
|
|
from apps.users.models import User
|
|
from apps.blog.permissions import can_access_blog_admin, can_review_blog_posts, can_write_blog_posts
|
|
from core.media import PREVIEW_VARIANT, THUMBNAIL_VARIANT, derivative_url
|
|
|
|
|
|
class UserRegistrationSchema(Schema):
|
|
username: str
|
|
mobile: str
|
|
code: str
|
|
password: str
|
|
email: Optional[str] = None
|
|
first_name: Optional[str] = None
|
|
last_name: Optional[str] = None
|
|
university: Optional[str] = None
|
|
student_id: Optional[str] = None
|
|
year_of_study: Optional[int] = None
|
|
major: Optional[str] = None
|
|
|
|
|
|
class UserLoginSchema(Schema):
|
|
identifier: str
|
|
password: str
|
|
|
|
|
|
class UserOtpLoginSchema(Schema):
|
|
mobile: str
|
|
code: str
|
|
|
|
|
|
class RegisterOtpVerifySchema(Schema):
|
|
mobile: str
|
|
code: str
|
|
|
|
|
|
class OtpSendSchema(Schema):
|
|
mobile: str
|
|
mode: str
|
|
|
|
|
|
class MobileOtpSendSchema(Schema):
|
|
mobile: str
|
|
|
|
|
|
class MobileOtpVerifySchema(Schema):
|
|
mobile: str
|
|
code: str
|
|
|
|
|
|
class GoogleFlowSchema(Schema):
|
|
flow: str
|
|
|
|
|
|
class GoogleClaimVerifySchema(Schema):
|
|
flow: str
|
|
code: str
|
|
|
|
|
|
class GoogleCompleteSchema(Schema):
|
|
flow: str
|
|
mobile: str
|
|
username: Optional[str] = None
|
|
student_id: Optional[str] = None
|
|
year_of_study: Optional[int] = None
|
|
major: Optional[str] = None
|
|
university: Optional[str] = None
|
|
first_name: Optional[str] = None
|
|
last_name: Optional[str] = None
|
|
|
|
|
|
class GoogleFlowResponseSchema(Schema):
|
|
status: str
|
|
email: Optional[str] = None
|
|
first_name: Optional[str] = None
|
|
last_name: Optional[str] = None
|
|
avatar_url: Optional[str] = None
|
|
resolution: Optional[str] = None
|
|
mobile: Optional[str] = None
|
|
mobile_hint: Optional[str] = None
|
|
detail: Optional[str] = None
|
|
access_token: Optional[str] = None
|
|
refresh_token: Optional[str] = None
|
|
|
|
|
|
class UserProfileSchema(ModelSchema):
|
|
profile_picture: Optional[str] = None
|
|
profile_picture_thumbnail_url: Optional[str] = None
|
|
profile_picture_preview_url: Optional[str] = None
|
|
student_id: Optional[str] = None
|
|
major: Optional[str] = None
|
|
major_code: Optional[str] = None
|
|
university: Optional[str] = None
|
|
university_code: Optional[str] = None
|
|
mobile: Optional[str] = None
|
|
requires_mobile_verification: bool
|
|
has_google_link: bool
|
|
can_access_blog_admin: bool
|
|
can_write_blog_posts: bool
|
|
can_review_blog_posts: bool
|
|
show_public_profile_details: bool = False
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = [
|
|
"id",
|
|
"username",
|
|
"email",
|
|
"mobile",
|
|
"first_name",
|
|
"last_name",
|
|
"student_id",
|
|
"year_of_study",
|
|
"major",
|
|
"university",
|
|
"bio",
|
|
"date_joined",
|
|
"is_email_verified",
|
|
"is_mobile_verified",
|
|
"show_public_profile_details",
|
|
"is_active",
|
|
"is_staff",
|
|
"is_superuser",
|
|
"is_deleted",
|
|
"deleted_at",
|
|
]
|
|
|
|
@staticmethod
|
|
def resolve_major(obj):
|
|
return obj.get_major_display()
|
|
|
|
@staticmethod
|
|
def resolve_major_code(obj):
|
|
return obj.major.code if obj.major_id else None
|
|
|
|
@staticmethod
|
|
def resolve_university(obj):
|
|
return obj.get_university_display()
|
|
|
|
@staticmethod
|
|
def resolve_university_code(obj):
|
|
return obj.university.code if obj.university_id else None
|
|
|
|
@staticmethod
|
|
def resolve_requires_mobile_verification(obj):
|
|
return obj.requires_mobile_verification
|
|
|
|
@staticmethod
|
|
def resolve_has_google_link(obj):
|
|
return obj.has_google_link
|
|
|
|
@staticmethod
|
|
def resolve_can_access_blog_admin(obj):
|
|
return can_access_blog_admin(obj)
|
|
|
|
@staticmethod
|
|
def resolve_can_write_blog_posts(obj):
|
|
return can_write_blog_posts(obj)
|
|
|
|
@staticmethod
|
|
def resolve_can_review_blog_posts(obj):
|
|
return can_review_blog_posts(obj)
|
|
|
|
@staticmethod
|
|
def resolve_profile_picture(obj, context):
|
|
request = context["request"]
|
|
if obj.profile_picture and hasattr(obj.profile_picture, "url"):
|
|
return request.build_absolute_uri(obj.profile_picture.url)
|
|
return None
|
|
|
|
@staticmethod
|
|
def resolve_profile_picture_thumbnail_url(obj, context):
|
|
request = context["request"]
|
|
url = derivative_url(obj.profile_picture, THUMBNAIL_VARIANT)
|
|
return request.build_absolute_uri(url) if url else None
|
|
|
|
@staticmethod
|
|
def resolve_profile_picture_preview_url(obj, context):
|
|
request = context["request"]
|
|
url = derivative_url(obj.profile_picture, PREVIEW_VARIANT)
|
|
return request.build_absolute_uri(url) if url else None
|
|
|
|
|
|
class UserListSchema(ModelSchema):
|
|
major: Optional[str] = None
|
|
university: Optional[str] = None
|
|
mobile: Optional[str] = None
|
|
profile_picture: Optional[str] = None
|
|
profile_picture_thumbnail_url: Optional[str] = None
|
|
profile_picture_preview_url: Optional[str] = None
|
|
student_id: Optional[str] = None
|
|
year_of_study: Optional[int] = None
|
|
bio: Optional[str] = None
|
|
is_email_verified: bool
|
|
is_mobile_verified: bool
|
|
is_deleted: bool
|
|
deleted_at: Optional[datetime] = None
|
|
can_access_blog_admin: bool
|
|
can_write_blog_posts: bool
|
|
can_review_blog_posts: bool
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = [
|
|
"id",
|
|
"username",
|
|
"email",
|
|
"mobile",
|
|
"first_name",
|
|
"last_name",
|
|
"student_id",
|
|
"year_of_study",
|
|
"bio",
|
|
"is_active",
|
|
"is_staff",
|
|
"is_superuser",
|
|
"date_joined",
|
|
"major",
|
|
"university",
|
|
"is_email_verified",
|
|
"is_mobile_verified",
|
|
"is_deleted",
|
|
"deleted_at",
|
|
]
|
|
|
|
@staticmethod
|
|
def resolve_major(obj):
|
|
return obj.get_major_display()
|
|
|
|
@staticmethod
|
|
def resolve_university(obj):
|
|
return obj.get_university_display()
|
|
|
|
@staticmethod
|
|
def resolve_can_access_blog_admin(obj):
|
|
return can_access_blog_admin(obj)
|
|
|
|
@staticmethod
|
|
def resolve_can_write_blog_posts(obj):
|
|
return can_write_blog_posts(obj)
|
|
|
|
@staticmethod
|
|
def resolve_can_review_blog_posts(obj):
|
|
return can_review_blog_posts(obj)
|
|
|
|
@staticmethod
|
|
def resolve_profile_picture(obj, context):
|
|
request = context["request"]
|
|
if obj.profile_picture and hasattr(obj.profile_picture, "url"):
|
|
return request.build_absolute_uri(obj.profile_picture.url)
|
|
return None
|
|
|
|
@staticmethod
|
|
def resolve_profile_picture_thumbnail_url(obj, context):
|
|
request = context["request"]
|
|
url = derivative_url(obj.profile_picture, THUMBNAIL_VARIANT)
|
|
return request.build_absolute_uri(url) if url else None
|
|
|
|
@staticmethod
|
|
def resolve_profile_picture_preview_url(obj, context):
|
|
request = context["request"]
|
|
url = derivative_url(obj.profile_picture, PREVIEW_VARIANT)
|
|
return request.build_absolute_uri(url) if url else None
|
|
|
|
|
|
class AuthorizationRoleSchema(Schema):
|
|
key: str
|
|
label: str
|
|
description: str
|
|
enabled: bool = False
|
|
locked: bool = False
|
|
|
|
|
|
class UserAuthorizationSchema(Schema):
|
|
id: int
|
|
username: str
|
|
email: Optional[str] = None
|
|
mobile: Optional[str] = None
|
|
first_name: str
|
|
last_name: str
|
|
is_active: bool
|
|
is_staff: bool
|
|
is_superuser: bool
|
|
groups: List[str]
|
|
roles: List[AuthorizationRoleSchema]
|
|
|
|
|
|
class UserAuthorizationUpdateSchema(Schema):
|
|
is_staff: bool = False
|
|
groups: List[str] = []
|
|
|
|
|
|
class UserUpdateSchema(Schema):
|
|
username: Optional[str] = None
|
|
email: Optional[str] = None
|
|
first_name: Optional[str] = None
|
|
last_name: Optional[str] = None
|
|
bio: Optional[str] = Field(default=None, max_length=500)
|
|
year_of_study: Optional[int] = None
|
|
major: Optional[str] = None
|
|
university: Optional[str] = None
|
|
student_id: Optional[str] = None
|
|
show_public_profile_details: Optional[bool] = None
|
|
|
|
|
|
class PublicProfileEventSchema(Schema):
|
|
title: str
|
|
slug: str
|
|
start_time: datetime
|
|
end_time: datetime
|
|
registration_start_date: Optional[datetime] = None
|
|
registration_end_date: Optional[datetime] = None
|
|
effective_registration_end_date: datetime
|
|
capacity: Optional[int] = None
|
|
registration_count: int = 0
|
|
status: str
|
|
status_label: str
|
|
is_cancelled: bool = False
|
|
image_url: Optional[str] = None
|
|
participation: Literal["confirmed", "attended"]
|
|
|
|
|
|
class PublicMemberProfileSchema(Schema):
|
|
username: str
|
|
first_name: str
|
|
last_name: str
|
|
full_name: str
|
|
profile_picture: Optional[str] = None
|
|
bio: str = ""
|
|
details_visible: bool
|
|
mobile: Optional[str] = None
|
|
email: Optional[str] = None
|
|
student_id: Optional[str] = None
|
|
year_of_study: Optional[int] = None
|
|
university: Optional[str] = None
|
|
major: Optional[str] = None
|
|
badges: List[PublicGamificationBadgeSchema] = []
|
|
events: List[PublicProfileEventSchema] = []
|
|
|
|
|
|
class TokenSchema(Schema):
|
|
access_token: str
|
|
refresh_token: str
|
|
token_type: str = "bearer"
|
|
|
|
|
|
class TokenRefreshIn(Schema):
|
|
refresh_token: str
|
|
|
|
|
|
class PasswordResetSchema(Schema):
|
|
mobile: str
|
|
code: str
|
|
new_password: str
|
|
|
|
|
|
class UsernameCheckSchema(Schema):
|
|
exists: bool
|
|
|
|
|
|
class MobileLookupSchema(Schema):
|
|
exists: bool
|
|
has_password: bool
|
|
|
|
|
|
class OtpSendResponseSchema(Schema):
|
|
message: str
|
|
expires_in_seconds: int
|
|
expires_at: datetime
|