init
Some checks failed
CI/CD / Backend & Frontend Checks (push) Has been cancelled
CI/CD / Deploy to Production (push) Has been cancelled

This commit is contained in:
2026-05-18 11:34:07 +03:30
commit 7a8ddeabed
279 changed files with 37390 additions and 0 deletions

122
backend/users/admin.py Normal file
View File

@@ -0,0 +1,122 @@
from django import forms
from django.utils import timezone
from django.conf import settings
from django.contrib import admin, messages
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from import_export.admin import ImportExportModelAdmin
from simplemde.widgets import SimpleMDEEditor
from users.models import User, University, Major
from users.resources import UserResource
from users.tasks import send_verification_email
from utils.admin import SoftDeleteListFilter, BaseModelAdmin
class UserAdminForm(forms.ModelForm):
bio = forms.CharField(widget=SimpleMDEEditor(), required=False)
student_id = forms.CharField(required=False)
class Meta:
model = User
fields = '__all__'
@admin.register(User)
class UserAdmin(BaseUserAdmin, BaseModelAdmin, ImportExportModelAdmin):
form = UserAdminForm
resource_class = UserResource
list_display = ('email', 'username', 'university', 'is_email_verified', 'date_joined')
list_filter = ('is_email_verified', 'is_staff', 'year_of_study', SoftDeleteListFilter)
search_fields = ('email', 'username', 'student_id', 'first_name', 'last_name')
ordering = ('-date_joined',)
fieldsets = (
('Auth Credentials', {'fields': ('username', 'email', 'password')}),
('Personal info', {
'fields': ('first_name', 'last_name', 'student_id', 'university', 'year_of_study', 'major', 'bio', 'profile_picture')
}),
('Permissions', {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions',),
}),
('Important dates', {'fields': ('last_login', 'date_joined')}),
('Email Verification', {
'fields': ('is_email_verified', 'email_verification_token', 'email_verification_sent_at')
}),
('Password Reset', {
'fields': ('password_reset_token', 'password_reset_token_expires_at'),
'classes': ('collapse',)
}),
('Soft Delete', {
'fields': ('is_deleted', 'deleted_at'),
'classes': ('collapse',)
}),
)
add_fieldsets = (
(
'Step 1',
{
'classes': ('wide',),
'fields': ('email', 'student_id', 'password1', 'password2', 'usable_password'),
},
),
)
readonly_fields = ('email_verification_token', 'email_verification_sent_at', 'deleted_at',
'password_reset_token', 'password_reset_token_expires_at')
actions = BaseModelAdmin.actions + [
'verify_emails',
'resend_verification_email',
]
@admin.action(description='Verify selected user emails')
def verify_emails(self, request, queryset):
queryset.update(is_email_verified=True)
self.message_user(request, f'Verified {queryset.count()} user emails.')
@admin.action(description="Resend verification email")
def resend_verification_email(self, request, queryset):
qs = queryset.filter(is_email_verified=False).exclude(email__isnull=True).exclude(email="")
total = queryset.count()
to_send = qs.count()
skipped = total - to_send
sent = failed = 0
for user in qs:
try:
user.regenerate_verification_token()
user.email_verification_sent_at = timezone.now()
user.save(update_fields=["email_verification_sent_at"])
verification_url = f"{settings.FRONTEND_ROOT}verify-email/{user.email_verification_token}"
send_verification_email.delay(user.id, verification_url)
sent += 1
except Exception as exc:
failed += 1
if sent:
self.message_user(request, f"ایمیل تأیید برای {sent} کاربر ارسال شد.", level=messages.SUCCESS)
if skipped:
self.message_user(
request,
f"{skipped} کاربر کنار گذاشته شدند (یا قبلاً تأیید شده‌اند یا ایمیل ندارند).",
level=messages.WARNING,
)
if failed:
self.message_user(request, f"ارسال برای {failed} کاربر با خطا مواجه شد.", level=messages.ERROR)
@admin.register(University)
class UniversityAdmin(BaseModelAdmin):
list_display = ('name', 'code', 'is_active', 'created_at')
list_filter = ('is_active', SoftDeleteListFilter)
search_fields = ('name', 'code')
@admin.register(Major)
class MajorAdmin(BaseModelAdmin):
list_display = ('name', 'code', 'is_active', 'created_at')
list_filter = ('is_active', SoftDeleteListFilter)
search_fields = ('name', 'code')

8
backend/users/apps.py Normal file
View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'users'
def ready(self):
import users.signals

View File

