Skip to content
This repository has been archived by the owner on Jun 7, 2023. It is now read-only.

Commit

Permalink
Merge b32ec51 into a688e15
Browse files Browse the repository at this point in the history
  • Loading branch information
bnmnetp committed Aug 29, 2019
2 parents a688e15 + b32ec51 commit ba4675a
Show file tree
Hide file tree
Showing 49 changed files with 8,587 additions and 5,081 deletions.
2,038 changes: 1,290 additions & 748 deletions controllers/admin.py

Large diffs are not rendered by default.

1,045 changes: 716 additions & 329 deletions controllers/ajax.py

Large diffs are not rendered by default.

643 changes: 394 additions & 249 deletions controllers/appadmin.py

Large diffs are not rendered by default.

1,269 changes: 823 additions & 446 deletions controllers/assignments.py

Large diffs are not rendered by default.

233 changes: 146 additions & 87 deletions controllers/books.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,37 +48,55 @@ def _route_book(is_published=True):

# See `caching selects <http://web2py.com/books/default/chapter/29/06/the-database-abstraction-layer#Caching-selects>`_.
cache_kwargs = dict(cache=(cache.ram, 3600), cacheable=True)
allow_pairs = 'false'
allow_pairs = "false"
# Find the course to access.
if auth.user:
# Given a logged-in user, use ``auth.user.course_id``.
course = db(db.courses.id == auth.user.course_id).select(
db.courses.course_name,
db.courses.base_course,
db.courses.allow_pairs,
**cache_kwargs).first()
course = (
db(db.courses.id == auth.user.course_id)
.select(
db.courses.course_name,
db.courses.base_course,
db.courses.allow_pairs,
**cache_kwargs
)
.first()
)

# Ensure the base course in the URL agrees with the base course in ``course``. If not, ask the user to select a course.
if not course or course.base_course != base_course:
session.flash = "{} is not the course your are currently in, switch to or add it to go there".format(base_course)
redirect(URL(c='default', f='courses'))
session.flash = "{} is not the course your are currently in, switch to or add it to go there".format(
base_course
)
redirect(URL(c="default", f="courses"))

allow_pairs = 'true' if course.allow_pairs else 'false'
allow_pairs = "true" if course.allow_pairs else "false"

# Ensure the user has access to this book.
if is_published and not db(
(db.user_courses.user_id == auth.user.id) &
(db.user_courses.course_id == auth.user.course_id)
).select(db.user_courses.id, **cache_kwargs).first():
if (
is_published
and not db(
(db.user_courses.user_id == auth.user.id)
& (db.user_courses.course_id == auth.user.course_id)
)
.select(db.user_courses.id, **cache_kwargs)
.first()
):
session.flash = "Sorry you are not registered for this course. You can view most Open courses if you log out"
redirect(URL(c='default', f='courses'))
redirect(URL(c="default", f="courses"))

else:
# Get the base course from the URL.
course = db(db.courses.course_name == base_course).select(
db.courses.course_name, db.courses.base_course,
db.courses.login_required, **cache_kwargs
).first()
course = (
db(db.courses.course_name == base_course)
.select(
db.courses.course_name,
db.courses.base_course,
db.courses.login_required,
**cache_kwargs
)
.first()
)

if not course:
# This course doesn't exist.
Expand All @@ -96,17 +114,24 @@ def dummy():
assert False

# Make this an absolute path.
book_path = safe_join(os.path.join(request.folder, 'books', base_course,
'published' if is_published else 'build', base_course),
*request.args[1:])
book_path = safe_join(
os.path.join(
request.folder,
"books",
base_course,
"published" if is_published else "build",
base_course,
),
*request.args[1:]
)
if not book_path:
logger.error("No Safe Path for {}".format(request.args[1:]))
raise HTTP(404)

# See if this is static content. By default, the Sphinx static directory names are ``_static`` and ``_images``.
if request.args(1) in ['_static', '_images']:
if request.args(1) in ["_static", "_images"]:
# See the `response <http://web2py.com/books/default/chapter/29/04/the-core#response>`_. Warning: this is slow. Configure a production server to serve this statically.
return response.stream(book_path, 2**20, request=request)
return response.stream(book_path, 2 ** 20, request=request)

