Commit 4915771315021267e1d9175143272633f5c9302e

Authored by Andrew Buss
1 parent cbcaace7a1
Exists in master

Class -> section. Set up API for sections, for real this time

Showing 9 changed files with 116 additions and 27 deletions Inline Diff

flashcards/admin.py View file @ 4915771
from django.contrib import admin 1 1 from django.contrib import admin
from flashcards.models import Flashcard, UserFlashcard, Class, FlashcardMask, \ 2 2 from flashcards.models import Flashcard, UserFlashcard, Section, FlashcardMask, \
UserFlashcardReview 3 3 UserFlashcardReview
4 4
admin.site.register([ 5 5 admin.site.register([
Flashcard, 6 6 Flashcard,
FlashcardMask, 7 7 FlashcardMask,
UserFlashcard, 8 8 UserFlashcard,
UserFlashcardReview, 9 9 UserFlashcardReview,
Class 10 10 Section
]) 11 11 ])
flashcards/migrations/0004_auto_20150429_0827.py View file @ 4915771
File was created 1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3
4 from django.db import models, migrations
5
6
7 class Migration(migrations.Migration):
8
9 dependencies = [
10 ('flashcards', '0003_auto_20150429_0344'),
11 ]
12
13 operations = [
14 migrations.CreateModel(
15 name='LecturePeriod',
16 fields=[
17 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
18 ('week_day', models.IntegerField(help_text=b'0-indexed day of week, starting at Monday')),
19 ('start_time', models.TimeField()),
20 ('end_time', models.TimeField()),
21 ],
22 ),
23 migrations.RenameModel(
24 old_name='Class',
25 new_name='Section',
26 ),
27 migrations.RemoveField(
28 model_name='flashcard',
29 name='associated_class',
30 ),
31 migrations.AddField(
32 model_name='flashcard',
33 name='section',
34 field=models.ForeignKey(default=None, to='flashcards.Section', help_text=b'The section with which the card is associated'),
35 preserve_default=False,
36 ),
37 migrations.AddField(
flashcards/models.py View file @ 4915771
from django.contrib.auth.models import User 1 1 from django.contrib.auth.models import User
from django.db.models import * 2 2 from django.db.models import *
3 3
4 4
class UserFlashcard(Model): 5 5 class UserFlashcard(Model):
""" 6 6 """
Represents the relationship between a user and a flashcard by: 7 7 Represents the relationship between a user and a flashcard by:
1. A user has a flashcard in their deck 8 8 1. A user has a flashcard in their deck
2. A user used to have a flashcard in their deck 9 9 2. A user used to have a flashcard in their deck
3. A user has a flashcard hidden from them 10 10 3. A user has a flashcard hidden from them
""" 11 11 """
user = ForeignKey(User) 12 12 user = ForeignKey(User)
mask = ForeignKey('FlashcardMask', help_text="A mask which overrides the card's mask") 13 13 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") 14 14 pulled = DateTimeField(blank=True, null=True, help_text="When the user pulled the card")
flashcard = ForeignKey('Flashcard') 15 15 flashcard = ForeignKey('Flashcard')
unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card") 16 16 unpulled = DateTimeField(blank=True, null=True, help_text="When the user unpulled this card")
17 17
class Meta: 18 18 class Meta:
# There can be at most one UserFlashcard for each User and Flashcard 19 19 # There can be at most one UserFlashcard for each User and Flashcard
unique_together = (('user', 'flashcard'),) 20 20 unique_together = (('user', 'flashcard'),)
index_together = ["user", "flashcard"] 21 21 index_together = ["user", "flashcard"]
# By default, order by most recently pulled 22 22 # By default, order by most recently pulled
ordering = ['-pulled'] 23 23 ordering = ['-pulled']
24 24
def is_hidden(self): 25 25 def is_hidden(self):
""" 26 26 """
A card is hidden only if a user has not ever added it to their deck. 27 27 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 28 28 :return: Whether the flashcard is hidden from the user
""" 29 29 """
return not self.pulled 30 30 return not self.pulled
31 31
def is_in_deck(self): 32 32 def is_in_deck(self):
""" 33 33 """
:return:Whether the flashcard is in the user's deck 34 34 :return:Whether the flashcard is in the user's deck
""" 35 35 """
return self.pulled and not self.unpulled 36 36 return self.pulled and not self.unpulled
37 37
38 38
class FlashcardMask(Model): 39 39 class FlashcardMask(Model):
""" 40 40 """
A serialized list of character ranges that can be blanked out during review. 41 41 A serialized list of character ranges that can be blanked out during review.
This is encoded as '13-145,150-195' 42 42 This is encoded as '13-145,150-195'
""" 43 43 """
ranges = CharField(max_length=255) 44 44 ranges = CharField(max_length=255)
45 45
46 46
class Flashcard(Model): 47 47 class Flashcard(Model):
text = CharField(max_length=255, help_text='The text on the card') 48 48 text = CharField(max_length=255, help_text='The text on the card')
associated_class = ForeignKey('Class', help_text='The class with which the card is associated') 49 49 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") 50 50 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") 51 51 material_date = DateTimeField(help_text="The date with which the card is associated")
previous = ForeignKey('Flashcard', null=True, blank=True, 52 52 previous = ForeignKey('Flashcard', null=True, blank=True,
help_text="The previous version of this card, if one exists") 53 53 help_text="The previous version of this card, if one exists")
author = ForeignKey(User) 54 54 author = ForeignKey(User)
is_hidden = BooleanField(default=False) 55 55 is_hidden = BooleanField(default=False)
hide_reason = CharField(blank=True, max_length=255, help_text="Reason for hiding this card") 56 56 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") 57 57 mask = ForeignKey(FlashcardMask, blank=True, null=True, help_text="The default mask for this card")
58 58
class Meta: 59 59 class Meta:
# By default, order by most recently pushed 60 60 # By default, order by most recently pushed
ordering = ['-pushed'] 61 61 ordering = ['-pushed']
62 62
def is_hidden_from(self, user): 63 63 def is_hidden_from(self, user):
""" 64 64 """
A card can be hidden globally, but if a user has the card in their deck, 65 65 A card can be hidden globally, but if a user has the card in their deck,
this visibility overrides a global hide. 66 66 this visibility overrides a global hide.
:param user: 67 67 :param user:
:return: Whether the card is hidden from the user. 68 68 :return: Whether the card is hidden from the user.
""" 69 69 """
result = user.userflashcard_set.filter(flashcard=self) 70 70 result = user.userflashcard_set.filter(flashcard=self)
if not result.exists(): return self.is_hidden 71 71 if not result.exists(): return self.is_hidden
return result[0].is_hidden() 72 72 return result[0].is_hidden()
73 73
74 74
@classmethod 75 75 @classmethod
def cards_visible_to(cls, user): 76 76 def cards_visible_to(cls, user):
""" 77 77 """
:param user: 78 78 :param user:
:return: A queryset with all cards that should be visible to a user. 79 79 :return: A queryset with all cards that should be visible to a user.
""" 80 80 """
return cls.objects.filter(hidden=False).exclude(userflashcard=user, userflashcard__pulled=None) 81 81 return cls.objects.filter(hidden=False).exclude(userflashcard=user, userflashcard__pulled=None)
82 82
83 83
class UserFlashcardReview(Model): 84 84 class UserFlashcardReview(Model):
""" 85 85 """
An event of a user reviewing a flashcard. 86 86 An event of a user reviewing a flashcard.
""" 87 87 """
user_flashcard = ForeignKey(UserFlashcard) 88 88 user_flashcard = ForeignKey(UserFlashcard)
when = DateTimeField() 89 89 when = DateTimeField()
blanked_word = CharField(max_length=8, blank=True, help_text="The character range which was blanked") 90 90 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") 91 91 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") 92 92 correct = NullBooleanField(help_text="The user's self-evaluation of their response")
93 93
def status(self): 94 94 def status(self):
""" 95 95 """
There are three stages of a review object: 96 96 There are three stages of a review object:
1. the user has been shown the card 97 97 1. the user has been shown the card
2. the user has answered the card 98 98 2. the user has answered the card
3. the user has self-evaluated their response's correctness 99 99 3. the user has self-evaluated their response's correctness
100 100
:return: string (evaluated, answered, viewed) 101 101 :return: string (evaluated, answered, viewed)
""" 102 102 """
if self.correct is not None: return "evaluated" 103 103 if self.correct is not None: return "evaluated"
if self.response: return "answered" 104 104 if self.response: return "answered"
return "viewed" 105 105 return "viewed"
106 106
107 107 class Section(Model):
class Class(Model): 108
""" 109 108 """
A UCSD course taught by an instructor during a quarter. 110 109 A UCSD course taught by an instructor during a quarter.
Different sections taught by the same instructor in the same quarter are considered identical.s 111 110 Different sections taught by the same instructor in the same quarter are considered identical.
111 We use the term "section" to avoid collision with the builtin keyword "class"
""" 112 112 """
department = CharField(max_length=50) 113 113 department = CharField(max_length=50)
course_num = IntegerField() 114 114 course_num = IntegerField()
name = CharField(max_length=50) 115 115 name = CharField(max_length=50)
flashcards/serializers.py View file @ 4915771
__author__ = 'andrew' 1 1 from flashcards.models import Section, LecturePeriod
2 from rest_framework.relations import HyperlinkedRelatedField
3 from rest_framework.serializers import HyperlinkedModelSerializer
4
5
6 class SectionSerializer(HyperlinkedModelSerializer):
7 lectureperiod_set = HyperlinkedRelatedField(many=True, view_name='lectureperiod-detail', read_only=True)
8 class Meta:
9 model = Section
10 exclude = ('members',)
11
12
13 class LecturePeriodSerializer(HyperlinkedModelSerializer):
14 class Meta:
15 model = LecturePeriod
2
flashcards/views.py View file @ 4915771
from django.shortcuts import render 1 1 from flashcards.models import Section, LecturePeriod
2 from flashcards.serializers import SectionSerializer, LecturePeriodSerializer
3 from rest_framework.permissions import IsAuthenticatedOrReadOnly
4 from rest_framework.viewsets import ModelViewSet
2 5
# Create your views here. 3 6
7 class SectionViewSet(ModelViewSet):
8 queryset = Section.objects.all()
9 serializer_class = SectionSerializer
10 permission_classes = (IsAuthenticatedOrReadOnly,)
11
12 class LecturePeriodViewSet(ModelViewSet):
13 queryset = LecturePeriod.objects.all()
14 serializer_class = LecturePeriodSerializer
15 permission_classes = (IsAuthenticatedOrReadOnly,)
4
flashy/settings.py View file @ 4915771
""" 1 1 """
Django settings for flashy project. 2 2 Django settings for flashy project.
3 3
Generated by 'django-admin startproject' using Django 1.8. 4 4 Generated by 'django-admin startproject' using Django 1.8.
5 5
For more information on this file, see 6 6 For more information on this file, see
https://docs.djangoproject.com/en/1.8/topics/settings/ 7 7 https://docs.djangoproject.com/en/1.8/topics/settings/
8 8
For the full list of settings and their values, see 9 9 For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.8/ref/settings/ 10 10 https://docs.djangoproject.com/en/1.8/ref/settings/
""" 11 11 """
12 12
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) 13 13 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os 14 14 import os
15 15
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 16 16 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 17
18 18
# Quick-start development settings - unsuitable for production 19 19 # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ 20 20 # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
21 21
# SECURITY WARNING: keep the secret key used in production secret! 22 22 # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '8)#-j&8dghe-y&v4a%r6u#y@r86&6nfv%k$(a((r-zh$m3ct!9' 23 23 SECRET_KEY = '8)#-j&8dghe-y&v4a%r6u#y@r86&6nfv%k$(a((r-zh$m3ct!9'
24 24
# SECURITY WARNING: don't run with debug turned on in production! 25 25 # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True 26 26 DEBUG = True
27 27
ALLOWED_HOSTS = [] 28 28 ALLOWED_HOSTS = []
29 29
30 30
# Application definition 31 31 # Application definition
32 32
INSTALLED_APPS = ( 33 33 INSTALLED_APPS = (
'django.contrib.admin', 34 34 'django.contrib.admin',
'django.contrib.admindocs', 35 35 'django.contrib.admindocs',
'django.contrib.auth', 36 36 'django.contrib.auth',
'django.contrib.contenttypes', 37 37 'django.contrib.contenttypes',
'django.contrib.sessions', 38 38 'django.contrib.sessions',
'django.contrib.messages', 39 39 'django.contrib.messages',
'django.contrib.staticfiles', 40 40 'django.contrib.staticfiles',
'rest_framework', 41 41 'rest_framework',
'flashcards', 42 42 'flashcards',
) 43 43 )
44 44
MIDDLEWARE_CLASSES = ( 45 45 MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware', 46 46 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 47 47 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 48 48 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 49 49 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 50 50 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 51 51 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 52 52 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware', 53 53 'django.middleware.security.SecurityMiddleware',
) 54 54 )
55 55
ROOT_URLCONF = 'flashy.urls' 56 56 ROOT_URLCONF = 'flashy.urls'
57 57
TEMPLATES = [ 58 58 TEMPLATES = [
{ 59 59 {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 60 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], 61 61 'DIRS': ['templates/'],
'APP_DIRS': True, 62 62 'APP_DIRS': True,
'OPTIONS': { 63 63 'OPTIONS': {
'context_processors': [ 64 64 'context_processors': [
'django.template.context_processors.debug', 65 65 'django.template.context_processors.debug',
'django.template.context_processors.request', 66 66 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 67 67 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 68 68 'django.contrib.messages.context_processors.messages',
], 69 69 ],
}, 70 70 },
}, 71 71 },
] 72 72 ]
73 73
WSGI_APPLICATION = 'flashy.wsgi.application' 74 74 WSGI_APPLICATION = 'flashy.wsgi.application'
75 75
76 76
flashy/urls.py View file @ 4915771
from django.conf.urls import include, url 1 1 from django.conf.urls import include, url
from django.contrib import admin 2 2 from django.contrib import admin
3 from flashcards.views import SectionViewSet, LecturePeriodViewSet
4 from rest_framework.routers import DefaultRouter
3 5
6 router = DefaultRouter()
7 router.register(r'sections', SectionViewSet)
8 router.register(r'lectureperiods', LecturePeriodViewSet)
9
urlpatterns = [ 4 10 urlpatterns = [
11 url(r'^api/', include(router.urls)),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 5 12 url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)), 6 13 url(r'^admin/', include(admin.site.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) 7 14 url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
schedule-scraper/scrape_schedule.js View file @ 4915771
var page = require('webpage').create(); 1 1 var page = require('webpage').create();
var fs = require('fs'); 2 2 var fs = require('fs');
page.open('file:///home/andrew/Downloads/Student Class Info.html', function(status) { 3 3 page.open('Student Class Info.html', function (status) {
if (status !== 'success') { 4 4 if (status !== 'success') {
console.log('load failed'); 5 5 console.log('load failed');
} else { 6 6 } else {
console.log("loaded"); 7 7 console.log("loaded");
page.injectJs("jquery.min.js"); 8 8 page.injectJs("jquery.min.js");
console.log("got jquery"); 9 9 console.log("got jquery");
page.includeJs("http://www.kunalbabre.com/projects/table2CSV.js", function() { 10 10 page.includeJs("http://www.kunalbabre.com/projects/table2CSV.js", function () {
console.log("got script"); 11 11 console.log("got script");
page.evaluate(function() { 12 12 page.evaluate(function () {
console.log("parsing"); 13 13 console.log("parsing");
try { 14 14 try {
fs.write("schedule.csv", $('table.tbrdr').table2CSV({'delivery':'value'}), 'w'); 15 15 fs.write("schedule.csv", $('table.tbrdr').table2CSV({'delivery': 'value'}), 'w');
} catch(e) { 16 16 } catch (e) {
console.log(e); 17 17 console.log(e);
} 18 18 }
}); 19 19 });
}); 20 20 });
} 21 21 }
phantom.exit(); 22 22 phantom.exit();
}); 23 23 });
24 24
templates/rest_framework/api.html View file @ 4915771
File was created 1 {% extends "rest_framework/base.html" %}
2 {% block bootstrap_theme %}
3 <link rel="stylesheet" href="https://bootswatch.com/darkly/bootstrap.css" type="text/css">
4 {% endblock %}