@@ -0,0 +1,48 @@
[
{"model":"users.user","fields":{"username":"u1403020111029","email":"pending-1403020111029@noemail.local","first_name":"پوریا","last_name":"شامخی","student_id":"1403020111029","year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1400020111002","email":"pending-1400020111002@noemail.local","first_name":"سمانه","last_name":"جباری","student_id":"1400020111002","year_of_study":1400,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u990201110035","email":"pending-990201110035@noemail.local","first_name":"سید علی","last_name":"حجتی مقدم","student_id":"990201110035","year_of_study":1399,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u990201200032","email":"pending-990201200032@noemail.local","first_name":"مهدی","last_name":"خدیوی سرشت","student_id":"990201200032","year_of_study":1399,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020111026","email":"pending-1403020111026@noemail.local","first_name":"امیر سجاد","last_name":"حیدری","student_id":"1403020111026","year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020111037","email":"pending-1403020111037@noemail.local","first_name":"امیرکیان","last_name":"رادپور","student_id":"1403020111037","year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120011","email":"pending-1401020120011@noemail.local","first_name":"شیما","last_name":"گندم‌کار","student_id":"1401020120011","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120024","email":"pending-1401020120024@noemail.local","first_name":"رضا","last_name":"سالمی‌درگاهی","student_id":"1401020120024","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120102","email":"pending-1401020120102@noemail.local","first_name":"امیرمحمد","last_name":"نیک‌کار","student_id":"1401020120102","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120028","email":"pending-1401020120028@noemail.local","first_name":"امیرمحمد","last_name":"کیان‌فر","student_id":"1401020120028","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120035","email":"pending-1401020120035@noemail.local","first_name":"رژان","last_name":"پناهی‌پور","student_id":"1401020120035","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1400020111032","email":"pending-1400020111032@noemail.local","first_name":"مریم","last_name":"صفری","student_id":"1400020111032","year_of_study":1400,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1400020111014","email":"pending-1400020111014@noemail.local","first_name":"علیرضا","last_name":"رحیمی","student_id":"1400020111014","year_of_study":1400,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u992818200","email":"pending-992818200@noemail.local","first_name":"مریم","last_name":"مسلمی دوران محله","student_id":"992818200","year_of_study":1400,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1400020111022","email":"pending-1400020111022@noemail.local","first_name":"امیرمحمد","last_name":"خیراندیش","student_id":"1400020111022","year_of_study":1400,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1400020111029","email":"pending-1400020111029@noemail.local","first_name":"امیرحسین","last_name":"حسن‌پور","student_id":"1400020111029","year_of_study":1400,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u990201201007","email":"pending-990201201007@noemail.local","first_name":"امیررضا","last_name":"اخلاقی","student_id":"990201201007","year_of_study":1399,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020111006","email":"pending-1403020111006@noemail.local","first_name":"سینا","last_name":"زمان‌پور","student_id":"1403020111006","year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020130021","email":"pending-1403020130021@noemail.local","first_name":"سبحان","last_name":"آسوده جلالی","student_id":"1403020130021","year_of_study":1403,"major":null,"university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403012268121","email":"pending-1403012268121@noemail.local","first_name":"فربد","last_name":"خلیلی خوشه مهر","student_id":"1403012268121","year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u03111129302057","email":"pending-03111129302057@noemail.local","first_name":"محمد مهدی","last_name":"جباری","student_id":"03111129302057","year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121009","email":"pending-1403020121009@noemail.local","first_name":"امیرحسین","last_name":"امین‌پور","student_id":"1403020121009","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121013","email":"pending-1403020121013@noemail.local","first_name":"عرشیا","last_name":"عرشی","student_id":"1403020121013","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121023","email":"pending-1403020121023@noemail.local","first_name":"طاها","last_name":"محیط مافی","student_id":"1403020121023","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"uidx28","email":"pending-idx28@noemail.local","first_name":"مهدی","last_name":"منصورپور","student_id":null,"year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121007","email":"pending-1403020121007@noemail.local","first_name":"سید محمدرضا","last_name":"حسین‌نیان","student_id":"1403020121007","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121001","email":"pending-1403020121001@noemail.local","first_name":"محمود","last_name":"یاسری","student_id":"1403020121001","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120039","email":"pending-1401020120039@noemail.local","first_name":"ارشاد","last_name":"ایزدی","student_id":"1401020120039","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120002","email":"pending-1401020120002@noemail.local","first_name":"دلناز","last_name":"محمودی","student_id":"1401020120002","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121018","email":"pending-1403020121018@noemail.local","first_name":"اروین","last_name":"نعمتی","student_id":"1403020121018","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120149","email":"pending-1401020120149@noemail.local","first_name":"مائده","last_name":"حسرت قرانی","student_id":"1401020120149","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120036","email":"pending-1401020120036@noemail.local","first_name":"شهریار","last_name":"اقاجانی","student_id":"1401020120036","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121027","email":"pending-1403020121027@noemail.local","first_name":"عمید","last_name":"عباسی","student_id":"1403020121027","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u990201200016","email":"pending-990201200016@noemail.local","first_name":"مهدی","last_name":"دیداری","student_id":"990201200016","year_of_study":1399,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120041","email":"pending-1401020120041@noemail.local","first_name":"حمید","last_name":"عباسی","student_id":"1401020120041","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020130022","email":"pending-1403020130022@noemail.local","first_name":"امیرمحمد","last_name":"نجفی","student_id":"1403020130022","year_of_study":1403,"major":null,"university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020111049","email":"pending-1401020111049@noemail.local","first_name":"علی","last_name":"رهگذر","student_id":"1401020111049","year_of_study":1401,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120103","email":"pending-1401020120103@noemail.local","first_name":"یاسان","last_name":"حاج‌قلی‌زاده","student_id":"1401020120103","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"uidx45","email":"pending-idx45@noemail.local","first_name":"امیر","last_name":"دوستی ماسوله","student_id":null,"year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120031","email":"pending-1401020120031@noemail.local","first_name":"امیررضا","last_name":"علیپور","student_id":"1401020120031","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u990201200036","email":"pending-990201200036@noemail.local","first_name":"مونا","last_name":"یحیی‌زاده واقفی","student_id":"990201200036","year_of_study":1399,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120005","email":"pending-1401020120005@noemail.local","first_name":"بهار","last_name":"محمدی","student_id":"1401020120005","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1401020120026","email":"pending-1401020120026@noemail.local","first_name":"مطهره","last_name":"حق‌شناس","student_id":"1401020120026","year_of_study":1401,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u1403020121020","email":"pending-1403020121020@noemail.local","first_name":"محمد","last_name":"خلیلی‌مقدم ملامحله","student_id":"1403020121020","year_of_study":1403,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"u990201200027","email":"pending-990201200027@noemail.local","first_name":"مهراب","last_name":"گودرزی","student_id":"990201200027","year_of_study":1399,"major":"IE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}},
{"model":"users.user","fields":{"username":"uidx52","email":"pending-idx52@noemail.local","first_name":"امیرمحمد","last_name":"چرختاب مقدم","student_id":null,"year_of_study":1403,"major":"CE","university":"GILAN","is_email_verified":false,"password":"pbkdf2_sha256$390000$guilan$2FFrsLkTODb20Jf7oxtxgXXL1z6uQY8iCJJ2OGANkKk=","is_active":true,"created_at":"2025-01-01T00:00:00Z","updated_at":"2025-01-01T00:00:00Z"}}
]