# It's HTML -- use the file as a template.
#
Expand All @@ -121,77 +146,105 @@ def dummy():
if auth.user:
user_id = auth.user.username
email = auth.user.email
is_logged_in = 'true'
is_logged_in = "true"
# Get the necessary information to update subchapter progress on the page
page_divids = db((db.questions.subchapter == subchapter) &
(db.questions.chapter == chapter) &
(db.questions.from_source == True) &
(db.questions.base_course == base_course)).select(db.questions.name)
div_counts = {q.name:0 for q in page_divids}
sid_counts = db((db.questions.subchapter == subchapter) &
(db.questions.chapter == chapter) &
(db.questions.base_course == base_course) &
(db.questions.from_source == True) &
(db.questions.name == db.useinfo.div_id) &
(db.useinfo.course_id == auth.user.course_name) &
(db.useinfo.sid == auth.user.username)).select(db.useinfo.div_id, distinct=True)
page_divids = db(
(db.questions.subchapter == subchapter)
& (db.questions.chapter == chapter)
& (db.questions.from_source == True)
& (db.questions.base_course == base_course)
).select(db.questions.name)
div_counts = {q.name: 0 for q in page_divids}
sid_counts = db(
(db.questions.subchapter == subchapter)
& (db.questions.chapter == chapter)
& (db.questions.base_course == base_course)
& (db.questions.from_source == True)
& (db.questions.name == db.useinfo.div_id)
& (db.useinfo.course_id == auth.user.course_name)
& (db.useinfo.sid == auth.user.username)
).select(db.useinfo.div_id, distinct=True)
for row in sid_counts:
div_counts[row.div_id] = 1
else:
user_id = 'Anonymous'
email = ''
is_logged_in = 'false'
user_id = "Anonymous"
email = ""
is_logged_in = "false"

if session.readings:
reading_list = session.readings
else:
reading_list = 'null'
reading_list = "null"

# TODO: - Add log entry for page view
try:
db.useinfo.insert(sid=user_id, act='view', div_id=book_path,
event='page', timestamp=datetime.datetime.utcnow(),
course_id=course.course_name)
db.useinfo.insert(
sid=user_id,
act="view",
div_id=book_path,
event="page",
timestamp=datetime.datetime.utcnow(),
course_id=course.course_name,
)
except:
logger.debug('failed to insert log record for {} in {} : {} {} {}'.format(user_id, course.course_name, book_path, 'page', 'view'))

user_is_instructor = 'true' if auth.user and verifyInstructorStatus(auth.user.course_name, auth.user) else 'false'

return dict(course_name=course.course_name,
base_course=base_course,
is_logged_in=is_logged_in,
user_id=user_id,
user_email=email,
is_instructor=user_is_instructor,
allow_pairs=allow_pairs,
readings=XML(reading_list),
activity_info=json.dumps(div_counts),
subchapter_list=_subchaptoc(base_course, chapter))
logger.debug(
"failed to insert log record for {} in {} : {} {} {}".format(
user_id, course.course_name, book_path, "page", "view"
)
)

user_is_instructor = (
"true"
if auth.user and verifyInstructorStatus(auth.user.course_name, auth.user)
else "false"
)

return dict(
course_name=course.course_name,
base_course=base_course,
is_logged_in=is_logged_in,
user_id=user_id,
user_email=email,
is_instructor=user_is_instructor,
allow_pairs=allow_pairs,
readings=XML(reading_list),
activity_info=json.dumps(div_counts),
subchapter_list=_subchaptoc(base_course, chapter),
)


