Commit 56c04ca5b8947b83c0aab8c094da3dd60b6ba9c3

Authored by Andrew Buss
1 parent 05ddc98b92
Exists in master

Extended UserManager to fix createsuperuser

Showing 3 changed files with 8 additions and 5 deletions Inline Diff

flashcards/admin.py View file @ 56c04ca
from django.contrib import admin 1 1 from django.contrib import admin
from flashcards.models import Flashcard, UserFlashcard, Section, FlashcardMask, \ 2 2 from flashcards.models import Flashcard, UserFlashcard, Section, FlashcardMask, \
UserFlashcardReview, LecturePeriod, User 3 3 UserFlashcardReview, LecturePeriod, User
from simple_email_confirmation import EmailAddress 4
5 4
admin.site.register([ 6 5 admin.site.register([
User, 7 6 User,
Flashcard, 8 7 Flashcard,
FlashcardMask, 9 8 FlashcardMask,
UserFlashcard, 10 9 UserFlashcard,
UserFlashcardReview, 11 10 UserFlashcardReview,
Section, 12 11 Section,
LecturePeriod 13 12 LecturePeriod
flashcards/models.py View file @ 56c04ca
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, AbstractUser 1 1 from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, AbstractUser, UserManager
from django.db.models import * 2 2 from django.db.models import *
from simple_email_confirmation import SimpleEmailConfirmationUserMixin 3 3 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
4 4
# Hack to fix AbstractUser before subclassing it 5 5 # Hack to fix AbstractUser before subclassing it
AbstractUser._meta.get_field('email')._unique = True 6 6 AbstractUser._meta.get_field('email')._unique = True
7 7
8 8
class User(AbstractUser, SimpleEmailConfirmationUserMixin, ): 9 9 class UserManager(UserManager):
10 def create_superuser(self, email, password, **extra_fields):
11 return self._create_user(email, email, password, True, True, **extra_fields)
12
13
14 class User(AbstractUser, SimpleEmailConfirmationUserMixin):
15 objects = UserManager()
USERNAME_FIELD = 'email' 10 16 USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] 11 17 REQUIRED_FIELDS = []
sections = ManyToManyField('Section') 12 18 sections = ManyToManyField('Section')
13 19
14 20
class UserFlashcard(Model): 15 21 class UserFlashcard(Model):
""" 16 22 """
Represents the relationship between a user and a flashcard by: 17 23 Represents the relationship between a user and a flashcard by:
1. A user has a flashcard in their deck 18 24 1. A user has a flashcard in their deck
2. A user used to have a flashcard in their deck 19 25 2. A user used to have a flashcard in their deck
3. A user has a flashcard hidden from them 20 26 3. A user has a flashcard hidden from them
""" 21 27 """
user = ForeignKey('User') 22 28 user = ForeignKey('User')
mask = ForeignKey('FlashcardMask', help_text="A mask which overrides the card's mask") 23 29 mask = ForeignKey('FlashcardMask', help_text="A mask which overrides the card's mask")
pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card") 24 30 pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card")
flashcard = ForeignKey('Flashcard') 25 31 flashcard = ForeignKey('Flashcard')
unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card") 26 32 unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card")
27 33
class Meta: 28 34 class Meta:
# There can be at most one UserFlashcard for each User and Flashcard 29 35 # There can be at most one UserFlashcard for each User and Flashcard
unique_together = (('user', 'flashcard'),) 30 36 unique_together = (('user', 'flashcard'),)
index_together = ["user", "flashcard"] 31 37 index_together = ["user", "flashcard"]
# By default, order by most recently pulled 32 38 # By default, order by most recently pulled
ordering = ['-pulled'] 33 39 ordering = ['-pulled']
34 40
def is_hidden(self): 35 41 def is_hidden(self):
""" 36 42 """
A card is hidden only if a user has not ever added it to their deck. 37 43 A card is hidden only if a user has not ever added it to their deck.
:return: Whether the flashcard is hidden from the user 38 44 :return: Whether the flashcard is hidden from the user
""" 39 45 """
return not self.pulled 40 46 return not self.pulled
41 47
def is_in_deck(self): 42 48 def is_in_deck(self):
""" 43 49 """
:return:Whether the flashcard is in the user's deck 44 50 :return:Whether the flashcard is in the user's deck
""" 45 51 """
return self.pulled and not self.unpulled 46 52 return self.pulled and not self.unpulled
47 53
48 54
class FlashcardMask(Model): 49 55 class FlashcardMask(Model):
""" 50 56 """
A serialized list of character ranges that can be blanked out during review. 51 57 A serialized list of character ranges that can be blanked out during review.
This is encoded as '13-145,150-195' 52 58 This is encoded as '13-145,150-195'
""" 53 59 """
ranges = CharField(max_length=255) 54 60 ranges = CharField(max_length=255)
55 61
56 62
class Flashcard(Model): 57 63 class Flashcard(Model):
text = CharField(max_length=255, help_text='The text on the card') 58 64 text = CharField(max_length=255, help_text='The text on the card')
section = ForeignKey('Section', help_text='The section with which the card is associated') 59 65 section = ForeignKey('Section', help_text='The section with which the card is associated')
pushed = DateTimeField(auto_now_add=True, help_text="When the card was first pushed") 60 66 pushed = DateTimeField(auto_now_add=True, help_text="When the card was first pushed")
material_date = DateTimeField(help_text="The date with which the card is associated") 61 67 material_date = DateTimeField(help_text="The date with which the card is associated")
previous = ForeignKey('Flashcard', null=True, blank=True, 62 68 previous = ForeignKey('Flashcard', null=True, blank=True,
help_text="The previous version of this card, if one exists") 63 69 help_text="The previous version of this card, if one exists")
author = ForeignKey(User) 64 70 author = ForeignKey(User)
is_hidden = BooleanField(default=False) 65 71 is_hidden = BooleanField(default=False)
hide_reason = CharField(blank=True, max_length=255, help_text="Reason for hiding this card") 66 72 hide_reason = CharField(blank=True, max_length=255, help_text="Reason for hiding this card")
mask = ForeignKey(FlashcardMask, blank=True, null=True, help_text="The default mask for this card") 67 73 mask = ForeignKey(FlashcardMask, blank=True, null=True, help_text="The default mask for this card")
68 74
class Meta: 69 75 class Meta:
# By default, order by most recently pushed 70 76 # By default, order by most recently pushed
ordering = ['-pushed'] 71 77 ordering = ['-pushed']
72 78
def is_hidden_from(self, user): 73 79 def is_hidden_from(self, user):
""" 74 80 """
A card can be hidden globally, but if a user has the card in their deck, 75 81 A card can be hidden globally, but if a user has the card in their deck,
this visibility overrides a global hide. 76 82 this visibility overrides a global hide.
:param user: 77 83 :param user:
:return: Whether the card is hidden from the user. 78 84 :return: Whether the card is hidden from the user.
""" 79 85 """
result = user.userflashcard_set.filter(flashcard=self) 80 86 result = user.userflashcard_set.filter(flashcard=self)
if not result.exists(): return self.is_hidden 81 87 if not result.exists(): return self.is_hidden
return result[0].is_hidden() 82 88 return result[0].is_hidden()
83 89
84 90
@classmethod 85 91 @classmethod
def cards_visible_to(cls, user): 86 92 def cards_visible_to(cls, user):
""" 87 93 """
:param user: 88 94 :param user:
:return: A queryset with all cards that should be visible to a user. 89 95 :return: A queryset with all cards that should be visible to a user.
""" 90 96 """
return cls.objects.filter(hidden=False).exclude(userflashcard=user, userflashcard__pulled=None) 91 97 return cls.objects.filter(hidden=False).exclude(userflashcard=user, userflashcard__pulled=None)
92 98
93 99
class UserFlashcardReview(Model): 94 100 class UserFlashcardReview(Model):
""" 95 101 """
An event of a user reviewing a flashcard. 96 102 An event of a user reviewing a flashcard.
""" 97 103 """
user_flashcard = ForeignKey(UserFlashcard) 98 104 user_flashcard = ForeignKey(UserFlashcard)
when = DateTimeField() 99 105 when = DateTimeField()
blanked_word = CharField(max_length=8, blank=True, help_text="The character range which was blanked") 100 106 blanked_word = CharField(max_length=8, blank=True, help_text="The character range which was blanked")
response = CharField(max_length=255, blank=True, null=True, help_text="The user's response") 101 107 response = CharField(max_length=255, blank=True, null=True, help_text="The user's response")
correct = NullBooleanField(help_text="The user's self-evaluation of their response") 102 108 correct = NullBooleanField(help_text="The user's self-evaluation of their response")
103 109
def status(self): 104 110 def status(self):
""" 105 111 """
There are three stages of a review object: 106 112 There are three stages of a review object:
1. the user has been shown the card 107 113 1. the user has been shown the card
2. the user has answered the card 108 114 2. the user has answered the card
3. the user has self-evaluated their response's correctness 109 115 3. the user has self-evaluated their response's correctness
110 116
:return: string (evaluated, answered, viewed) 111 117 :return: string (evaluated, answered, viewed)
""" 112 118 """
if self.correct is not None: return "evaluated" 113 119 if self.correct is not None: return "evaluated"
if self.response: return "answered" 114 120 if self.response: return "answered"
return "viewed" 115 121 return "viewed"
116 122
117 123
class Section(Model): 118 124 class Section(Model):
""" 119 125 """
A UCSD course taught by an instructor during a quarter. 120 126 A UCSD course taught by an instructor during a quarter.
Different sections taught by the same instructor in the same quarter are considered identical. 121 127 Different sections taught by the same instructor in the same quarter are considered identical.
We use the term "section" to avoid collision with the builtin keyword "class" 122 128 We use the term "section" to avoid collision with the builtin keyword "class"
""" 123 129 """
department = CharField(max_length=50) 124 130 department = CharField(max_length=50)
course_num = CharField(max_length=6) 125 131 course_num = CharField(max_length=6)
# section_id = CharField(max_length=10) 126 132 # section_id = CharField(max_length=10)
course_title = CharField(max_length=50) 127 133 course_title = CharField(max_length=50)
instructor = CharField(max_length=50) 128 134 instructor = CharField(max_length=50)
quarter = CharField(max_length=4) 129 135 quarter = CharField(max_length=4)
whitelist = ManyToManyField(User, related_name="whitelisted_sections") 130 136 whitelist = ManyToManyField(User, related_name="whitelisted_sections")
131 137
class Meta: 132 138 class Meta:
flashy/settings.py View file @ 56c04ca
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) 1 1 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os 2 2 import os
3 3
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 4 4 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5 5
# SECURITY WARNING: don't run with debug turned on in production! 6 6 # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True 7 7 DEBUG = True
8 8
ALLOWED_HOSTS = [] 9 9 ALLOWED_HOSTS = []
10 10
AUTH_USER_MODEL = 'flashcards.User' 11 11 AUTH_USER_MODEL = 'flashcards.User'
12 12
INSTALLED_APPS = ( 13 13 INSTALLED_APPS = (
'simple_email_confirmation', 14 14 'simple_email_confirmation',
'flashcards', 15 15 'flashcards',
'django.contrib.admin', 16 16 'django.contrib.admin',
'django.contrib.admindocs', 17 17 'django.contrib.admindocs',
'django.contrib.auth', 18 18 'django.contrib.auth',
'django.contrib.contenttypes', 19 19 'django.contrib.contenttypes',
'django.contrib.sessions', 20 20 'django.contrib.sessions',
'django.contrib.messages', 21 21 'django.contrib.messages',
'django.contrib.staticfiles', 22 22 'django.contrib.staticfiles',
'django_ses', 23 23 'django_ses',
'rest_framework', 24 24 'rest_framework',
25
26
) 27 25 )
28 26
REST_FRAMEWORK = { 29 27 REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination', 30 28 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 20 31 29 'PAGE_SIZE': 20
} 32 30 }
33 31
MIDDLEWARE_CLASSES = ( 34 32 MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 35 33 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 36 34 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 37 35 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 38 36 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 39 37 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 40 38 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 41 39 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware', 42 40 'django.middleware.security.SecurityMiddleware',
) 43 41 )
44 42
ROOT_URLCONF = 'flashy.urls' 45 43 ROOT_URLCONF = 'flashy.urls'
46 44
AUTHENTICATION_BACKENDS = ( 47 45 AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend', 48 46 'django.contrib.auth.backends.ModelBackend',
) 49 47 )
50 48
TEMPLATES = [ 51 49 TEMPLATES = [
{ 52 50 {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 53 51 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates/'], 54 52 'DIRS': ['templates/'],
'APP_DIRS': True, 55 53 'APP_DIRS': True,
'OPTIONS': { 56 54 'OPTIONS': {
'context_processors': [ 57 55 'context_processors': [
'django.template.context_processors.debug', 58 56 'django.template.context_processors.debug',
'django.template.context_processors.request', 59 57 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 60 58 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 61 59 'django.contrib.messages.context_processors.messages',
], 62 60 ],
}, 63 61 },
}, 64 62 },
] 65 63 ]
66 64
WSGI_APPLICATION = 'flashy.wsgi.application' 67 65 WSGI_APPLICATION = 'flashy.wsgi.application'
68 66
DATABASES = { 69 67 DATABASES = {
'default': { 70 68 'default': {
'ENGINE': 'django.db.backends.sqlite3', 71 69 'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 72 70 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
} 73 71 }
} 74 72 }
75 73
LANGUAGE_CODE = 'en-us' 76 74 LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Los_Angeles' 77 75 TIME_ZONE = 'America/Los_Angeles'
USE_I18N = True 78 76 USE_I18N = True