View File

@@ -0,0 +1,244 @@
[
{
"model": "users.user",
"pk": 1,
"fields": {
"username": "admin",
"email": "admin@cs-association.ac.ir",
"first_name": "علی",
"last_name": "احمدی",
"student_id": "9812345001",
"year_of_study": 4,
"major": "مهندسی کامپیوتر",
"bio": "رئیس انجمن علمی مهندسی کامپیوتر دانشگاه",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440001",
"is_staff": true,
"is_superuser": true,
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-01T10:00:00Z",
"updated_at": "2024-01-01T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 2,
"fields": {
"username": "sara_mohammadi",
"email": "sara.mohammadi@student.ac.ir",
"first_name": "سارا",
"last_name": "محمدی",
"student_id": "9912345002",
"year_of_study": 3,
"major": "مهندسی کامپیوتر",
"bio": "نایب رئیس انجمن و مسئول رویدادها",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440002",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-02T10:00:00Z",
"updated_at": "2024-01-02T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 3,
"fields": {
"username": "reza_karimi",
"email": "reza.karimi@student.ac.ir",
"first_name": "رضا",
"last_name": "کریمی",
"student_id": "9912345003",
"year_of_study": 2,
"major": "مهندسی کامپیوتر",
"bio": "علاقه‌مند به هوش مصنوعی و یادگیری ماشین",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440003",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-03T10:00:00Z",
"updated_at": "2024-01-03T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 4,
"fields": {
"username": "maryam_hosseini",
"email": "maryam.hosseini@student.ac.ir",
"first_name": "مریم",
"last_name": "حسینی",
"student_id": "0012345004",
"year_of_study": 1,
"major": "مهندسی کامپیوتر",
"bio": "دانشجوی سال اول و علاقه‌مند به برنامه‌نویسی وب",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440004",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-04T10:00:00Z",
"updated_at": "2024-01-04T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 5,
"fields": {
"username": "hassan_zare",
"email": "hassan.zare@student.ac.ir",
"first_name": "حسن",
"last_name": "زارع",
"student_id": "9812345005",
"year_of_study": 4,
"major": "مهندسی کامپیوتر",
"bio": "مسئول روابط عمومی انجمن",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440005",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-05T10:00:00Z",
"updated_at": "2024-01-05T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 6,
"fields": {
"username": "zahra_safari",
"email": "zahra.safari@student.ac.ir",
"first_name": "زهرا",
"last_name": "صفری",
"student_id": "9912345006",
"year_of_study": 3,
"major": "مهندسی کامپیوتر",
"bio": "علاقه‌مند به امنیت سایبری و شبکه",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440006",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-06T10:00:00Z",
"updated_at": "2024-01-06T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 7,
"fields": {
"username": "mohammad_rahmani",
"email": "mohammad.rahmani@student.ac.ir",
"first_name": "محمد",
"last_name": "رحمانی",
"student_id": "0012345007",
"year_of_study": 1,
"major": "مهندسی کامپیوتر",
"bio": "دانشجوی جدید الورود",
"is_email_verified": false,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440007",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-07T10:00:00Z",
"updated_at": "2024-01-07T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 8,
"fields": {
"username": "fateme_moradi",
"email": "fateme.moradi@student.ac.ir",
"first_name": "فاطمه",
"last_name": "مرادی",
"student_id": "9912345008",
"year_of_study": 2,
"major": "مهندسی کامپیوتر",
"bio": "علاقه‌مند به توسعه اپلیکیشن موبایل",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440008",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-08T10:00:00Z",
"updated_at": "2024-01-08T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 9,
"fields": {
"username": "amir_ghorbani",
"email": "amir.ghorbani@student.ac.ir",
"first_name": "امیر",
"last_name": "قربانی",
"student_id": "9812345009",
"year_of_study": 4,
"major": "مهندسی کامپیوتر",
"bio": "مسئول فنی انجمن و توسعه‌دهنده وب‌سایت",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440009",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-09T10:00:00Z",
"updated_at": "2024-01-09T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 10,
"fields": {
"username": "nasrin_jafari",
"email": "nasrin.jafari@student.ac.ir",
"first_name": "نسرین",
"last_name": "جعفری",
"student_id": "9912345010",
"year_of_study": 3,
"major": "مهندسی کامپیوتر",
"bio": "علاقه‌مند به علم داده و تحلیل داده",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440010",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-10T10:00:00Z",
"updated_at": "2024-01-10T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 11,
"fields": {
"username": "mehdi_bagheri",
"email": "mehdi.bagheri@student.ac.ir",
"first_name": "مهدی",
"last_name": "باقری",
"student_id": "0012345011",
"year_of_study": 1,
"major": "مهندسی کامپیوتر",
"bio": "دانشجوی سال اول و علاقه‌مند به بازی‌سازی",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440011",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-11T10:00:00Z",
"updated_at": "2024-01-11T10:00:00Z",
"is_deleted": false
}
},
{
"model": "users.user",
"pk": 12,
"fields": {
"username": "leila_mousavi",
"email": "leila.mousavi@student.ac.ir",
"first_name": "لیلا",
"last_name": "موسوی",
"student_id": "9912345012",
"year_of_study": 2,
"major": "مهندسی کامپیوتر",
"bio": "علاقه‌مند به طراحی UI/UX",
"is_email_verified": true,
"email_verification_token": "550e8400-e29b-41d4-a716-446655440012",
"password": "pbkdf2_sha256$600000$test$test",
"created_at": "2024-01-12T10:00:00Z",
"updated_at": "2024-01-12T10:00:00Z",
"is_deleted": false
}
}
]

View File

@@ -0,0 +1,60 @@
# Generated by Django 5.2.5 on 2025-10-16 12:07
import django.contrib.auth.models
import django.contrib.auth.validators
import django.utils.timezone
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('auth', '0012_alter_user_first_name_max_length'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('is_deleted', models.BooleanField(default=False)),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('email', models.EmailField(max_length=254, unique=True)),
('bio', models.TextField(blank=True, null=True)),
('profile_picture', models.ImageField(blank=True, null=True, upload_to='profile_pictures/')),
('student_id', models.CharField(max_length=20, null=True)),
('year_of_study', models.IntegerField(blank=True, null=True)),
('major', models.CharField(blank=True, choices=[('CE', 'مهندسی کامپیوتر'), ('CS', 'علوم کامپیوتر'), ('SE', 'مهندسی نرم\u200cافزار'), ('IT', 'فناوری اطلاعات'), ('AI', 'هوش مصنوعی و رباتیک'), ('DATA', 'علم داده'), ('EE', 'مهندسی برق'), ('ME', 'مهندسی مکانیک'), ('CIV', 'مهندسی عمران'), ('CHE', 'مهندسی شیمی'), ('IE', 'مهندسی صنایع'), ('MSE', 'مهندسی مواد و متالورژی'), ('BME', 'مهندسی پزشکی'), ('ARCH', 'معماری'), ('AERO', 'مهندسی هوافضا'), ('PET', 'مهندسی نفت'), ('MIN', 'مهندسی معدن'), ('ENV', 'مهندسی محیط\u200cزیست'), ('URP', 'برنامه\u200cریزی شهری و منطقه\u200cای'), ('MATH', 'ریاضیات'), ('STAT', 'آمار'), ('PHYS', 'فیزیک'), ('CHEM', 'شیمی'), ('BIO', 'زیست\u200cشناسی'), ('GEO', 'زمین\u200cشناسی'), ('MED', 'پزشکی'), ('DEN', 'دندان\u200cپزشکی'), ('PHARM', 'داروسازی'), ('NURS', 'پرستاری'), ('MID', 'مامایی'), ('LAB', 'علوم آزمایشگاهی'), ('RAD', 'رادیولوژی'), ('ANES', 'بیهوشی'), ('PUBH', 'بهداشت'), ('AGRI', 'کشاورزی (عمومی)'), ('HORT', 'باغبانی'), ('PLP', 'گیاه\u200cپزشکی'), ('SOIL', 'علوم خاک'), ('VET', 'دامپزشکی'), ('MGT', 'مدیریت'), ('ACC', 'حسابداری'), ('FIN', 'مالی'), ('ECO', 'اقتصاد'), ('BA', 'مدیریت بازرگانی'), ('LAW', 'حقوق'), ('POL', 'علوم سیاسی'), ('SOC', 'جامعه\u200cشناسی'), ('PSY', 'روان\u200cشناسی'), ('PHIL', 'فلسفه'), ('HIST', 'تاریخ'), ('GEOG', 'جغرافیا'), ('EDU', 'علوم تربیتی'), ('PEd', 'تربیت بدنی'), ('LIT_FA', 'زبان و ادبیات فارسی'), ('LIT_EN', 'زبان و ادبیات انگلیسی'), ('LIT_AR', 'زبان و ادبیات عربی'), ('TRAN_EN', 'مترجمی زبان انگلیسی'), ('ART', 'هنرهای تجسمی'), ('GRAPH', 'گرافیک'), ('MUSIC', 'موسیقی'), ('THEAT', 'نمایش و تئاتر')], max_length=16, null=True)),
('university', models.CharField(blank=True, choices=[('UT', 'دانشگاه تهران'), ('AUT', 'دانشگاه صنعتی امیرکبیر'), ('SHARIF', 'دانشگاه صنعتی شریف'), ('SBU', 'دانشگاه شهید بهشتی'), ('IUST', 'دانشگاه علم و صنعت ایران'), ('KNTU', 'دانشگاه صنعتی خواجه\u200cنصیر'), ('MODARES', 'دانشگاه تربیت مدرس'), ('ALLAMEH', 'دانشگاه علامه طباطبایی'), ('KHARAZMI', 'دانشگاه خوارزمی'), ('ISFAHAN_UNI', 'دانشگاه اصفهان'), ('IUT', 'دانشگاه صنعتی اصفهان'), ('SHIRAZ_UNI', 'دانشگاه شیراز'), ('TABRIZ_UNI', 'دانشگاه تبریز'), ('FERDOWSI', 'دانشگاه فردوسی مشهد'), ('RAZI', 'دانشگاه رازی'), ('BUALI', 'دانشگاه بوعلی\u200cسینا'), ('KURDISTAN', 'دانشگاه کردستان'), ('YAZD_UNI', 'دانشگاه یزد'), ('KERMAN_UNI', 'دانشگاه شهید باهنر کرمان'), ('MAZANDARAN', 'دانشگاه مازندران'), ('GILAN', 'دانشگاه گیلان'), ('GOLESTAN', 'دانشگاه گلستان'), ('URMIA', 'دانشگاه ارومیه'), ('ZANJAN', 'دانشگاه زنجان'), ('ARDABIL', 'دانشگاه محقق اردبیلی'), ('ARAK_UNI', 'دانشگاه اراک'), ('SEMNAN', 'دانشگاه سمنان'), ('SHAHROOD', 'دانشگاه صنعتی شاهرود'), ('QOM_UNI', 'دانشگاه قم'), ('IKIU', 'دانشگاه بین\u200cالمللی امام خمینی قزوین'), ('MAL_ASHTAR', 'دانشگاه صنعتی مالک\u200cاشتر'), ('SAHAND', 'دانشگاه صنعتی سهند'), ('BABOL_NOSH', 'دانشگاه صنعتی نوشیروانی بابل'), ('TOLOU', 'دانشگاه تحصیلات تکمیلی صنعتی و فناوری پیشرفته کرمان'), ('TUMS', 'دانشگاه علوم پزشکی تهران'), ('SBMU_MED', 'دانشگاه علوم پزشکی شهید بهشتی'), ('IUMS_MED', 'دانشگاه علوم پزشکی ایران'), ('MUMS_MED', 'دانشگاه علوم پزشکی مشهد'), ('SUMS_MED', 'دانشگاه علوم پزشکی شیراز'), ('TBZ_MED', 'دانشگاه علوم پزشکی تبریز'), ('ISF_MED', 'دانشگاه علوم پزشکی اصفهان'), ('AJA_MED', 'دانشگاه علوم پزشکی ارتش'), ('AJUMS_MED', 'دانشگاه علوم پزشکی اهواز'), ('KUMS_MED', 'دانشگاه علوم پزشکی کرمانشاه'), ('KER_MED', 'دانشگاه علوم پزشکی کرمان'), ('IAU_TEH', 'دانشگاه آزاد اسلامی تهران'), ('IAU_SCIRES', 'دانشگاه آزاد اسلامی علوم و تحقیقات تهران'), ('IAU_MASH', 'دانشگاه آزاد اسلامی مشهد'), ('IAU_TBRZ', 'دانشگاه آزاد اسلامی تبریز'), ('IAU_SHIR', 'دانشگاه آزاد اسلامی شیراز'), ('IAU_ISF', 'دانشگاه آزاد اسلامی اصفهان (خوراسگان)'), ('IAU_RASHT', 'دانشگاه آزاد اسلامی رشت'), ('IAU_QAZ', 'دانشگاه آزاد اسلامی قزوین'), ('PNU_TEH', 'دانشگاه پیام نور تهران'), ('PNU_RAS', 'دانشگاه پیام نور رشت'), ('PNU_MASH', 'دانشگاه پیام نور مشهد'), ('PNU_TBRZ', 'دانشگاه پیام نور تبریز'), ('UAST_TEH', 'دانشگاه علمی-کاربردی تهران'), ('UAST_GIL', 'دانشگاه علمی-کاربردی گیلان'), ('TVU_TEH', 'دانشگاه فنی و حرفه\u200cای تهران'), ('TVU_GIL', 'دانشگاه فنی و حرفه\u200cای گیلان'), ('RAJAEI', 'دانشگاه تربیت دبیر شهید رجایی'), ('IMAM_SADEQ', 'دانشگاه امام صادق (ع)'), ('ART_TEH', 'دانشگاه هنر'), ('TEH_MARK', 'دانشگاه علامه محدث نوری/علامه طباطبایی (در صورت نیاز اصلاح کنید)')], max_length=16, null=True)),
('is_email_verified', models.BooleanField(default=False)),
('email_verification_token', models.UUIDField(default=uuid.uuid4, unique=True)),
('email_verification_sent_at', models.DateTimeField(blank=True, null=True)),
('password_reset_token', models.UUIDField(blank=True, null=True, unique=True)),
('password_reset_token_expires_at', models.DateTimeField(blank=True, null=True)),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
'verbose_name': 'User',
'verbose_name_plural': 'Users',
'db_table': 'users',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.5 on 2025-10-18 10:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='university',
field=models.CharField(blank=True, choices=[('UT', 'دانشگاه تهران'), ('AUT', 'دانشگاه صنعتی امیرکبیر'), ('SHARIF', 'دانشگاه صنعتی شریف'), ('SBU', 'دانشگاه شهید بهشتی'), ('IUST', 'دانشگاه علم و صنعت ایران'), ('KNTU', 'دانشگاه صنعتی خواجه\u200cنصیر'), ('MODARES', 'دانشگاه تربیت مدرس'), ('ALLAMEH', 'دانشگاه علامه طباطبایی'), ('KHARAZMI', 'دانشگاه خوارزمی'), ('ISFAHAN_UNI', 'دانشگاه اصفهان'), ('IUT', 'دانشگاه صنعتی اصفهان'), ('SHIRAZ_UNI', 'دانشگاه شیراز'), ('TABRIZ_UNI', 'دانشگاه تبریز'), ('FERDOWSI', 'دانشگاه فردوسی مشهد'), ('RAZI', 'دانشگاه رازی'), ('BUALI', 'دانشگاه بوعلی\u200cسینا'), ('KURDISTAN', 'دانشگاه کردستان'), ('YAZD_UNI', 'دانشگاه یزد'), ('KERMAN_UNI', 'دانشگاه شهید باهنر کرمان'), ('MAZANDARAN', 'دانشگاه مازندران'), ('GILAN', 'دانشگاه گیلان'), ('GOLESTAN', 'دانشگاه گلستان'), ('URMIA', 'دانشگاه ارومیه'), ('ZANJAN', 'دانشگاه زنجان'), ('ARDABIL', 'دانشگاه محقق اردبیلی'), ('ARAK_UNI', 'دانشگاه اراک'), ('SEMNAN', 'دانشگاه سمنان'), ('SHAHROOD', 'دانشگاه صنعتی شاهرود'), ('QOM_UNI', 'دانشگاه قم'), ('IKIU', 'دانشگاه بین\u200cالمللی امام خمینی قزوین'), ('MAL_ASHTAR', 'دانشگاه صنعتی مالک\u200cاشتر'), ('SAHAND', 'دانشگاه صنعتی سهند'), ('BABOL_NOSH', 'دانشگاه صنعتی نوشیروانی بابل'), ('TOLOU', 'دانشگاه تحصیلات تکمیلی صنعتی و فناوری پیشرفته کرمان'), ('TUMS', 'دانشگاه علوم پزشکی تهران'), ('SBMU_MED', 'دانشگاه علوم پزشکی شهید بهشتی'), ('IUMS_MED', 'دانشگاه علوم پزشکی ایران'), ('MUMS_MED', 'دانشگاه علوم پزشکی مشهد'), ('SUMS_MED', 'دانشگاه علوم پزشکی شیراز'), ('TBZ_MED', 'دانشگاه علوم پزشکی تبریز'), ('ISF_MED', 'دانشگاه علوم پزشکی اصفهان'), ('AJA_MED', 'دانشگاه علوم پزشکی ارتش'), ('AJUMS_MED', 'دانشگاه علوم پزشکی اهواز'), ('KUMS_MED', 'دانشگاه علوم پزشکی کرمانشاه'), ('KER_MED', 'دانشگاه علوم پزشکی کرمان'), ('IAU_TEH', 'دانشگاه آزاد اسلامی تهران'), ('IAU_SCIRES', 'دانشگاه آزاد اسلامی علوم و تحقیقات تهران'), ('IAU_MASH', 'دانشگاه آزاد اسلامی مشهد'), ('IAU_TBRZ', 'دانشگاه آزاد اسلامی تبریز'), ('IAU_SHIR', 'دانشگاه آزاد اسلامی شیراز'), ('IAU_ISF', 'دانشگاه آزاد اسلامی اصفهان'), ('IAU_RASHT', 'دانشگاه آزاد اسلامی رشت'), ('IAU_LAHIJAN', 'دانشگاه آزاد اسلامی لاهیجان'), ('IAU_QAZ', 'دانشگاه آزاد اسلامی قزوین'), ('PNU_TEH', 'دانشگاه پیام نور تهران'), ('PNU_RAS', 'دانشگاه پیام نور رشت'), ('PNU_MASH', 'دانشگاه پیام نور مشهد'), ('PNU_TBRZ', 'دانشگاه پیام نور تبریز'), ('UAST_TEH', 'دانشگاه علمی-کاربردی تهران'), ('UAST_GIL', 'دانشگاه علمی-کاربردی گیلان'), ('TVU_TEH', 'دانشگاه فنی و حرفه\u200cای تهران'), ('TVU_GIL', 'دانشگاه فنی و حرفه\u200cای گیلان'), ('RAJAEI', 'دانشگاه تربیت دبیر شهید رجایی'), ('IMAM_SADEQ', 'دانشگاه امام صادق (ع)'), ('ART_TEH', 'دانشگاه هنر')], max_length=127, null=True),
),
]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,116 @@
from django.db import migrations, models
import django.db.models.deletion
from utils.choices import MajorChoices, UniversityChoices
class Migration(migrations.Migration):
dependencies = [
("users", "0003_alter_user_university"),
]
operations = [
migrations.CreateModel(
name="University",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_deleted", models.BooleanField(default=False)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
("code", models.CharField(max_length=64, unique=True)),
("name", models.CharField(max_length=255)),
("is_active", models.BooleanField(default=True)),
],
options={
"ordering": ["name"],
},
),
migrations.CreateModel(
name="Major",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_deleted", models.BooleanField(default=False)),
("deleted_at", models.DateTimeField(blank=True, null=True)),
("code", models.CharField(max_length=64, unique=True)),
("name", models.CharField(max_length=255)),
("is_active", models.BooleanField(default=True)),
],
options={
"ordering": ["name"],
},
),
migrations.RenameField(
model_name="user",
old_name="major",
new_name="legacy_major",
),
migrations.RenameField(
model_name="user",
old_name="university",
new_name="legacy_university",
),
migrations.AlterField(
model_name="user",
name="legacy_major",
field=models.CharField(
blank=True,
choices=MajorChoices.choices,
editable=False,
max_length=16,
null=True,
),
),
migrations.AlterField(
model_name="user",
name="legacy_university",
field=models.CharField(
blank=True,
choices=UniversityChoices.choices,
editable=False,
max_length=127,
null=True,
),
),
migrations.AddField(
model_name="user",
name="major",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="users",
to="users.major",
),
),
migrations.AddField(
model_name="user",
name="university",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="users",
to="users.university",
),
),
]

View File

@@ -0,0 +1,60 @@
from django.db import migrations
from utils.choices import MajorChoices, UniversityChoices
def seed_reference_models(apps, schema_editor):
Major = apps.get_model("users", "Major")
University = apps.get_model("users", "University")
User = apps.get_model("users", "User")
major_map = {}
for code, label in MajorChoices.choices:
obj, _ = Major.objects.update_or_create(
code=code,
defaults={"name": label},
)
major_map[code] = obj
university_map = {}
for code, label in UniversityChoices.choices:
obj, _ = University.objects.update_or_create(
code=code,
defaults={"name": label},
)
university_map[code] = obj
users = User.objects.all()
for user in users.iterator():
updates = []
major_code = getattr(user, "legacy_major", None)
if major_code:
major = major_map.get(major_code)
if major and user.major_id != major.id:
user.major_id = major.id
updates.append("major")
university_code = getattr(user, "legacy_university", None)
if university_code:
uni = university_map.get(university_code)
if uni and user.university_id != uni.id:
user.university_id = uni.id
updates.append("university")
if updates:
user.save(update_fields=updates)
def noop(apps, schema_editor):
pass
class Migration(migrations.Migration):
dependencies = [
("users", "0004_major_university_models"),
]
operations = [
migrations.RunPython(seed_reference_models, noop),
]

View File

@@ -0,0 +1,19 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("users", "0005_populate_major_university"),
]
operations = [
migrations.RemoveField(
model_name="user",
name="legacy_major",
),
migrations.RemoveField(
model_name="user",
name="legacy_university",
),
]

View File

112
backend/users/models.py Normal file
View File

@@ -0,0 +1,112 @@
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
from django.db import models
import uuid
from datetime import timedelta
from utils.models import BaseModel
class University(BaseModel):
code = models.CharField(max_length=64, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
class Major(BaseModel):
code = models.CharField(max_length=64, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
class User(AbstractUser, BaseModel):
email = models.EmailField(unique=True)
bio = models.TextField(null=True, blank=True)
profile_picture = models.ImageField(upload_to='profile_pictures/', null=True, blank=True)
student_id = models.CharField(max_length=20, null=True)
year_of_study = models.IntegerField(null=True, blank=True)
major = models.ForeignKey(
Major,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='users',
)
university = models.ForeignKey(
University,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='users',
)
is_email_verified = models.BooleanField(default=False)
email_verification_token = models.UUIDField(default=uuid.uuid4, unique=True)
email_verification_sent_at = models.DateTimeField(null=True, blank=True)
password_reset_token = models.UUIDField(null=True, blank=True, unique=True)
password_reset_token_expires_at = models.DateTimeField(null=True, blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
class Meta:
db_table = 'users'
verbose_name = 'User'
verbose_name_plural = 'Users'
def __str__(self):
return f"{self.get_full_name()} ({self.email})"
def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip()
def get_major_display(self):
if self.major:
return self.major.name
return None
def get_university_display(self):
if self.university:
return self.university.name
return None
def regenerate_verification_token(self):
self.email_verification_token = uuid.uuid4()
self.save(update_fields=['email_verification_token'])
def set_password_reset_token(self):
"""Generates a new password reset token and sets its expiry."""
self.password_reset_token = uuid.uuid4()
self.password_reset_token_expires_at = timezone.now() + timedelta(hours=1)
self.save(update_fields=['password_reset_token', 'password_reset_token_expires_at'])
def save(self, *args, **kwargs):
send_verified_success = False
if self.pk is not None:
prev = type(self).objects.filter(pk=self.pk).values_list('is_email_verified', flat=True).first()
if prev is not None and prev is False and self.is_email_verified is True:
send_verified_success = True
super().save(*args, **kwargs)
if send_verified_success:
try:
from users.tasks import send_email_verified_success
send_email_verified_success.delay(self.id)
except Exception:
pass

View File

@@ -0,0 +1,29 @@
from import_export import resources, fields
from import_export.widgets import BooleanWidget
from users.models import User
class UserResource(resources.ModelResource):
is_staff = fields.Field(
column_name='is_staff',
attribute='is_staff',
widget=BooleanWidget()
)
is_superuser = fields.Field(
column_name='is_superuser',
attribute='is_superuser',
widget=BooleanWidget()
)
is_email_verified = fields.Field(
column_name='is_email_verified',
attribute='is_email_verified',
widget=BooleanWidget()
)
class Meta:
model = User
fields = ('id', 'username', 'email', 'first_name', 'last_name',
'student_id', 'year_of_study', 'major',
'is_staff', 'is_superuser',
'is_email_verified', 'bio')
export_order = fields

27
backend/users/signals.py Normal file
View File

@@ -0,0 +1,27 @@
import uuid
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils import timezone
from django.conf import settings
from users.models import User
from users.tasks import send_verification_email
@receiver(post_save, sender=User)
def send_verification_email_on_registration(sender, instance, created, **kwargs):
if created:
if not instance.username:
instance.username = str(uuid.uuid4())[:10]
instance.save(update_fields=['username'])
if not instance.is_email_verified and instance.email:
# Update the email verification sent timestamp
instance.email_verification_sent_at = timezone.now()
instance.save(update_fields=['email_verification_sent_at'])
# Generate verification URL (you'll need to adjust this based on your frontend)
verification_url = f"{settings.FRONTEND_ROOT}verify-email/{instance.email_verification_token}"
# Send verification email asynchronously
send_verification_email.delay(instance.id, verification_url)

99
backend/users/tasks.py Normal file
View File

@@ -0,0 +1,99 @@
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.conf import settings
from django.utils.html import strip_tags
from celery import shared_task
import logging
from users.models import User
logger = logging.getLogger(__name__)
@shared_task(bind=True, max_retries=3)
def send_verification_email(self, user_id, verification_url):
try:
user = User.objects.get(id=user_id)
subject = 'تایید ایمیل | انجمن علمی مهندسی کامپیوتر'
html_message = render_to_string('emails/verification_email.html', {
'user': user,
'verification_url': verification_url,
})
plain_message = strip_tags(html_message)
send_mail(
subject=subject,
message=plain_message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[user.email],
html_message=html_message,
fail_silently=False,
)
logger.info(f"Verification email sent to {user.email}")
return f"Verification email sent to {user.email}"
except Exception as exc:
logger.error(f"Failed to send verification email: {exc}")
raise self.retry(exc=exc, countdown=60)
@shared_task(bind=True, max_retries=3)
def send_password_reset_email(self, user_id, reset_url):
try:
user = User.objects.get(id=user_id)
subject = 'بازیابی رمز عبور | انجمن علمی مهندسی کامپیوتر'
html_message = render_to_string('emails/password_reset_email.html', {
'user': user,
'reset_url': reset_url,
})
plain_message = strip_tags(html_message)
send_mail(
subject=subject,
message=plain_message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[user.email],
html_message=html_message,
fail_silently=False,
)
logger.info(f"Password reset email sent to {user.email}")
return f"Password reset email sent to {user.email}"
except Exception as exc:
logger.error(f"Failed to send password reset email: {exc}")
raise self.retry(exc=exc, countdown=60)
@shared_task(bind=True, max_retries=3)
def send_email_verified_success(self, user_id: int):
"""
ارسال ایمیل «ایمیل شما با موفقیت تأیید شد» پس از تغییر وضعیت تأیید.
"""
try:
user = User.objects.get(pk=user_id)
subject = "تأیید ایمیل شما با موفقیت انجام شد"
context = {
"user": user,
"home_url": getattr(settings, "FRONTEND_ROOT", "/"),
}
html_message = render_to_string("emails/verification_success.html", context)
plain_message = strip_tags(html_message)
send_mail(
subject=subject,
message=plain_message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[user.email],
html_message=html_message,
fail_silently=False,
)
logger.info(f"verified success email sent to {user.email}")
return f"verified success email sent to {user.email}"
except Exception as exc:
logger.error(f"Failed to send verified success email: {exc}")
raise self.retry(exc=exc, countdown=60)