# Path: apps/core/models.py
# SERVICE_TEST = 'TEST'
# 
# class Service(models.Model):
#     """
#     denotes single sso client
#     - name:               unique name
#     - is_shown:           decides to show in SSO main page
#     - alias:              user friendly name
#     - scope:              scope; TEST / SPARCS / PUBLIC
#     - main_url:           main page url
#     - login_callback_url: login callback url
#     - unregister_url:     unregister page url
#     - secret_key:         secret key
#     - admin_user:         admin of this client
#     - cooltime:           cooltime (days) to re-register; < 60
#     - icon:               icon of this service
#     """
# 
#     name = models.CharField(max_length=40, primary_key=True)
#     is_shown = models.BooleanField(default=True)
#     alias = models.CharField(max_length=30)
#     scope = models.CharField(max_length=6, choices=SERVICE_SCOPE,
#                              default=SERVICE_TEST)
#     main_url = models.CharField(max_length=200)
#     login_callback_url = models.CharField(max_length=200)
#     unregister_url = models.CharField(max_length=200, null=True, blank=True)
#     secret_key = models.CharField(max_length=100)
#     admin_user = models.ForeignKey(User, on_delete=models.CASCADE,
#                                    related_name='managed_services')
#     cooltime = models.IntegerField()
#     icon = models.ImageField(null=True, blank=True)
# 
#     @property
#     def icon_url(self):
#         return self.icon.url if self.icon else '/static/img/test-service.png'
# 
#     def __str__(self):
#         return self.alias
# 
# class UserProfile(models.Model):
#     """
#     denotes additional information of single user
#     - user:            user object
#     - gender:          gender; *H / *M / *F / *E or others
#     - birthday:        birthday
#     - point:           point for public services
#     - point_test:      point for test services
#     - email_new:       new email before auth
#     - email_authed:    email authed state
#     - test_only:       indicate test only account
#     - test_enabled:    test mode state
#     - facebook_id:     facebook unique id
#     - twitter_id:      twitter unique id
#     - kaist_id:        kaist uid
#     - kaist_info:      additional kaist info
#     - kaist_info_time: kaist info updated time
#     - sparcs_id:       sparcs id iff sparcs member
#     - expire_time:     expire time for permanent deletion
#     """
#     user = models.OneToOneField(User, on_delete=models.CASCADE,
#                                 related_name='profile')
#     gender = models.CharField(max_length=30)
#     birthday = models.DateField(blank=True, null=True)
#     point = models.IntegerField(default=0)
#     point_test = models.IntegerField(default=0)
#     email_new = models.EmailField(blank=True, null=True)
#     email_authed = models.BooleanField(default=False)
#     test_only = models.BooleanField(default=False)
#     test_enabled = models.BooleanField(default=False)
#     facebook_id = models.CharField(max_length=50, blank=True, null=True)
#     twitter_id = models.CharField(max_length=50, blank=True, null=True)
#     kaist_id = models.CharField(max_length=50, blank=True, null=True)
#     kaist_info = models.TextField(blank=True, null=True)
#     kaist_info_time = models.DateField(blank=True, null=True)
#     sparcs_id = models.CharField(max_length=50, blank=True, null=True)
#     expire_time = models.DateTimeField(blank=True, null=True)
# 
#     @property
#     def flags(self):
#         return {
#             'test': self.test_enabled,
#             'test-only': self.test_only,
#             'dev': self.user.is_staff or bool(self.sparcs_id),
#             'sparcs': bool(self.sparcs_id),
#             'sysop': self.user.is_staff,
#         }
# 
#     def gender_display(self):
#         if self.gender == '*M':
#             return 'Male'
#         elif self.gender == '*F':
#             return 'Female'
#         elif self.gender == '*H':
#             return 'Hide'
#         elif self.gender == '*E':
#             return 'etc'
#         return self.gender
# 
#     def activate(self):
#         if self.expire_time:
#             self.expire_time = None
#             self.save()
#             return True
#         return False
# 
#     def save_kaist_info(self, info):
#         self.kaist_id = info['userid']
#         self.kaist_info = json.dumps(info['kaist_info'])
#         self.kaist_info_time = timezone.now()
#         self.save()
# 
#     def __str__(self):
#         return f'{self.user}''s profile'


import datetime
from typing import Tuple
from django.contrib.auth.models import User
from django.db.models import Q
from rest_framework.test import APIClient
from apps.core.models import SERVICE_TEST, Service, UserProfile
            'get': self.api_client.get,
            'delete': self.api_client.delete,
        }
        url = f'/{path}/?{querystring}'
        return request_func[method](url, data=data, format='json', **kwargs)


def ensure_user(username: str, name: Tuple[str, str], email: str, is_staff=False, password=None, **kwargs) -> User:
    user_defaults = {
        "username": username,
        "first_name": name[0],
        "last_name": name[1],
        "email": email,
        "is_staff": is_staff,
    }
    user, _ = User.objects.filter(Q(username=username) | Q(email=email)).update_or_create(defaults=user_defaults)
    if password is None:
        user.set_unusable_password()
    else:
        user.set_password(password)
    user.save()
    UserProfile.objects.update_or_create(defaults={
        "gender": "*H",
        "email_authed": True,
        **kwargs,
    }, user=user)
    return user


def ensure_service(name: str, alias: str, admin_user: User, **kwargs) -> Service:

