feat(events): expand admin event management APIs
This commit is contained in:
@@ -138,6 +138,7 @@ class EventListSchema(Schema):
|
|||||||
class EventCreateSchema(Schema):
|
class EventCreateSchema(Schema):
|
||||||
"""Payload for creating events via the API."""
|
"""Payload for creating events via the API."""
|
||||||
title: str
|
title: str
|
||||||
|
slug: Optional[str] = None
|
||||||
description: str
|
description: str
|
||||||
event_type: str
|
event_type: str
|
||||||
address: Optional[str] = None
|
address: Optional[str] = None
|
||||||
@@ -150,11 +151,13 @@ class EventCreateSchema(Schema):
|
|||||||
capacity: Optional[int] = None
|
capacity: Optional[int] = None
|
||||||
price: Optional[float] = None
|
price: Optional[float] = None
|
||||||
status: str = "draft"
|
status: str = "draft"
|
||||||
|
registration_success_markdown: Optional[str] = None
|
||||||
gallery_image_ids: Optional[List[int]] = []
|
gallery_image_ids: Optional[List[int]] = []
|
||||||
|
|
||||||
class EventUpdateSchema(Schema):
|
class EventUpdateSchema(Schema):
|
||||||
"""Payload for updating events via the API."""
|
"""Payload for updating events via the API."""
|
||||||
title: Optional[str] = None
|
title: Optional[str] = None
|
||||||
|
slug: Optional[str] = None
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
event_type: Optional[str] = None
|
event_type: Optional[str] = None
|
||||||
address: Optional[str] = None
|
address: Optional[str] = None
|
||||||
@@ -167,6 +170,7 @@ class EventUpdateSchema(Schema):
|
|||||||
capacity: Optional[int] = None
|
capacity: Optional[int] = None
|
||||||
price: Optional[float] = None
|
price: Optional[float] = None
|
||||||
status: Optional[str] = None
|
status: Optional[str] = None
|
||||||
|
registration_success_markdown: Optional[str] = None
|
||||||
gallery_image_ids: Optional[List[int]] = None
|
gallery_image_ids: Optional[List[int]] = None
|
||||||
|
|
||||||
class RegistrationSchema(ModelSchema):
|
class RegistrationSchema(ModelSchema):
|
||||||
@@ -199,12 +203,56 @@ class AdminUserSchema(Schema):
|
|||||||
first_name: str
|
first_name: str
|
||||||
last_name: str
|
last_name: str
|
||||||
email: str
|
email: str
|
||||||
|
mobile: Optional[str] = None
|
||||||
|
profile_picture: Optional[str] = None
|
||||||
|
profile_picture_thumbnail_url: Optional[str] = None
|
||||||
|
profile_picture_preview_url: Optional[str] = None
|
||||||
|
university: Optional[str] = None
|
||||||
|
major: Optional[str] = None
|
||||||
|
student_id: Optional[str] = None
|
||||||
|
year_of_study: Optional[int] = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_profile_picture(obj, context):
|
||||||
|
image = getattr(obj, "profile_picture", None)
|
||||||
|
if not getattr(image, "name", None):
|
||||||
|
return None
|
||||||
|
request = context["request"]
|
||||||
|
return request.build_absolute_uri(image.url) if hasattr(image, "url") else None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_profile_picture_thumbnail_url(obj, context):
|
||||||
|
image = getattr(obj, "profile_picture", None)
|
||||||
|
if not getattr(image, "name", None):
|
||||||
|
return None
|
||||||
|
request = context["request"]
|
||||||
|
url = derivative_url(image, THUMBNAIL_VARIANT)
|
||||||
|
return request.build_absolute_uri(url) if url else None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_profile_picture_preview_url(obj, context):
|
||||||
|
image = getattr(obj, "profile_picture", None)
|
||||||
|
if not getattr(image, "name", None):
|
||||||
|
return None
|
||||||
|
request = context["request"]
|
||||||
|
url = derivative_url(image, PREVIEW_VARIANT)
|
||||||
|
return request.build_absolute_uri(url) if url else None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_university(obj):
|
||||||
|
return obj.get_university_display()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_major(obj):
|
||||||
|
return obj.get_major_display()
|
||||||
|
|
||||||
|
|
||||||
class PaymentAdminSchema(Schema):
|
class PaymentAdminSchema(Schema):
|
||||||
id: int
|
id: int
|
||||||
authority: Optional[str]
|
authority: Optional[str]
|
||||||
ref_id: Optional[str]
|
ref_id: Optional[str]
|
||||||
|
card_pan: Optional[str]
|
||||||
|
card_hash: Optional[str]
|
||||||
status: int
|
status: int
|
||||||
status_label: str
|
status_label: str
|
||||||
base_amount: int
|
base_amount: int
|
||||||
@@ -241,7 +289,7 @@ class EventAdminDetailSchema(EventSchema):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_registrations(obj):
|
def resolve_registrations(obj):
|
||||||
return obj.registrations.select_related("user").prefetch_related(
|
return obj.registrations.select_related("user", "user__university", "user__major").prefetch_related(
|
||||||
"payments__discount_code"
|
"payments__discount_code"
|
||||||
).order_by("-registered_at")
|
).order_by("-registered_at")
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.db.models import Q, Case, When, IntegerField
|
from django.db.models import Q, Case, When, IntegerField
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
from ninja import Router, Query
|
from ninja import File, Router, Query, UploadedFile
|
||||||
from ninja.errors import HttpError
|
from ninja.errors import HttpError
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID, uuid4
|
||||||
|
|
||||||
from apps.events.api.schemas import (
|
from apps.events.api.schemas import (
|
||||||
EventAdminDetailSchema,
|
EventAdminDetailSchema,
|
||||||
EventBriefSchema,
|
EventBriefSchema,
|
||||||
EventCreateSchema,
|
EventCreateSchema,
|
||||||
|
EventGallerySchema,
|
||||||
EventListSchema,
|
EventListSchema,
|
||||||
EventSchema,
|
EventSchema,
|
||||||
EventUpdateSchema,
|
EventUpdateSchema,
|
||||||
@@ -26,6 +28,8 @@ from apps.events.api.schemas import (
|
|||||||
)
|
)
|
||||||
from core.authentication import jwt_auth
|
from core.authentication import jwt_auth
|
||||||
from apps.events.models import Event, Registration
|
from apps.events.models import Event, Registration
|
||||||
|
from apps.gallery.models import Gallery
|
||||||
|
from apps.gallery.tasks import process_uploaded_image
|
||||||
from apps.notifications.services import notify_user
|
from apps.notifications.services import notify_user
|
||||||
from apps.payments.models import DiscountCode
|
from apps.payments.models import DiscountCode
|
||||||
from apps.users.tasks import send_critical_sms
|
from apps.users.tasks import send_critical_sms
|
||||||
@@ -34,6 +38,28 @@ from core.api.schemas import ErrorSchema, MessageSchema
|
|||||||
events_router = Router()
|
events_router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
def _is_staff_user(user) -> bool:
|
||||||
|
return bool(user and (user.is_staff or user.is_superuser))
|
||||||
|
|
||||||
|
|
||||||
|
def _staff_forbidden():
|
||||||
|
return 403, {"error": "اجازه دسترسی ندارید."}
|
||||||
|
|
||||||
|
|
||||||
|
def _save_uploaded_image(instance, field_name: str, file: UploadedFile, folder: str):
|
||||||
|
if not file.content_type or not file.content_type.startswith("image/"):
|
||||||
|
return False, {"error": "فایل باید تصویر باشد."}
|
||||||
|
if file.size > 10 * 1024 * 1024:
|
||||||
|
return False, {"error": "حجم فایل باید کمتر از ۱۰ مگابایت باشد."}
|
||||||
|
extension = file.name.rsplit(".", 1)[-1] if "." in file.name else "jpg"
|
||||||
|
getattr(instance, field_name).save(
|
||||||
|
f"{folder}/{uuid4().hex}.{extension}",
|
||||||
|
ContentFile(file.read()),
|
||||||
|
save=True,
|
||||||
|
)
|
||||||
|
return True, instance
|
||||||
|
|
||||||
|
|
||||||
def _frontend_event_url(event: Event) -> str:
|
def _frontend_event_url(event: Event) -> str:
|
||||||
root = getattr(settings, "FRONTEND_ROOT", "/") or "/"
|
root = getattr(settings, "FRONTEND_ROOT", "/") or "/"
|
||||||
if not root.endswith("/"):
|
if not root.endswith("/"):
|
||||||
@@ -130,20 +156,32 @@ def get_event_by_slug(request, slug: str):
|
|||||||
)
|
)
|
||||||
return event
|
return event
|
||||||
|
|
||||||
@events_router.post("/", response=EventSchema)
|
@events_router.post("/", response={201: EventSchema, 403: ErrorSchema, 400: ErrorSchema}, auth=jwt_auth)
|
||||||
def create_event(request, payload: EventCreateSchema):
|
def create_event(request, payload: EventCreateSchema):
|
||||||
"""Create a new event"""
|
"""Create a new event"""
|
||||||
gallery_image_ids = payload.dict().pop('gallery_image_ids', [])
|
if not _is_staff_user(request.auth):
|
||||||
event = Event.objects.create(**payload.dict(exclude={'gallery_image_ids'}))
|
return _staff_forbidden()
|
||||||
|
data = payload.dict(exclude={'gallery_image_ids'})
|
||||||
|
gallery_image_ids = payload.gallery_image_ids or []
|
||||||
|
if data.get("slug"):
|
||||||
|
data["slug"] = slugify(data["slug"])
|
||||||
|
event = Event(**data)
|
||||||
|
try:
|
||||||
|
event.full_clean()
|
||||||
|
event.save()
|
||||||
|
except Exception as exc:
|
||||||
|
return 400, {"error": str(exc)}
|
||||||
|
|
||||||
if gallery_image_ids:
|
if gallery_image_ids:
|
||||||
event.gallery_images.set(gallery_image_ids)
|
event.gallery_images.set(gallery_image_ids)
|
||||||
|
|
||||||
return event
|
return 201, event
|
||||||
|
|
||||||
@events_router.put("/{int:event_id}", response=EventSchema)
|
@events_router.put("/{int:event_id}", response={200: EventSchema, 403: ErrorSchema, 400: ErrorSchema}, auth=jwt_auth)
|
||||||
def update_event(request, event_id: int, payload: EventUpdateSchema):
|
def update_event(request, event_id: int, payload: EventUpdateSchema):
|
||||||
"""Update an existing event"""
|
"""Update an existing event"""
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
previous_state = {
|
previous_state = {
|
||||||
"status": event.status,
|
"status": event.status,
|
||||||
@@ -158,12 +196,18 @@ def update_event(request, event_id: int, payload: EventUpdateSchema):
|
|||||||
gallery_image_ids = update_data.pop('gallery_image_ids', None)
|
gallery_image_ids = update_data.pop('gallery_image_ids', None)
|
||||||
|
|
||||||
for attr, value in update_data.items():
|
for attr, value in update_data.items():
|
||||||
|
if attr == "slug" and value:
|
||||||
|
value = slugify(value)
|
||||||
setattr(event, attr, value)
|
setattr(event, attr, value)
|
||||||
|
|
||||||
if 'title' in update_data:
|
if 'title' in update_data and not update_data.get("slug"):
|
||||||
event.slug = slugify(event.title)
|
event.slug = slugify(event.title)
|
||||||
|
|
||||||
event.save()
|
try:
|
||||||
|
event.full_clean()
|
||||||
|
event.save()
|
||||||
|
except Exception as exc:
|
||||||
|
return 400, {"error": str(exc)}
|
||||||
|
|
||||||
if gallery_image_ids is not None:
|
if gallery_image_ids is not None:
|
||||||
event.gallery_images.set(gallery_image_ids)
|
event.gallery_images.set(gallery_image_ids)
|
||||||
@@ -196,14 +240,94 @@ def update_event(request, event_id: int, payload: EventUpdateSchema):
|
|||||||
sms_kind="event_reschedule",
|
sms_kind="event_reschedule",
|
||||||
)
|
)
|
||||||
|
|
||||||
return event
|
return 200, event
|
||||||
|
|
||||||
@events_router.delete("/{int:event_id}", response=MessageSchema)
|
@events_router.delete("/{int:event_id}", response={200: MessageSchema, 403: ErrorSchema}, auth=jwt_auth)
|
||||||
def delete_event(request, event_id: int):
|
def delete_event(request, event_id: int):
|
||||||
"""Soft delete an event"""
|
"""Soft delete an event"""
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
event.delete()
|
event.delete()
|
||||||
return {"message": "Event deleted successfully"}
|
return 200, {"message": "Event deleted successfully"}
|
||||||
|
|
||||||
|
|
||||||
|
@events_router.post("/{int:event_id}/featured-image", response={200: EventSchema, 403: ErrorSchema, 400: ErrorSchema}, auth=jwt_auth)
|
||||||
|
def upload_event_featured_image(request, event_id: int, file: UploadedFile = File(...)):
|
||||||
|
"""Upload or replace the poster/featured image for an event."""
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
|
ok, result = _save_uploaded_image(event, "featured_image", file, "events/featured")
|
||||||
|
if not ok:
|
||||||
|
return 400, result
|
||||||
|
return 200, event
|
||||||
|
|
||||||
|
|
||||||
|
@events_router.delete("/{int:event_id}/featured-image", response={200: EventSchema, 403: ErrorSchema}, auth=jwt_auth)
|
||||||
|
def delete_event_featured_image(request, event_id: int):
|
||||||
|
"""Remove the poster/featured image for an event."""
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
|
if event.featured_image:
|
||||||
|
event.featured_image.delete(save=False)
|
||||||
|
event.featured_image = None
|
||||||
|
event.save(update_fields=["featured_image", "updated_at"])
|
||||||
|
return 200, event
|
||||||
|
|
||||||
|
|
||||||
|
@events_router.get("/{int:event_id}/gallery", response={200: List[EventGallerySchema], 403: ErrorSchema}, auth=jwt_auth)
|
||||||
|
def list_event_gallery(request, event_id: int):
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
|
return 200, event.gallery_images.filter(is_deleted=False).select_related("uploaded_by")
|
||||||
|
|
||||||
|
|
||||||
|
@events_router.post("/{int:event_id}/gallery", response={201: EventGallerySchema, 403: ErrorSchema, 400: ErrorSchema}, auth=jwt_auth)
|
||||||
|
def upload_event_gallery_image(
|
||||||
|
request,
|
||||||
|
event_id: int,
|
||||||
|
file: UploadedFile = File(...),
|
||||||
|
title: str | None = None,
|
||||||
|
alt_text: str | None = None,
|
||||||
|
):
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
|
if not file.content_type or not file.content_type.startswith("image/"):
|
||||||
|
return 400, {"error": "فایل باید تصویر باشد."}
|
||||||
|
if file.size > 10 * 1024 * 1024:
|
||||||
|
return 400, {"error": "حجم فایل باید کمتر از ۱۰ مگابایت باشد."}
|
||||||
|
try:
|
||||||
|
gallery_item = Gallery.objects.create(
|
||||||
|
title=title or file.name,
|
||||||
|
description="",
|
||||||
|
uploaded_by=request.auth,
|
||||||
|
alt_text=alt_text or title or file.name,
|
||||||
|
is_public=True,
|
||||||
|
)
|
||||||
|
gallery_item._defer_image_processing = True
|
||||||
|
extension = file.name.rsplit(".", 1)[-1] if "." in file.name else "jpg"
|
||||||
|
gallery_item.image.save(f"gallery/{uuid4().hex}.{extension}", ContentFile(file.read()))
|
||||||
|
event.gallery_images.add(gallery_item)
|
||||||
|
process_uploaded_image.delay(gallery_item.id)
|
||||||
|
except Exception as exc:
|
||||||
|
return 400, {"error": str(exc)}
|
||||||
|
return 201, gallery_item
|
||||||
|
|
||||||
|
|
||||||
|
@events_router.delete("/{int:event_id}/gallery/{int:image_id}", response={200: MessageSchema, 403: ErrorSchema}, auth=jwt_auth)
|
||||||
|
def delete_event_gallery_image(request, event_id: int, image_id: int):
|
||||||
|
if not _is_staff_user(request.auth):
|
||||||
|
return _staff_forbidden()
|
||||||
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
|
image = get_object_or_404(Gallery, id=image_id, is_deleted=False)
|
||||||
|
event.gallery_images.remove(image)
|
||||||
|
if not image.event_galleries.exclude(id=event.id).exists():
|
||||||
|
image.delete()
|
||||||
|
return 200, {"message": "Gallery image removed"}
|
||||||
|
|
||||||
# Registration endpoints
|
# Registration endpoints
|
||||||
@events_router.get("/{int:event_id}/registrations", response=List[RegistrationSchema])
|
@events_router.get("/{int:event_id}/registrations", response=List[RegistrationSchema])
|
||||||
@@ -235,7 +359,7 @@ def list_event_registrations_admin(
|
|||||||
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
event = get_object_or_404(Event, id=event_id, is_deleted=False)
|
||||||
qs = (
|
qs = (
|
||||||
event.registrations.filter(is_deleted=False)
|
event.registrations.filter(is_deleted=False)
|
||||||
.select_related("user")
|
.select_related("user", "user__university", "user__major")
|
||||||
.prefetch_related("payments__discount_code")
|
.prefetch_related("payments__discount_code")
|
||||||
.order_by("-registered_at")
|
.order_by("-registered_at")
|
||||||
)
|
)
|
||||||
@@ -259,6 +383,7 @@ def list_event_registrations_admin(
|
|||||||
if search:
|
if search:
|
||||||
qs = qs.filter(
|
qs = qs.filter(
|
||||||
Q(user__username__icontains=search)
|
Q(user__username__icontains=search)
|
||||||
|
| Q(user__mobile__icontains=search)
|
||||||
| Q(user__email__icontains=search)
|
| Q(user__email__icontains=search)
|
||||||
| Q(user__first_name__icontains=search)
|
| Q(user__first_name__icontains=search)
|
||||||
| Q(user__last_name__icontains=search)
|
| Q(user__last_name__icontains=search)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class EventsAPIIntegrationTests(TestCase):
|
|||||||
cls.user = User.objects.create_user(
|
cls.user = User.objects.create_user(
|
||||||
username="event_user",
|
username="event_user",
|
||||||
email="event.user@example.com",
|
email="event.user@example.com",
|
||||||
|
mobile="09198000001",
|
||||||
password=cls.password,
|
password=cls.password,
|
||||||
)
|
)
|
||||||
cls.user.is_email_verified = True
|
cls.user.is_email_verified = True
|
||||||
@@ -45,6 +46,7 @@ class EventsAPIIntegrationTests(TestCase):
|
|||||||
cls.staff = User.objects.create_user(
|
cls.staff = User.objects.create_user(
|
||||||
username="event_staff",
|
username="event_staff",
|
||||||
email="event.staff@example.com",
|
email="event.staff@example.com",
|
||||||
|
mobile="09198000002",
|
||||||
password=cls.password,
|
password=cls.password,
|
||||||
is_staff=True,
|
is_staff=True,
|
||||||
)
|
)
|
||||||
@@ -151,19 +153,21 @@ class EventsAPIIntegrationTests(TestCase):
|
|||||||
"/api/events/",
|
"/api/events/",
|
||||||
data=json.dumps(payload),
|
data=json.dumps(payload),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
|
**self._auth_headers(self.staff_token),
|
||||||
)
|
)
|
||||||
self.assertEqual(created.status_code, 200)
|
self.assertEqual(created.status_code, 201)
|
||||||
event_id = created.json()["id"]
|
event_id = created.json()["id"]
|
||||||
|
|
||||||
updated = self.client.put(
|
updated = self.client.put(
|
||||||
f"/api/events/{event_id}",
|
f"/api/events/{event_id}",
|
||||||
data=json.dumps({"title": "Updated Event"}),
|
data=json.dumps({"title": "Updated Event"}),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
|
**self._auth_headers(self.staff_token),
|
||||||
)
|
)
|
||||||
self.assertEqual(updated.status_code, 200)
|
self.assertEqual(updated.status_code, 200)
|
||||||
self.assertEqual(updated.json()["title"], "Updated Event")
|
self.assertEqual(updated.json()["title"], "Updated Event")
|
||||||
|
|
||||||
deleted = self.client.delete(f"/api/events/{event_id}")
|
deleted = self.client.delete(f"/api/events/{event_id}", **self._auth_headers(self.staff_token))
|
||||||
self.assertEqual(deleted.status_code, 200)
|
self.assertEqual(deleted.status_code, 200)
|
||||||
|
|
||||||
def test_admin_detail_and_registration_list_requires_staff(self):
|
def test_admin_detail_and_registration_list_requires_staff(self):
|
||||||
@@ -230,9 +234,10 @@ class EventsAPIIntegrationTests(TestCase):
|
|||||||
"/api/events/",
|
"/api/events/",
|
||||||
data=json.dumps(payload),
|
data=json.dumps(payload),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
|
**self._auth_headers(self.staff_token),
|
||||||
)
|
)
|
||||||
body = response.json()
|
body = response.json()
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 201)
|
||||||
self.assertTrue(body["gallery_images"])
|
self.assertTrue(body["gallery_images"])
|
||||||
|
|
||||||
updated = self.client.put(
|
updated = self.client.put(
|
||||||
@@ -244,6 +249,7 @@ class EventsAPIIntegrationTests(TestCase):
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
content_type="application/json",
|
content_type="application/json",
|
||||||
|
**self._auth_headers(self.staff_token),
|
||||||
)
|
)
|
||||||
self.assertEqual(updated.status_code, 200)
|
self.assertEqual(updated.status_code, 200)
|
||||||
self.assertEqual(updated.json()["slug"], "gallery-event-updated")
|
self.assertEqual(updated.json()["slug"], "gallery-event-updated")
|
||||||
@@ -370,7 +376,8 @@ class EventsAPIIntegrationTests(TestCase):
|
|||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
|
|
||||||
def _create_event_user(self, username, email):
|
def _create_event_user(self, username, email):
|
||||||
user = User.objects.create_user(username=username, email=email, password=self.password)
|
suffix = str(abs(hash(username)) % 1_000_000).zfill(6)
|
||||||
|
user = User.objects.create_user(username=username, email=email, mobile=f"09190{suffix}", password=self.password)
|
||||||
user.is_email_verified = True
|
user.is_email_verified = True
|
||||||
user.save(update_fields=["is_email_verified"])
|
user.save(update_fields=["is_email_verified"])
|
||||||
user.major = self.user.major
|
user.major = self.user.major
|
||||||
@@ -468,6 +475,7 @@ class EventSchemasIntegrationTests(TestCase):
|
|||||||
self.user = User.objects.create_user(
|
self.user = User.objects.create_user(
|
||||||
username="schema_user",
|
username="schema_user",
|
||||||
email="schema.user@example.com",
|
email="schema.user@example.com",
|
||||||
|
mobile="09198000003",
|
||||||
password=self.password,
|
password=self.password,
|
||||||
)
|
)
|
||||||
self.user.is_email_verified = True
|
self.user.is_email_verified = True
|
||||||
|
|||||||
Reference in New Issue
Block a user