Skip to content

Commit 49d509a

Browse files
committed
Refactor code into modules.
1 parent 6c003a6 commit 49d509a

16 files changed

+132
-164
lines changed

README.md

+18-14
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,21 @@ A+ is a Django 1.7+ and Python 3 application which has been run in production us
1717
Testing environment
1818
-------------------
1919

20-
See [doc/README.md](doc/README.md) on how to create and run a test environment for development.
21-
22-
Structure
23-
---------
24-
25-
Included Django applications
26-
* `course` : The course instances
27-
* `exercise` : Learning modules and exercises for the course instances
28-
* `userprofile` : Additional user information and groups
29-
* `django_shibboleth` : Handles users for Apache Shibboleth headers
30-
* `notification` : User messaging framework
31-
* `inheritance` : Utilities for model class hierarchy
32-
* `external_services` : Linking to external services, optionally LTI authenticated
33-
* `apps` Provides plugins (tabs disabled) that integrate additional parts to main content
20+
See [doc/](doc) on how to create and run a test environment for development.
21+
22+
The [selenium_test/](selenium_test) offers an integration test suite run with Selenium Firefox driver.
23+
24+
Code Organization
25+
-----------------
26+
27+
[a-plus/](a-plus) : Django main settings
28+
[course/](course) : The course instances
29+
[exercise/](exercise) : Learning modules and exercises for the course instances
30+
[userprofile/](userprofile) : Additional user information and groups
31+
[django_shibboleth/](django_shibboleth) : Handles users for Apache Shibboleth request headers
32+
[notification/](notification) : User messaging framework
33+
[inheritance/](inheritance) : Utilities for model class hierarchy
34+
[external_services/](external_services) : Linking to external services, optionally LTI authenticated
35+
[apps/](apps) : Provides plugins that can integrate additional content to course instances
36+
[api/](api) : An HTTP service API for accessing A+ data
37+
[lib/](lib) : More general libraries

a-plus/__init__.py

Whitespace-only changes.

settings.py a-plus/settings.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
88
import os
99

10-
BASE_DIR = os.path.dirname(__file__)
10+
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
1111

1212

1313
# Quick-start development settings - unsuitable for production
@@ -83,7 +83,7 @@
8383
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
8484
)
8585

86-
ROOT_URLCONF = 'urls'
86+
ROOT_URLCONF = 'a-plus.urls'
8787

8888
LOGIN_REDIRECT_URL = "/"
8989

urls.py a-plus/urls.py

+10-12
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,32 @@
1-
# A+
2-
from views import login, home, privacy
3-
4-
# Django
1+
from django.conf import settings
52
from django.conf.urls import patterns, url, include
63
from django.contrib import admin
7-
from django.conf import settings
4+
from django.views.generic import TemplateView
5+
86

97
admin.autodiscover()
108

119
urlpatterns = patterns('',
1210

1311
# A+
14-
(r'^$', home),
15-
url(r'^privacy-policy(?:.html)?$', privacy, name="privacy_policy"),
16-
12+
url(r'^privacy-policy$', TemplateView.as_view(template_name='aaltoplus/privacy.html'),
13+
name="privacy_policy"),
14+
(r'^$', 'course.views.home'),
1715
(r'^exercise/', include('exercise.urls')),
1816
(r'^course/', include('course.urls')),
19-
(r'^api/', include('api_urls')),
17+
(r'^api/', include('api.urls')),
2018
(r'^userprofile/', include('userprofile.urls')),
2119
(r'^apps/', include('apps.urls')),
2220
(r'^external/', include('external_services.urls')),
2321

2422
# Shibboleth
2523
(r'^shibboleth/', include('django_shibboleth.urls')),
2624

27-
# Django:
28-
(r'^admin/', include(admin.site.urls)),
29-
(r'^accounts/login/$', login),
25+
# Django
26+
(r'^accounts/login/$', 'userprofile.views.login'),
3027
url(r'^accounts/logout/$', "django.contrib.auth.views.logout",
3128
{ "template_name": "aaltoplus/logout.html" }, name="logout"),
29+
(r'^admin/', include(admin.site.urls)),
3230
)
3331

3432
if settings.DEBUG:

a-plus/wsgi.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
WSGI config for A+ project.
3+
4+
It exposes the WSGI callable as a module-level variable named ``application``.
5+
6+
For more information on this file, see
7+
https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
8+
"""
9+
10+
import os
11+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "a-plus.settings")
12+
13+
from django.core.wsgi import get_wsgi_application
14+
application = get_wsgi_application()

api/__init__.py

Whitespace-only changes.
File renamed without changes.

api_urls.py api/urls.py

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
api.register(LearningObjectResource())
1818
api.register(CourseInstanceSummaryResource())
1919

20-
2120
urlpatterns = patterns('',
2221
(r'^', include(api.urls)),
2322
)

course/api.py

+8-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
# Django
21
from django.conf.urls import url
3-
from django.core.urlresolvers import reverse
42
from django.contrib.auth.models import User
3+
from django.core.urlresolvers import reverse
54

6-
# Tastypie
7-
from tastypie.resources import ModelResource, Resource
8-
from tastypie.authentication import Authentication #, OAuthAuthentication
9-
from tastypie.authorization import DjangoAuthorization, ReadOnlyAuthorization
10-
from tastypie import fields
11-
from tastypie.bundle import Bundle
12-
13-
# A+
145
from course.models import Course, CourseInstance
156
from exercise.exercise_summary import UserCourseSummary
7+
from tastypie import fields
8+
from tastypie.authentication import Authentication # , OAuthAuthentication
9+
from tastypie.authorization import DjangoAuthorization, ReadOnlyAuthorization
10+
from tastypie.bundle import Bundle
11+
from tastypie.resources import ModelResource, Resource
1612
from userprofile.models import UserProfile
1713

14+
15+
# A+
1816
class CourseResource(ModelResource):
1917
instances = fields.ToManyField('course.api.CourseInstanceResource', 'instances')
2018

course/views.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
from collections import defaultdict
2-
import datetime
32
from django.contrib import messages
43
from django.contrib.auth.decorators import login_required
54
from django.http import HttpResponse, HttpResponseForbidden, \
65
HttpResponseRedirect
76
from django.shortcuts import get_object_or_404, render_to_response
87
from django.template import loader
8+
from django.template.context import RequestContext
9+
from django.utils.datetime_safe import datetime
910
from django.utils.translation import ugettext_lazy as _
1011

1112
from apps.app_renderers import build_plugin_renderers
1213
from course.context import CourseContext
1314
from course.forms import CourseModuleForm
14-
from course.models import Course, CourseInstance
15+
from course.models import Course, CourseInstance, get_visible_open_course_instances
1516
from course.results import ResultTable
1617
from exercise.exercise_models import CourseModule, BaseExercise, LearningObjectCategory
1718
from exercise.exercise_summary import UserCourseSummary
1819
from icalendar import Calendar, Event
1920

2021

22+
def home(request):
23+
if request.user.is_authenticated():
24+
instances = get_visible_open_course_instances(request.user.userprofile)
25+
else:
26+
instances = get_visible_open_course_instances()
27+
context = RequestContext(request, {"instances": instances})
28+
return render_to_response("aaltoplus/home.html", context)
29+
30+
2131
# TODO: The string constant "You are not allowed to access this view." is
2232
# repeated a lot in this file. Giving this error message should be somehow
2333
# unified.

exercise/api.py

+38-43
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,76 @@
1-
# Python
21
from base64 import b64encode
32

4-
# Tastypie
5-
from tastypie.resources import ModelResource, Resource, ALL
6-
from api_permissions import *
7-
from tastypie.authentication import ApiKeyAuthentication #,OAuthAuthentication
8-
from tastypie.authorization import DjangoAuthorization, ReadOnlyAuthorization
3+
from api.permissions import SuperuserAuthorization
94
from tastypie import fields
5+
from tastypie.authentication import Authentication, ApiKeyAuthentication
6+
from tastypie.authorization import DjangoAuthorization, ReadOnlyAuthorization
7+
from tastypie.resources import ModelResource, Resource, ALL
8+
9+
from .exercise_models import LearningObject, BaseExercise, CourseModule
10+
from .submission_models import Submission
1011

11-
# A+
12-
from userprofile.models import UserProfile
13-
from .exercise_models import LearningObject, BaseExercise, CourseModule, CourseInstance
14-
from .submission_models import Submission, SubmittedFile
15-
from api_permissions import SuperuserAuthorization
1612

1713
class LearningObjectResource(ModelResource):
1814
class Meta:
19-
queryset = LearningObject.objects.all()
20-
resource_name = 'learning_object'
21-
excludes = []
15+
queryset = LearningObject.objects.all()
16+
resource_name = 'learning_object'
17+
excludes = []
2218

2319
# In the first version GET (read only) requests are
2420
# allowed and no authentication is required
2521
allowed_methods = ['get']
26-
authentication = Authentication()
27-
authorization = ReadOnlyAuthorization()
22+
authentication = Authentication()
23+
authorization = ReadOnlyAuthorization()
2824

2925

3026
class ExerciseResource(ModelResource):
31-
submissions = fields.ToManyField('exercise.api.SubmissionResource', 'submissions')
27+
submissions = fields.ToManyField('exercise.api.SubmissionResource', 'submissions')
3228

3329
class Meta:
34-
queryset = BaseExercise.objects.all()
35-
resource_name = 'exercise'
36-
excludes = []
30+
queryset = BaseExercise.objects.all()
31+
resource_name = 'exercise'
32+
excludes = []
3733

3834
# In the first version GET (read only) requests are
3935
# allowed and no authentication is required
4036
allowed_methods = ['get']
41-
authentication = Authentication()
42-
authorization = ReadOnlyAuthorization()
37+
authentication = Authentication()
38+
authorization = ReadOnlyAuthorization()
4339

4440
class CourseModuleResource(ModelResource):
45-
learning_objects = fields.ToManyField('exercise.api.LearningObjectResource', 'learning_objects')
41+
learning_objects = fields.ToManyField('exercise.api.LearningObjectResource', 'learning_objects')
4642

4743
class Meta:
48-
queryset = CourseModule.objects.all()
49-
resource_name = 'coursemodule'
50-
excludes = []
44+
queryset = CourseModule.objects.all()
45+
resource_name = 'coursemodule'
46+
excludes = []
5147

5248
# In the first version GET (read only) requests are
5349
# allowed and no authentication is required
5450
allowed_methods = ['get']
55-
authentication = Authentication()
56-
authorization = ReadOnlyAuthorization()
51+
authentication = Authentication()
52+
authorization = ReadOnlyAuthorization()
5753

5854
class SubmissionResource(ModelResource):
59-
exercise = fields.ToOneField('exercise.api.ExerciseResource', 'exercise')
60-
grader = fields.ToOneField('userprofile.api.UserProfileResource', 'grader', null=True, blank=True)
61-
submitters = fields.ToManyField('userprofile.api.UserProfileResource', 'submitters', null=True, blank=True)
55+
exercise = fields.ToOneField('exercise.api.ExerciseResource', 'exercise')
56+
grader = fields.ToOneField('userprofile.api.UserProfileResource', 'grader', null=True, blank=True)
57+
submitters = fields.ToManyField('userprofile.api.UserProfileResource', 'submitters', null=True, blank=True)
6258

6359
def dehydrate(self, bundle):
6460
"""
6561
This method iterates over the URLs of the files submitted with each
6662
submission and adds them in the file_urls lists of submissions.
6763
"""
68-
file_urls = []
64+
file_urls = []
6965
for file in bundle.obj.files.all():
7066
file_urls.append(file.get_absolute_url())
7167
bundle.data.update({"files": file_urls})
7268
return bundle
7369

7470
class Meta:
75-
queryset = Submission.objects.all()
76-
resource_name = 'submission'
77-
excludes = ['feedback']
71+
queryset = Submission.objects.all()
72+
resource_name = 'submission'
73+
excludes = ['feedback']
7874
allowed_methods = ['get']
7975
include_absolute_url = True
8076

@@ -88,9 +84,8 @@ class Meta:
8884
}
8985

9086
# In this version only superusers are allowed to access
91-
# submissions after being authenticated with OAuth
92-
# authentication = OAuthAuthentication()
93-
authorization = SuperuserAuthorization()
87+
# submissions.
88+
authorization = SuperuserAuthorization()
9489

9590
class SubmissionContentResource(ModelResource):
9691
"""
@@ -122,9 +117,9 @@ def dehydrate(self, bundle):
122117
return bundle
123118