# Path: apps/core/models.py
# SERVICE_TEST = 'TEST'
# 
# class Service(models.Model):
#     """
#     denotes single sso client
#     - name:               unique name
#     - is_shown:           decides to show in SSO main page
#     - alias:              user friendly name
#     - scope:              scope; TEST / SPARCS / PUBLIC
#     - main_url:           main page url
#     - login_callback_url: login callback url
#     - unregister_url:     unregister page url
#     - secret_key:         secret key
#     - admin_user:         admin of this client
#     - cooltime:           cooltime (days) to re-register; < 60
#     - icon:               icon of this service
#     """
# 
#     name = models.CharField(max_length=40, primary_key=True)
#     is_shown = models.BooleanField(default=True)
#     alias = models.CharField(max_length=30)
#     scope = models.CharField(max_length=6, choices=SERVICE_SCOPE,
#                              default=SERVICE_TEST)
#     main_url = models.CharField(max_length=200)
#     login_callback_url = models.CharField(max_length=200)
#     unregister_url = models.CharField(max_length=200, null=True, blank=True)
#     secret_key = models.CharField(max_length=100)
#     admin_user = models.ForeignKey(User, on_delete=models.CASCADE,
#                                    related_name='managed_services')
#     cooltime = models.IntegerField()
#     icon = models.ImageField(null=True, blank=True)
# 
#     @property
#     def icon_url(self):
#         return self.icon.url if self.icon else '/static/img/test-service.png'
# 
#     def __str__(self):
#         return self.alias
# 
# class UserProfile(models.Model):
#     """
#     denotes additional information of single user
#     - user:            user object
#     - gender:          gender; *H / *M / *F / *E or others
#     - birthday:        birthday
#     - point:           point for public services
#     - point_test:      point for test services
#     - email_new:       new email before auth
#     - email_authed:    email authed state
#     - test_only:       indicate test only account
#     - test_enabled:    test mode state
#     - facebook_id:     facebook unique id
#     - twitter_id:      twitter unique id
#     - kaist_id:        kaist uid
#     - kaist_info:      additional kaist info
#     - kaist_info_time: kaist info updated time
#     - sparcs_id:       sparcs id iff sparcs member
#     - expire_time:     expire time for permanent deletion
#     """
#     user = models.OneToOneField(User, on_delete=models.CASCADE,
#                                 related_name='profile')
#     gender = models.CharField(max_length=30)
#     birthday = models.DateField(blank=True, null=True)
#     point = models.IntegerField(default=0)
#     point_test = models.IntegerField(default=0)
#     email_new = models.EmailField(blank=True, null=True)
#     email_authed = models.BooleanField(default=False)
#     test_only = models.BooleanField(default=False)
#     test_enabled = models.BooleanField(default=False)
#     facebook_id = models.CharField(max_length=50, blank=True, null=True)
#     twitter_id = models.CharField(max_length=50, blank=True, null=True)
#     kaist_id = models.CharField(max_length=50, blank=True, null=True)
#     kaist_info = models.TextField(blank=True, null=True)
#     kaist_info_time = models.DateField(blank=True, null=True)
#     sparcs_id = models.CharField(max_length=50, blank=True, null=True)
#     expire_time = models.DateTimeField(blank=True, null=True)
# 
#     @property
#     def flags(self):
#         return {
#             'test': self.test_enabled,
#             'test-only': self.test_only,
#             'dev': self.user.is_staff or bool(self.sparcs_id),
#             'sparcs': bool(self.sparcs_id),
#             'sysop': self.user.is_staff,
#         }
# 
#     def gender_display(self):
#         if self.gender == '*M':
#             return 'Male'
#         elif self.gender == '*F':
#             return 'Female'
#         elif self.gender == '*H':
#             return 'Hide'
#         elif self.gender == '*E':
#             return 'etc'
#         return self.gender
# 
#     def activate(self):
#         if self.expire_time:
#             self.expire_time = None
#             self.save()
#             return True
#         return False
# 
#     def save_kaist_info(self, info):
#         self.kaist_id = info['userid']
#         self.kaist_info = json.dumps(info['kaist_info'])
#         self.kaist_info_time = timezone.now()
#         self.save()
# 
#     def __str__(self):
#         return f'{self.user}''s profile'


# Path: tests/conftest.py
import datetime
from typing import Tuple
from django.contrib.auth.models import User
from django.db.models import Q
from rest_framework.test import APIClient
from apps.core.models import SERVICE_TEST, Service, UserProfile

            'get': self.api_client.get,
            'delete': self.api_client.delete,
        }
        url = f'/{path}/?{querystring}'
        return request_func[method](url, data=data, format='json', **kwargs)


def ensure_user(username: str, name: Tuple[str, str], email: str, is_staff=False, password=None, **kwargs) -> User:
    user_defaults = {
        "username": username,
        "first_name": name[0],
        "last_name": name[1],
        "email": email,
        "is_staff": is_staff,
    }
    user, _ = User.objects.filter(Q(username=username) | Q(email=email)).update_or_create(defaults=user_defaults)
    if password is None:
        user.set_unusable_password()
    else:
        user.set_password(password)
    user.save()
    UserProfile.objects.update_or_create(defaults={
        "gender": "*H",
        "email_authed": True,
        **kwargs,
    }, user=user)
    return user


def ensure_service(name: str, alias: str, admin_user: User, **kwargs) -> Service:

    Service.objects.filter(name=name).delete()