def _subchaptoc(course, chap):
res = db( (db.chapters.id == db.sub_chapters.chapter_id) &
(db.chapters.course_id == course ) &
(db.chapters.chapter_label == chap) ).select(db.chapters.chapter_num,
db.sub_chapters.sub_chapter_num,
db.chapters.chapter_label,
db.sub_chapters.sub_chapter_label,
db.sub_chapters.sub_chapter_name, orderby=db.sub_chapters.sub_chapter_num,
cache=(cache.ram, 3600), cacheable=True)
res = db(
(db.chapters.id == db.sub_chapters.chapter_id)
& (db.chapters.course_id == course)
& (db.chapters.chapter_label == chap)
).select(
db.chapters.chapter_num,
db.sub_chapters.sub_chapter_num,
db.chapters.chapter_label,
db.sub_chapters.sub_chapter_label,
db.sub_chapters.sub_chapter_name,
orderby=db.sub_chapters.sub_chapter_num,
cache=(cache.ram, 3600),
cacheable=True,
)
toclist = []
for row in res:
sc_url = "{}.html".format(row.sub_chapters.sub_chapter_label)
title = "{}.{} {}".format(row.chapters.chapter_num,
row.sub_chapters.sub_chapter_num,
row.sub_chapters.sub_chapter_name)
title = "{}.{} {}".format(
row.chapters.chapter_num,
row.sub_chapters.sub_chapter_num,
row.sub_chapters.sub_chapter_name,
)
toclist.append(dict(subchap_uri=sc_url, title=title))

return toclist


# This is copied verbatim from https://github.com/pallets/werkzeug/blob/master/werkzeug/security.py#L30.
_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
if sep not in (None, '/'))
_os_alt_seps = list(
sep for sep in [os.path.sep, os.path.altsep] if sep not in (None, "/")
)


# This is copied verbatim from https://github.com/pallets/werkzeug/blob/master/werkzeug/security.py#L216.
Expand All @@ -203,14 +256,12 @@ def safe_join(directory, *pathnames):
"""
parts = [directory]
for filename in pathnames:
if filename != '':
if filename != "":
filename = posixpath.normpath(filename)
for sep in _os_alt_seps:
if sep in filename:
return None
if os.path.isabs(filename) or \
filename == '..' or \
filename.startswith('../'):
if os.path.isabs(filename) or filename == ".." or filename.startswith("../"):
return None
parts.append(filename)
return posixpath.join(*parts)
Expand All @@ -219,7 +270,10 @@ def safe_join(directory, *pathnames):
# Endpoints
# =========
# This serves pages directly from the book's build directory. Therefore, restrict access.
@auth.requires(lambda: verifyInstructorStatus(auth.user.course_name, auth.user), requires_login=True)
@auth.requires(
lambda: verifyInstructorStatus(auth.user.course_name, auth.user),
requires_login=True,
)
def draft():
return _route_book(False)

Expand All @@ -230,6 +284,7 @@ def published():
return index()
return _route_book()


def index():
"""
Called by default (and by published if no args)
Expand All @@ -238,28 +293,32 @@ def index():
"""

book_list = os.listdir('applications/{}/books'.format(request.application))
book_list = [book for book in book_list if '.git' not in book]
book_list = os.listdir("applications/{}/books".format(request.application))
book_list = [book for book in book_list if ".git" not in book]

res = []
for book in sorted(book_list):
try:
# WARNING: This imports from ``applications.<runestone application name>.books.<book name>``. Since ``runestone/books/<book_name>`` lacks an ``__init__.py``, it will be treated as a `namespace package <https://www.python.org/dev/peps/pep-0420/>`_. Therefore, odd things will happen if there are other modules named ``applications.<runestone application name>.books.<book name>`` in the Python path.
config = importlib.import_module('applications.{}.books.{}.conf'.format(request.application, book))
config = importlib.import_module(
"applications.{}.books.{}.conf".format(request.application, book)
)
except:
continue
book_info = {}
if hasattr(config, 'navbar_title'):
book_info['title'] = config.navbar_title
elif hasattr(config, 'html_title'):
book_info['title'] = config.html_title
elif hasattr(config, 'html_short_title'):
book_info['title'] = config.html_short_title
if hasattr(config, "navbar_title"):
book_info["title"] = config.navbar_title
elif hasattr(config, "html_title"):
book_info["title"] = config.html_title
elif hasattr(config, "html_short_title"):
book_info["title"] = config.html_short_title
else:
book_info['title'] = 'Runestone Book'
book_info["title"] = "Runestone Book"

book_info['url'] = '/{}/books/published/{}/index.html'.format(request.application, book)
book_info['regname'] = book
book_info["url"] = "/{}/books/published/{}/index.html".format(
request.application, book
)
book_info["regname"] = book

res.append(book_info)

Expand Down

0 comments on commit ba4675a

Please sign in to comment.