124119
class Meta:
125-
queryset = Submission.objects.all()
126-
resource_name = 'submission_content'
127-
excludes = ['feedback']
120+
queryset = Submission.objects.all()
121+
resource_name = 'submission_content'
122+
excludes = ['feedback']
128123
allowed_methods = ['get']
129124
include_absolute_url = True
130125

@@ -137,5 +132,5 @@ class Meta:
137132
"id": ALL
138133
}
139134

140-
authentication = ApiKeyAuthentication()
141-
authorization = ReadOnlyAuthorization()
135+
authentication = ApiKeyAuthentication()
136+
authorization = ReadOnlyAuthorization()

manage.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44

55
if __name__ == "__main__":
6-
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
6+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "a-plus.settings")
77

88
from django.core.management import execute_from_command_line
99

userprofile/api.py

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
# Tastypie
1+
from api.permissions import SuperuserAuthorization
22
from tastypie.resources import ModelResource
3-
#from tastypie.authentication import OAuthAuthentication #TODO FIX
4-
5-
# A+
63
from userprofile.models import UserProfile
7-
from api_permissions import SuperuserAuthorization #TODO FIX
84

95

106
class UserProfileResource(ModelResource):
@@ -29,5 +25,4 @@ class Meta:
2925
# In this version of the API only superusers are allowed to access
3026
# userprofile objects
3127
allowed_methods = ['get']
32-
#authentication = OAuthAuthentication() #TODO fix
3328
authorization = SuperuserAuthorization()

0 commit comments

Comments
 (0)