Website Package

This part of the documentation describes the website package. It includes all parts of the website.

Flask Context Objects

Most of the context objects are initialized in railgun.website.context. Context objects are the controllers of the website.

railgun.website.context.app

A Flask object. It implements a WSGI application and acts as the central object of the website.

railgun.website.context.cache

A flask.ext.cache.Cache object. It extends the app to bring in cache facility.

railgun.website.context.csrf

A flask_wtf.csrf.CsrfProtect object. It extends the app so that all post requests will be protected from csrf attack, unless disabled explicitly.

railgun.website.context.db

A flask.ext.sqlalchemy.SQLAlchemy object. It extends the app so that each request will bind a database session.

However, there indeed exists some context objects initialized in other modules. All the global context objects are listed in the following table:

Object Description
railgun.website.context.app The WSGI application that acts as the central object of the website.
railgun.website.context.csrf Protect the website from CSRF attack.
railgun.website.context.db Bind a database session to each reqeust.
railgun.website.admin.bp The blueprint for all admin views.
railgun.website.credential.login_manager Support login & logout to the website.
railgun.website.i18n.babel Support translations of the website.

Per-Request Objects

Per-request objects should be attached to Flask request context object g. The registered objects are listed in the following table:

Object Description
g.homeworks HwSetProxy instance.
g.utcnow A datetime representing current time in UTC timezone.
g.navibar_identity The active navigation identity. Refer to navibar for more details.
g.scripts The dependency libraries of current page. Refer to PageScripts for more details.

Database Models

Database models are defined with SQLAlchemy. You may refer to SQLAlchemy Object Relational Tutorial to have a glance at the usage of this library.

class railgun.website.models.User(**kwargs)[source]

A user represents a registered account in Railgun.

check_password(password)[source]

Validate the plain text password.

Since all users from third-party authentication providers will store None in this attribute, you may call railgun.website.userauth.authenticate() if you just want to validate a user login at a very high-level stage. This method, however, is called mainly by the utilities in userauth.

Parameters:password (str) – The plain text password.
Returns:True if password passes validation, False otherwise.
fill_i18n_from_request()[source]

Fill locale and timezone according to current browser request.

This method only works if it is called in a request context.

gather_scores()[source]

Collect the final scores for each homework belong to this user.

Returns:dict of (“homework uuid” -> “final score”).
set_password(password)[source]

Update the hashed password by given plain text password.

Parameters:password (str) – The plain text password.
email

The user’s email address, maximum 70 characters, unique among users.

Note that some authentication providers may not give the user’s email address. Railgun will create a dummy email address like “[name]@not-a-email.secoder.net” for these users, and guide the user to fill in their actual email address at their first login.

You may modify the suffix of dummy email addresses in config.EXAMPLE_USER_EMAIL_SUFFIX.

family_name

The user’s family name, maximum 64 characters.

given_name

The user’s given name, maximum 64 characters.

id

Primary key of the table

is_active

Is this user active? If not active, the user will not be allowed to sign in the system.

is_admin

Is this user an administrator?

locale

Locale name of this user. It will be set to the browser locale name when a user is created. If browser locale is not known, it will be set to config.DEFAULT_LOCALE.

name

The user’s login name, maximum 50 characters, unique among users.

password

The hashed user password, maximum 255 characters. Users from third-party authentication providers will have None in password field.

provider

Which authentication provider does this user come from?

An authentication provider is a third-party service that validates the login name and password, and provide account information if the user passes validation.

railgun.website.thuauth.TsinghuaAuthProvider is the authentication provider to connect to Tsinghua account. Other providers may connect to csv account files, or ldap account servers.

None or empty indicates that this user does not come from any external provider.

scores

Refer to the final scores of this user on each homework assignment.

timezone

Timezone of this user. It will be set to config.DEFAULT_TIMEZONE when a user is created.

class railgun.website.models.Handin(**kwargs)[source]

A handin stores the information of a submission from user.

get_compile_error()[source]

Safe method to get empty string if compile_error is None, or the translated compile_error.

get_ctime()[source]

Attach UTC timezone object to ctime and return the new datetime object.

get_result()[source]

The brief comment on this submission may be instance of railgun.common.lazy_i18n.GetTextString or None.

If we call unicode(result) to get the translated text, we may have the risk to get the literal of “None”. This is a safe method to get empty string if result is None, or the translated text.

get_state()[source]

The state is stored as plain text in the database. Get the translated version of the state text.

get_stderr()[source]

Safe method to get empty string if stderr is None, or the translated stderr.

get_stdout()[source]

Safe method to get empty string if stdout is None, or the translated stdout.

is_accepted()[source]

Whether this handin has been accepted?

set_ctime(ctime)[source]

Set ctime to the value of ctime, but dettach timezone.

compile_error

The compiler error of this submission.

Actual type should be railgun.common.lazy_i18n.GetTextString, serialized by pickle and stored as byte sequence.

ctime

The creation time of this submission. Value of datetime is in UTC timezone, however, tzinfo is not stored.

exitcode

The program exit code of this submission.

hwid

Link with the associated homework.

id

We use uuid to seek locate submissions, but we maintain an integral id, in that some databases do not support uuids, and most databases handle integral primary keys much faster than string uuids.

lang

The programming language of this submission, maximum 32 characters.

partials

List of scores from each scorer.

Actual type is list of railgun.common.hw.HwPartialScore, serialized by pickle and stored as byte sequence.

result

The brief comment of this submission.

Actual type should be railgun.common.lazy_i18n.GetTextString, serialized by pickle and stored as byte sequence.

scale

The score scale of this submission, determined by ctime and the deadline configurations of the homework.

score

The total score of this submission. Usually the sum of all scorers.

state

The state of this submission. One of the following states:

State Description
Pending The submission is waiting to be executed.
Running The submission is being executed.
Accepted The submission successfully executed.
Rejected Some error occurred executing the submission.
stderr

The program standard error output of this submission.

stdout

The program standard output of this submission.

user

Refer to the associated user object.

user_id

Link with the associated user, usually mapped to a foreign key.

uuid

The uuid of this submission, used almost everywhere in Railgun system.

class railgun.website.models.FinalScore(**kwargs)[source]

A final score stores the highest score among all submissions for a particular homework assignment belong to a given user.

hwid

Link with the associated homework.

Since we do not have a homework table, we just store the uuid of the homework assignment. Maximum 32 characters.

score

The final score value.

user_id

Link with the associated user, usually mapped to a foreign key.

Jinja2 Template Filters

railgun.website.jinja_filters.codelang_filter(s)[source]

Get the translated name for given programming language.

If the given language name could not be understood, return the original language identifier. Here are some examples:

>>> {{ 'python' | codelang }}
'Python'

>>> {{ 'unknown' | codelang }}
'unknown'
Parameters:s (str) – The identifier of programming language.
railgun.website.jinja_filters.cssclass_filter(s)[source]

Replace invalid characters in given text so that it can be used as a css class name.

Parameters:s (str) – The given text.
Returns:The processed css class name.
railgun.website.jinja_filters.duecolor_filter(delta_or_date)[source]

Get a color to describe the emergency of given due date.

If the deadline is within 3 days, the filter will return a color between green and red. The closer the deadline, the redder the color.

Usage:

<span style="color: {{ next_deadline | duecolor }}">...</span>
Parameters:delta_or_date – A datetime or a timedelta object.
Returns:An html color string, for example, “#ffffff”.
railgun.website.jinja_filters.gravatar_filter(user, size=24)[source]

Get the gravatar url of given user. Refer to get_avatar() for more details.

Parameters:
  • user (object or str) – A user object with the email attribute, or an email string.
  • size (int) – The requested avatar size.
Returns:

The gravatar image url.

railgun.website.jinja_filters.handinstyle_filter(state)[source]

Get a bootstrap class name to describe the submission state. The relationships between submission state and bootstrap class name are:

State Class Name
Pending .warning
Running .info
Rejected .danger
Accepted .success
Parameters:state (str) – The submission state.
Returns:The bootstrap class name.
railgun.website.jinja_filters.provider_name_filter(name)[source]

Get the translated name for a given authentication provider.

If requested authentication provider not found, return the original identity name. For example:

>>> {{ 'csvfile' | provider_name }}
'Csv文件'

>>> {{ 'tsinghua' | provider_name }}
'网络学堂'

>>> {{ 'unknown' | provider_name }}
'unknown'
Parameters:name (str) – The identify name of authentication provider.
railgun.website.jinja_filters.roundscore_filter(score)[source]

Get the closest number to given score, whose precision is 0.1.

Parameters:score (float) – The given score in floating number.
Returns:The rounded score.
railgun.website.jinja_filters.scalecolor_filter(scale)[source]

Get a color to describe the emergency of given score scale.

The closer the scale is to 1.0, the greener the color should be. Refer to float_color() for more details.

Usage:

<span style="color: {{ deadline.scale | scalecolor }}">...</span>
Parameters:scale (float) – The value of score scale.
Returns:An html color string, for example, “#ffffff”.
railgun.website.jinja_filters.scorecolor_filter(score)[source]

Get a color to describe the ranking of score.

The closer the score is to 100, the greener the color should be. Refer to float_color() for more details.

Usage:

<span style="color: {{ handin.score | scorecolor }}">...</span>
Parameters:score (float) – The value of score.
Returns:An html color string, for example, “#ffffff”.
railgun.website.jinja_filters.sizeformat_filter(size)[source]

Get a human readable string of the given size in bytes. Refer to format_size() for more details.

Usage:

<span>The attachment size is : {{ attach_size | sizeformat }}</span>
Parameters:size (int) – Given size in bytes.
Returns:A human readable string to represent the size.
railgun.website.jinja_filters.timedelta_filter(delta_or_date)[source]

Format datetime or timedelta into localized string.

If given parameter is a datetime, it will be subtracted by railgun.common.dateutil.utcnow() to get the timedelta.

Usage:

{{ handin.get_ctime() | timedelta }}
Parameters:delta_or_date – A datetime or a timedelta object.
Returns:None if delta_or_date is None, otherwise the localized timedelta string.

Jinja2 Context Functions and Variables

The following table lists all the functions and variables accessible in a jinja context, in addition to the standard Flask configutaion.

Object Name Description
current_user Reference of flask.ext.login.current_user. If current_user.is_authenticated() is True, this object should also be an instance of UserContext.
allow_signup A bool configuration value indicating whether user registration is allowed.
pagelng

Store the language name for current page. You may use this value to localization html elements, for example:

<html lang="{{ pagelng }}">
    ...
</html>
navibar A NavibarProxy object.
renderPartialScore Reference of function renderPartialScore().

Flask View Decorators

It’s a common practice to decorate a view function so that the request is validated, pre-processed or post-processed. For example:

@app.route('/')
@login_required
def index():
    return "Hello, %(name)s" % {'name': current_user.name}

The index view didn’t check the credential of request user. However, being decorated by login_required(), this validation is performed automatically, where un-authorized users will be redirected to the login page.

The following table lists all the decorators available in the website:

Object Description
admin_required() Require the user to be an administrator.
login_required() Require the user to login.
fresh_login_required() Require the user to re-validate their credential if the session is remembered by the browser and is stale.
secret_api() Decrypt the posted data and decode the plain text as json text, and store the loaded object as request.payload.
csrf.exempt() Skip CSRF token validation for POSTed requests.

Flask-Login Integration

class railgun.website.credential.UserContext(user_dbo)[source]

A mixin object to integrate into flask.ext.login framework. It provides the credential information in a request session.

In addition to this, UserContext also acts as a proxy to the associated User model object, in that you may read all the attributes of User just from this class, for example:

>>> current_user.name
'Alice'
>>> current_user.dbo.name
'Alice'

Where the latter is what actually executed.

However, UserContext does not proxy write operations to the model object. So if you wish to update the user’s name, you must assign on current_user.dbo.name instead of current_user.name:

current_user.dbo.name = 'Bob'
get_id()[source]

Get the id of User model object.

Returns:The id of this user.
Return type:unicode
is_active()[source]

Whether this is an active user? Refer to User model for more details about this attribute.

Returns:A bool indicating whether the user is active.
is_anonymous()[source]

Whether this is an anonymous user? Since UserContext will be constructed only for authenticated users, this method will always return False.

Returns:False
is_authenticated()[source]

Is this an authenticated user? Since UserContext will be constructed only for authenticated users, this method will always return True.

However, for users not authenticated, flask.ext.login will create an flask.ext.login.AnonymousUser instance and store in flask.ext.login.current_user. This object will return False when called the same method.

So you may check the return value of current_user.is_authenticated(), to see whether some attributes can be accessed, for example:

{% if current_user.is_authenticated() %}
    {% for score in current_users.scores %}
        <p>The score for {{ score.hwid }} is: {{ score.score }}</p>
    {% endfor %}
{% else %}
    <p>Please login first!</p>
{% endif %}
Returns:True
railgun.website.credential.fresh_login_required(method)[source]

A decorator on Flask view functions that validate whether the visitor is authenticated and the session is not stale.

If not authenticated, the request user will be redirected to signin(). If the session is stale, the request user will be redirected to reauthenticate(). If should_update_email() returns True, the request user will be redirected to profile_edit().

Usage:

@bp.route('/')
@fresh_login_required
def foo():
    return 'This page can only be accessed by fresh users.'
railgun.website.credential.login_required(method)[source]

A decorator on Flask view functions that validate whether the visitor is authenticated.

If not authenticated, the request user will be redirected to signin(). If should_update_email() returns True, the request user will be redirected to profile_edit().

Usage:

@bp.route('/')
@login_required
def foo():
    return 'This page can only be accessed by authenticated users.'
railgun.website.credential.redirect_update_email()[source]

Redirect to profile_edit with a notificatio.

railgun.website.credential.should_update_email()[source]

Check whether the current user has not set his or her email address.

Some authentication providers may not give the email addresses. However, Railgun relies on email addresss to finish login process. So the users from these providers will be given fake email addresses.

should_update_email should be called before a view is executed, check whether the user should be redirected to profile_edit() to fill in his or her email address.

Returns:True if the user has a fake email address, False otherwise.

Note

If the current view is profile_edit(), this method will return False.

railgun.website.credential.login_manager

A flask.ext.login.LoginManager object. It extends the app to support login & logout.

Common Utilities

railgun.website.utility.date_histogram(data, getter, ignore_year=False)[source]

Get the date histogram from given objects.

Parameters:
  • data – Iterable objects to be analyzed.
  • getter – A callable() object to get a datetime from an object.
  • ignore_year – Ignore the year in the date. Only month and day will be used in histogram.
Returns:

A sorted list of tuple ((year, month, day), freq).

railgun.website.utility.float_color(value)[source]

Get a color to describe the safety level.

Safety level should be a floating number in range [0.0, 1.0]. The color will be more close to green if the safety level is more close to 1.0, and more close to red if level is more close to 0.0.

Parameters:value (float) – The value of safety level.
Returns:An html color string, for example, “#ffffff”.
railgun.website.utility.format_size(size)[source]

Get a human readable string of the given size in bytes.

Returns two-digit floating number with the size unit following, unless it is smaller than 1000B, the number part will be an integer.

The conversion between size units is 1G = 1024M = 1048576K = 1073741824B. However, if the size is larger than 10^3 the size of a smaller unit, it will be formatted as the larger unit.

For example:

>>> format_size(10)
'10B'

>>> format_size(1000)
'0.98K'

>>> format_size(1048576)
'1.00M'
Parameters:size (int) – Given size in bytes.
Returns:A human readable string to represent the size.
railgun.website.utility.get_avatar(user_or_email, size)[source]

Get the gravatar url of given user.

Parameters:
  • user_or_email (basestring or object) – If it is a basestring, representing the email address; otherwise it must carry an attribute email.
  • size (int) – Size of avatar in pixels.
Returns:

The url to the gavatar image.

railgun.website.utility.group_histogram(data, getter)[source]

Get the group histogram from given objects.

The frequency of each group will be counted and the histogram will be based on the groups.

Parameters:
  • data – Iterable objects to be analyzed.
  • getter – A callable() object to get the group name from an object.
Returns:

A dict {group: frequency}.

railgun.website.utility.is_email(login)[source]

Check whether the given login name is an email address.

Parameters:login (str) – The login name.
Returns:True if is email, False otherwise.
railgun.website.utility.list_vote_signup()[source]

List all signup data.

railgun.website.utility.load_vote_signup(username=None)[source]

Load voting signup data from directory.

railgun.website.utility.round_score(score)[source]

Get the closest number to given score, whose precision is 0.1.

Parameters:score (float) – The given score in floating number.
Returns:The rounded score.
railgun.website.utility.store_vote_signup(project_id, group_name, description, logo_file, username=None)[source]

Store voting signup data to directory.

Translation Utilities

railgun.website.i18n.get_best_locale_name(locale_names)[source]

Select the best matching locale from locale_names according to current request.

The locales are evaluated according to the language, script and territory. If no best choice is found, fallback to config.DEFAULT_LOCALE. Moreover, if even config.DEFAULT_LOCALE does not appear in locale_names, choose the last item in locale_names.

Usage:

# If current user prefers zh-CN, or zh-TW.
>>> get_best_locale_name(['zh-cn', 'en'])
'zh-cn'

# If current user prefers en-US.
>>> get_best_locale_name(['zh-cn', 'en'])
'en'

# If current user prefers ja-JP, and default locale is zh-CN.
>>> get_best_locale_name(['zh-cn', 'en'])
'zh-cn'

# If current user prefers ja-JP, and default locale is zh-CN.
>>> get_best_locale_name(['fr', 'en'])
'en'
Parameters:locale_names (list) – List of alternative locale names.
Returns:The best matching or fallback locale name.
Return type:str
railgun.website.i18n.list_locales()[source]

Get a list of available babel.core.Locale objects.

Whatever translations the system provides, “en” should always appear in the list. The returned list should be sorted in the order of display names, which may be vary in different languages.

Returns:list of babel.core.Locale objects.
railgun.website.i18n.babel = <flask_babel.Babel object at 0x7f8dbf12a650>

A flask.ext.babel.Babel object. It extends the app to support translations for each request.

Homework Proxies

This module contains the proxies to Homework and HwSet instances.

What is a proxy? A proxy is a container to another object. All the access, including getting or setting the attributes, calling methods, and any other operations, will be redirected from the container to the actual object.

Why proxy? Because we can change the behaviour of a given object without change its code. For example, Homework contains the titles and descriptions for all languages. However, the visitor only wants one version. Rather than accessing the correct resources carefully with an explicit language identity, we wrap the original objects with a simple proxy object, and we may just use hw.title to replace hw.info[lang].title.

class railgun.website.hw.HwProxy(hw)[source]

Per-request proxy to Homework. The best locale for current user will be detected during __init__() method.

Parameters:hw (Homework) – The original Homework object.
attach_size(lang)[source]

Get the attachment file size for given programming language. If the file does not exist, return None.

Parameters:lang (str) – The identity of programming language.
Returns:The attachment file size or None.
attach_url(lang)[source]

Get the attachment url for given programming language. The url should refer to hwpack().

Parameters:lang (str) – The identity of programming language.
Returns:The attachment url address.
is_hidden()[source]

Whether this homework is hidden?

You may put the uuid of hidden homework in config.HIDDEN_HOMEWORKS. If you wish to lock all homework assignments, put a “*” in it.

is_locked()[source]

Whether this homework is locked?

You may put the uuid of locked homework in config.LOCKED_HOMEWORKS. If you wish to lock all homework assignments, put a “*” in it.

class railgun.website.hw.HwSetProxy(hwset)[source]

Per-request proxy to HwSet. You may hide some homework assignments to the visitor, so a proxy to HwSet is necessary.

Parameters:hwset (HwSet) – The original HwSet object.
get_by_slug(slug)[source]

Get the homework with given slug.

Parameters:slug (str) – The homework slug.
Returns:The requested HwProxy or None if not found.
get_by_uuid(uuid)[source]

Get the homework with given uuid.

Parameters:uuid (str) – The homework uuid.
Returns:The requested HwProxy or None if not found.
railgun.website.hw.homeworks

The global HwSet instance which is initialized at website startup.

Navibar Registry

Railgun has a site-wide navigation bar.

Navigation bar is a two-level menu. Each item is related to some certain page. When the user is visiting some page, the corresponding item as well as its parent items should be marked as active.

This module provides the utility to track active navibar items and maintain the navigation bar object for each request. Each navibar item should be given a navibar identity.

The identity is stored in g.navibar_identity. At the start of each request, flask.request.endpoint will be used as the initial value of g.navibar_identity. If you wish to change the identity, you may call set_navibar_identity() anytime before render the template.

class railgun.website.navibar.NaviItem(title, url, identity, adminpage=None, subitems=None)[source]

Store the information of a navibar item.

Many fields in a NaviItem object can be lazy, which means you could give a callable() object as the value of some field, and the actual value will not be calculated until the navibar is being rendered. For example:

admin_menu = NaviItem(
    title=lazy_gettext('Admin Page'),
    url=lambda: url_for('admin.index', user=current_user.name),
    identity='admin_menu',
    adminpage=True,
    subitems=lambda: [
        NaviItem(title=itm.title, url=itm.url, identity=itm.id,
                 adminpage=True)
        for itm in Database.LoadAdminPages()
    ])

The title of this menu bar will be translated, the url will be generated according to the visitor’s username, and the items of subitems will be loaded from database.

Parameters:
  • title (A str, a lazy string or a callable() object.) – The title of this navi item.
  • url (A str or a callable() object.) – The url of this navi item.
  • identity (str) – The identity of this navi item. (static)
  • adminpage (bool) – Whether this navibar item links to an admin page? (static)
  • subitems (list or a callable() object.) – Lists of subitems.
static make_view(title, endpoint, *args, **kwargs)[source]

A shortcut to make a NaviItem linking to a standard Flask view. Equal to the following statement:

NaviItem(
    title=title,
    url=lambda: url_for(endpoint, *args, **kwargs),
    identity=endpoint
)
Parameters:
  • title – The title of new NaviItem
  • endpoint (str) – The endpoint of target view.
  • args – The unnamed arguments to flask.url_for() when generating the url.
  • kwargs – The named arguments to flask.url_for() when generating the url.
adminpage = None

Whether this navibar item links to an admin page? (static)

has_child[source]

Whether this navibar item has subitems?

identity = None

The identity of this navi item. (static)

is_active[source]

Whether this navibar item is active?

This method only compares the identity of itself with g.navibar_identity. If you wish to check the activeness of all subitems, you may put the NaviItem into a NaviItemProxy.

subitems[source]

Perform the lazy calculation and get the subitems.

title[source]

Perform the lazy calculation and get the title.

url[source]

Perform the lazy calculation and get the url.

class railgun.website.navibar.NaviItemProxy(navi)[source]

Per-request proxy to a NaviItem instance.

Once this proxy is created, all the lazy fields of NaviItem object will be calculated and cached, including all its subitems. Moreover, if a subitem is active, then the parent is marked active as well.

Parameters:navi (NaviItem) – The navibar item.
class railgun.website.navibar.Navibar[source]

Manage all the NaviItem instances in a navigation bar.

add(item)[source]

Add a first-level navibar item.

Parameters:item (NaviItem) – A navibar item.
add_view(title, endpoint, *args, **kwargs)[source]

Shortcut to add a view to this navibar.

Parameters:
  • title – The title of this navibar.
  • endpoint (str) – The endpoint of target view.
  • args – The unnamed arguments to flask.url_for() when generating the url.
  • kwargs – The named arguments to flask.url_for() when generating the url.
get_proxies()[source]

Contruct all the NaviItemProxy instances.

Returns:list of NaviItemProxy.
items = None

List of the first-level navibar items.

class railgun.website.navibar.NavibarProxy(navibar)[source]

A proxy to NavibarProxy, that delays the call to Navibar.get_proxies() until the navigation bar is actually accessed. The NaviItemProxy instances are then cached in this class.

Do not access items explicitly! The only way to use NavibarProxy is to iterate over it. For example:

for item in navibar:
    print item.title
items = None

The cached NaviItemProxy instances.

railgun.website.navibar.set_navibar_identity(identity)[source]

Set the identity of nagivation item for current request.

Parameters:identity (str) – The navibar item identity.
railgun.website.navibar.navigates = <railgun.website.navibar.Navibar object at 0x7f8dbf0deb10>

The global Navibar registry.

Script Repository

Modern html pages may rely on Javascript and CSS heavily. To ease the development of dynamic html pages, there comes some powerful libraries and frameworks, along with lots of plugins based on these utilities.

For example, the most widely used Javascript extension library, jQuery, is just a dependency of another widely used CSS framework, Bootstrap. Moreover, the Bootstrap framework is also a necessary dependency for many cool widgets, like bootstrap-tags, bootstrap-timeselector, and so on.

It is guaranteed that the dependencies should be loaded before the libraries who depends on them. Then how to manage the dependency relationship, produce the correct loading scripts, is a real problem.

This module provides to utility to register well-known libraries, to generate dependency graph, and to produce loading scripts.

class railgun.website.scriptlibs.Dependency(requirement)[source]

Represent a dependency.

Parameters:requirement (str or Dependency) –

Dependency requirement literal or object. The literal may be just a simple package name, or a named followed by a version requirement. For example:

  • jQuery
  • bootstrap >= 3.0
check(version)[source]

Check whether the given version meets the version requirement of this dependency. The package name is not compared.

Parameters:version – A Version object.
set(requirement)[source]

Assign the given value to this dependency object.

Parameters:requirement (str) – Dependency requirement literal.
Raises:ValueError if the given requirement is ill-formed.
class railgun.website.scriptlibs.DependencySet(deps=None)[source]

Store a set of dependencies.

Parameters:deps (iterable object over Dependency instances) – Initialize the DependencySet with a set of dependency objects.
checkdep(name, version)[source]

Check whether a package with given version can satisfy this dependency set.

Parameters:
  • name (str) – The name of the package.
  • version (Version) – The version object.
deps(requirement)[source]

Add a dependency to this set.

If the name of package in the requirement already exists, we will replace the old dependency with new one only if the version is higher, or the old one does not specify a version.

Note

This behaviour should be changed! Consider to combine the version requirements, or just store all the dependency objects with the same package name.

Parameters:requirement (str or Dependency.) – A requirement literal or object.
data = None

A dict of {pkgname: Dependency}

class railgun.website.scriptlibs.PageScripts(deps=None)[source]

Gather the script libraries used by current page.

deps(requirement)[source]

Add a dependency to this page.

Parameters:requirement (str or Dependency) – A dependency literal or object.
headScripts()[source]

Get the urls of all script files between <head> and </head>.

Returns:A list of urls.
headStyles()[source]

Get the urls of all style sheets between <head> and </head>.

Returns:A list of urls.
tailScripts()[source]

Get the urls of all script files before </body>.

Returns:A list of urls.
depends = None

The dependency set of this page. Each may be a script name followed by a version requirement.

class railgun.website.scriptlibs.ScriptLib(name, version, deps=None, headStyles=None, headScripts=None, tailScripts=None)[source]

Represent a script library, store its name, version, dependencies, and all its script resources.

Parameters:
  • name (str) – The name of this library.
  • version (str or Version) – A version literal or object.
  • headStyles (list of str) – A list of style sheet urls between <head> and </head>.
  • headScripts (list of str) – A list of script file urls between <head> and </head>.
  • tailScripts (list of str) – A list of script file urls before </body>.
checkdep(script)[source]

Check whether the given script object can be the dependency of this script library.

Parameters:script (ScriptLib) – The script library object.
deps(requirement)[source]

Add a dependency to this script library.

Parameters:requirement (str or Dependency) – A dependency literal or object.
depends = None

The DependencySet object of this library.

headScripts = None

A list of script file urls between <head> and </head>.

headStyles = None

A list of style sheet urls between <head> and </head>.

name = None

The name of the script.

tailScripts = None

A list of script file urls before </body>.

version = None

The Version object of this library.

class railgun.website.scriptlibs.ScriptRepo[source]

The registry of all script libraries.

addScript(script)[source]

Add a script object into repo.

Parameters:script (ScriptLib) – The script library object.
getScript(name)[source]

Get the script object with given name.

Parameters:name (str) – Name of requested script library object.
scripts = None

The dict of {script-name -> ScriptLib}.

class railgun.website.scriptlibs.Version(version)[source]

Represent a dot splitted version, for example, 1.0.0.

Parameters:version (str or Version.) – A version literal, or another Version object.
set(value)[source]

Assign the value to this version.

Parameters:value – A version literal, or another Version object.
railgun.website.scriptlibs.scripts

The global ScriptRepo to register all installed script libraries. In Railgun system, these libraries are registered by default:

  • jquery == 1.11.1
  • chart.js == 1.0.1
  • bootstrap == 3.2.0
  • railgun == 1.0.0
  • handlebars == 1.3.0
  • typeahead.js == 0.10.5

Third-party Authentication

Railgun supports third-party authentication providers.

We cannot store and shouldn’t store the up-to-time password of users from third-party authentication providers. Moreover we even cannot keep other data of a third-party user fresh.

In Railgun, we store neither the plain password nor the hashed ones for third-party users. Instead, we store the provider identity for such users. Each time these users are trying to sign in, we query the upstream provider with the user provided login name and password, and validate the user according to the remote response.

The other data from third-party authentication providers, like the user email address, or other information, will be pulled from the remote server each time we make a third-party authentication. On the other hand, each time a user edits his or her profile, we push the new data to remote server. We keep the user profile synchronized with third-party providers in this way.

class railgun.website.userauth.AuthProvider(name)[source]

The base class for all third-party user authenticate providers.

If a user try to authenticate with login and password which does not exist in main database, the external user authenticate providers will be queried to find such user.

What’s more, when a user is trying to edit his profile, the authenticate provider will also receive certain updates.

External auth provider can only store parts of user data. However, username and password are necessary fields that the provider should store.

Parameters:name (str) – The identity of this authentication provider, which will be used to associate user objects in the database with this instance.
authenticate(user, dbuser, password)[source]

Try to authenticate the given remote user with password. Derived classes should implement this.

Parameters:
  • user – The remote user object (created by pull())
  • dbuser (User) – The database user object.
  • password (str) – The provided password.
Returns:

The database object if authenticated, None otherwise.

Return type:

User or None

display_name()[source]

Get a translated name of this auth provider. Derived classes should implement this.

init_form(form)[source]

Third-party providers may store only parts of the user data. Derived classes should implement this to modify the user profile form.

Parameters:form – The flask_wtf.Form instance.
pull(name=None, email=None, dbuser=None)[source]

Try to get user from remote provider by name or email. Derived classes should implement this.

Return None if requested user does not exist.

If user exists, and if dbuser is None, construct a new database user and save it, filled with user data from the remote.

If dbuser is not None, any mismatch fields in dbuser should be updated according to remote user.

Returns:A tuple of (remote user, database user), or None if the user does not exist on remote server.
push(dbuser, password=None)[source]

Update the user on remote server according to provided dbuser. If password is not None, then the password of remote user should also be updated. Derived classes should implement this.

Parameters:
  • dbuser (User) – The database user object.
  • password (str) – The updated user password.
class railgun.website.userauth.AuthProviderSet[source]

The registry for all AuthProvider objects.

This class manages all third-party authentication providers, and provide convenient methods to authenticate a user with these providers one after another.

However, it does not take database users into account. You may use the top-most function authenticate() to validate a user’s credential.

add(provider)[source]

Add a AuthProvider into this set.

Parameters:provider (AuthProvider) – The authentication provider.
authenticate(**kwargs)[source]

Try to authenticate the user by third-party providers.

Only one of the name and the email should be provided, otherwise the behaviour is undefined.

Parameters:
  • name (str) – Authenticate the user by username.
  • email (str) – Authenticate the user by email.
  • password (str) – The plain user password.
  • dbuser (User) – The database user object.
Returns:

The database object if authenticated, None otherwise.

Return type:

User or None

get(name)[source]

Get a AuthProvider according to its identity.

Parameters:name (str) – The identity of authentication provider.
Returns:Requested AuthProvider instance.
Raises:KeyError if requested provider does not exist.
init_form(provider, form)[source]

Modify the given form according to given authentication provider.

Parameters:
  • provider (str) – The identity of auth provider.
  • form (flask_wtf.Form) – The form to be modified.
init_providers()[source]

Initialize the providers according to config value webconfig.AUTH_PROVIDERS.

pull(name=None, email=None, dbuser=None)[source]

Pull the user from third-party providers.

Only one of the name and the email should be provided to fetch the user, otherwise the behaviour is undefined.

Parameters:
  • name (str) – The name of the requested user.
  • email (str) – The email of the requested user.
  • dbuser (User) – The database user object.
Returns:

A tuple of (remote user, database user), or None if the user does not exist on remote server.

push(dbuser, password=None)[source]

Push the given database user object to its corresponding third-party authentication provider. If password is given, the remote user password will also be updated.

Parameters:
  • dbuser – The database user object.
  • password (str) – The plain user password.
items = None

list of AuthProvider instances.

class railgun.website.userauth.CsvFileAuthProvider(name, path)[source]

CSV file authentication provider.

This is a toy auth provider. You may create your own auth provider on the basis of this one, and you may own the first administrator account by add the following text into config/users.csv:

name,password,email,admin
"admin","pbkdf2:sha1:1000$aWa1MeYA$812c7fe6cfa00060b6e3fe0dfbbe99da98b6d1eb","admin@example.org",True

Where the account name is admin, and the password is admin123. CsvFileAuthProvider is enabled by default, with the following contents in railgun/website/webconfig.py:

AUTH_PROVIDERS = [
    ('railgun.website.userauth.CsvFileAuthProvider', {
        'name': 'csvfile',
        'path': os.path.join(RAILGUN_ROOT, 'config/users.csv'),
    }),
]
Parameters:
  • name (str) – The identity of this authentication provider.
  • path (str) – The path of the csv file.
class railgun.website.userauth.CsvFileUserObject[source]

Schema of CSV user database file.

email = <Field(CsvString)>

The email address string field.

is_admin = <Field(CsvBoolean, name="admin")>

The boolean field that indicates whether this user is an admin.

name = <Field(CsvString)>

The user name string field.

password = <Field(CsvString)>

The hashed password string field.

railgun.website.userauth.authenticate(login, password)[source]

Try to authenticate the user with login and password.

Parameters:
  • login (str) – The username or email, depends on the string value.
  • password (str) – The plain user password.
Returns:

The database object if authenticated, None otherwise.

Return type:

User or None

railgun.website.userauth.has_user(login)[source]

Check whether there exists a user with given login as username or email (depending on login itself) in the database and on remote servers of third-party authentication providers.

Parameters:login (str) – The username or email.
Returns:A bool indicating the existence of user.
railgun.website.userauth.auth_providers

The global AuthProviderSet registry instance.

Tsinghua Authentication

class railgun.website.thuauth.TsinghuaAccount(name)[source]

Represent a Tsinghua remote account.

We can only know the username of Tsinghua accounts. So we just put a faked email into email. Then the new users from Tsinghua will be required to fill in their real emails.

You may refer to should_update_email() to know about this feature.

Parameters:name (str) – The username of this user.
class railgun.website.thuauth.TsinghuaAuthProvider(name, auth_url)[source]

The authentication provider that validates user with Tsinghua info account.

You may activate this auth provider by adding the following contents into config/website.py:

AUTH_PROVIDERS += [(
    'railgun.website.thuauth.TsinghuaAuthProvider', {
        'name': 'tsinghua',
        'auth_url': 'http://student.tsinghua.edu.cn/practiceLogin.do',
    }
)]

WTF Form Models

class railgun.website.forms.AddressHandinForm(*args, **kwargs)[source]

The form for users to give url addresses as submissions.

address

The url address text input. Must be valid url addresses.

class railgun.website.forms.AdminUserEditForm(*args, **kwargs)[source]

Derived from ProfileForm, allow admins to edit a user’s profile. The only additional widget is is_admin. Used in user_edit(), where the_user would be set.

is_admin

Checkbox input representing whether the user is an administrator.

class railgun.website.forms.CreateUserForm(*args, **kwargs)[source]

The basic form to create a new user account. This form is used in adduser() view directly without modification. Moreover, it is also derived by SignupForm as a registration form for new users.

validate_name(form, field)[source]

Extra validation that the username has not been taken.

confirm

The password confirm text input.

name

The username text input. Valid pattern is [A-Za-z0-9_], minimum length is 3, and maximum length is 32.

password

The password text input. Minimum length is 7, and maximum length is 32. It must be equal to confirm.

class railgun.website.forms.CsvHandinForm(*args, **kwargs)[source]

The form for users to give CSV data as submissions.

csvdata

The CSV data text area. Use MultiRowsTextArea as the widget so that rows are defined.

class railgun.website.forms.MultiRowsTextArea(rows=10)[source]

HTML Text Area whose rows attribute can be set when define the Form schema.

Parameters:rows (int) – The rows attribute in HTML tag.
class railgun.website.forms.ProfileForm(*args, **kwargs)[source]

The form to edit a user’s profile. Used in profile_edit() directly, and is derived by AdminUserEditForm for admins to edit user profiles.

Some fields in this form should be disabled, since users from third-party authentication providers may not allow to change these fields. You may refer to railgun.website.userauth.AuthProvider for more details.

validate_email(form, field)[source]

Extra validation that the email has not been taken.

Note

Email addresses ends with config.EXAMPLE_USER_EMAIL_SUFFIX is not allowed.

Note

We need the_user to get associated user id, since we should allow the user to keep his or her email not changed, we then allow the user with same user id in the database has the same email address.

validate_locale(form, field)[source]

Validate whether the user provided locale is a valid locale.

validate_password(form, field)[source]

Validate whether the password length is within 7 and 32 characters.

We move the validations of password from general validators to customized method, in that we may allow the user to keep the password input empty, which means to keep password unchanged.

validate_timezone(form, field)[source]

Validate whether the user provided timezone is a valid timezone. Lists of valid timezones can be found on Wikipedia

confirm

The password confirm text input.

email

The email address text input. Input text must be a valid email address, and the maximum length is 80.

family_name

The family name text input. Maximum length is 64.

given_name

The given name text input. Maximum length is 64.

locale

The language select field input. Only the languages with translations installed are listed here.

password

The password text input. Minimum length is 7, and maximum length is 32. It must be equal to confirm.

the_user[source]

Get the user object associated with this form. Default is current_user.

timezone

The timezone text input.

class railgun.website.forms.ReAuthenticateForm(*args, **kwargs)[source]

The form for users to re-validate themselves by enter their passwords. Used in reauthenticate().

password

Password text input.

class railgun.website.forms.SigninForm(*args, **kwargs)[source]

The form for users to sign in. Used in signin().

login

Username or email address text input.

password

Password text input.

remember

The checkbox indicating whether to make the user session persistent. A persistent session can live alive even after the user quits his or her web browser.

class railgun.website.forms.SignupForm(*args, **kwargs)[source]

The form for anonymous users to create a new account. Derived from CreateUserForm, used in signup().

validate_email(form, field)[source]

Extra validation that the email has not been taken.

email

The email address text input. Input text must be a valid email address, and the maximum length is 80.

class railgun.website.forms.UploadHandinForm(*args, **kwargs)[source]

The form for users to upload archive files as submissions.

validate_handin(form, field)[source]

Extra validation on handin that the uploaded file must not be larger than config.MAX_SUBMISSION_SIZE.

handin

File upload input. Only archive files are allowed.

class railgun.website.forms.VoteJsonEditForm(*args, **kwargs)[source]

The form to edit a vote using JSON source code.

validate_json_source(form, field)[source]

Extract the vote data from JSON source code and and construct object representation.

class railgun.website.forms.VoteSignupForm(*args, **kwargs)[source]

The form to signup a project for the voting.

Extra validation on logo that the uploaded file must not be larger than config.VOTE_LOGO_MAXIMUM_FILE_SIZE.

description

The description input.

group_name

The group name input.

File upload input. Only image files are allowed.

project_id

The project name input.

Views for Standard Users

Some of the views support extra arguments in the query string. For example, the login page support an extra next argument, representing what url to redirect to after the user has successfully logged in:

/signin/?next=http%3A//www.baidu.com

Which will redirect the logged user to www.baidu.com. Other modules may accept page and perpage arguments, to define the behaviour of page navigation.

class railgun.website.views.CachedVoteItem(dbitm)[source]

Copy values from models.VoteItem so that it can be pickled and cached.

railgun.website.views.about()[source]

The about page in request locale. You may refer to translated_page() for more details about the translated manual page.

Route:/manual/about/
Method:GET
Template:manual.html, manual/about/<lang>.md
railgun.website.views.about_source()[source]

The about source code page in request locale. You may refer to translated_page_source() for more details about the translated manual source code page.

Route:/manual/about/source/
Method:GET
railgun.website.views.docs_index()[source]

The index page of the documentation.

Documentation pages should be placed at docs/_build/html. You may generate the documentation files by executing make html under docs directory.

Route:/docs/
Method:GET
railgun.website.views.docs_static(filename)[source]

The static resources of the documentation.

Route:/docs/
Method:GET
railgun.website.views.faq()[source]

The faq page in request locale. You may refer to translated_page() for more details about the translated manual page.

Route:/manual/faq/
Method:GET
Template:manual.html, manual/faq/<lang>.md
railgun.website.views.faq_source()[source]

The faq source code page in request locale. You may refer to translated_page_source() for more details about the translated manual source code page.

Route:/manual/faq/source/
Method:GET
railgun.website.views.handin_detail(*args, **kwargs)[source]

View the submission detailed report. If the submission is not owned by current user, nor is current user an administrator, then this view will send a 404 http error to request user.

If the submission file was stored in config.UPLOAD_STORE_DIR, a link will be displayed to let the user download original file.

Route:/handin/<uuid>/
Method:GET
Template:handin_detail.html
Parameters:uuid (str) – The submission uuid.
railgun.website.views.handin_download(*args, **kwargs)[source]

Download the original submission file. If the submission is not owned by current user, nor is current user an administrator, then this view will send a 404 http error to request user.

All the original submission files should be stored in config.UPLOAD_STORE_DIR, if config.STORE_UPLOAD is set to True.

Route:/handin/<uuid>/download/
Method:GET
railgun.website.views.homework(*args, **kwargs)[source]

The page to show homework detail, and to upload submissions.

If the homework is locked, then a standard user cannot download the attachment, nor can he or she upload submissions. If the homework is hidden, then a standard user will receive an http 404 error. However, admins can always view all homework and upload submissions.

The number of pending and running submissions for a single user uploaded to a single homework is restricted by config.MAX_USER_PENDING_PER_HW. If this limit exceeds, submissions will be rejected.

Submissions that have passed all deadlines are rejected as well.

If everything is okay, then the submission will be queued into the runner queue. There exists a controller in website package to deliver submissions in different programming languages to their desired destination. Such controller is implemented in railgun.website.codelang. You may refer to railgun.website.codelang.CodeLanguage.handle_upload() for more details.

When config.STORE_UPLOAD is set to True, the original submission from user will be stored into config.UPLOAD_STORE_DIR, where the user may download their submissions from handin_detail view.

After submitted successfully, the user will be redirected to hwhandins().

Route:/homework/<slug>/
Method:GET, POST
Template:homework.html
Form:Generated by railgun.website.codelang.CodeLanguage
NaviId:homework.<slug>
Parameters:slug (str) – The url slug of the requested homework.
railgun.website.views.hwhandins(*args, **kwargs)[source]

The page to list all submissions to a homework assignment uploaded by current user. If the homework has been deleted, send 404 http error to request user.

This page supports page navigation, thus accepts page and perpage query string argument, where page defines the navigated page id (>= 1), and perpage defines the page size (default 10).

Route:/homework/<slug>/handin/
Method:GET
Template:homework.handins.html
NaviId:homework.<slug>.handin
Parameters:slug (str) – The url slug of requested homework.
railgun.website.views.hwpack(*args, **kwargs)[source]

The attachment of a homework in given programming language.

If the request user is not an adminstrator, and the homework is locked, the user will get a 403 error. If the homework is hidden, the user will get a 404 error as if it does not exist.

The attachments will not be packed into archive files automatically. You should execute manage.py build-cache to cache the archive files manually (which will be stored in config.HOMEWORK_PACK_DIR).

Route:

/hwpack/<slug>/<lang>.zip

Method:

GET

Parameters:
  • slug (str) – The url slug of requested homework.
  • lang (str) – The attachment programming language.
railgun.website.views.hwstatic(filename)[source]

Serve the static resources of all homework descriptions. You may refer to Writing Description to see which files will be exposed, and how to use these resources in homework descriptions.

The resources should be gathered into config.HOMEWORK_STATIC_DIR manually, by executing manage.py build-cache. In addition, you may use a static http server, like nginx, to serve these files instead of a WSGI application server.

Route:/hwstatic/<path:filename>
Method:GET
Parameters:filename (str) – The relative file path of homework static resource.
railgun.website.views.index()[source]

The index page that displays the jumbotron to anonymous users, and show the scores of submitted homework to login user.

Route:/
Method:GET
Template:index.html
railgun.website.views.profile_edit(*args, **kwargs)[source]

The form page for the user to edit their profile.

For the accounts from third-party authentication providers, some fields of the form may be locked and cannot be modified. This feature isn’t implemented here, but in railgun.website.userauth.

You may refer to railgun.website.userauth.AuthProvider.init_form() for more details.

Route:/profile/edit/
Method:GET, POST
Template:profile_edit.html
Form:railgun.website.forms.ProfileForm
railgun.website.views.reauthenticate(*args, **kwargs)[source]

The form page to ask user enter their password to revalidate their stale session.

If a user let the system remember his or her login at signup(), we then call it a persistent session. This is because such session will not be expired within just hours of time, nor will it become invalid after quitting the browser.

We call a recovered session after a relaunch of the browser or time expiration a stale session. Sometimes we may want to make sure the user owns the permission before taking any operations. In these situations, the stale sessions are not enough.

So reauthenticate view force the user to refresh their stale session. You may decorate the view by fresh_login_required() to ensure this.

If the credential passes validation, the user will be redirected to index() view, unless the next argument is given in the query string, where a redirection to next will take place.

Route:/reauthenticate/
Method:GET, POST
Template:reauthenticate.html
Form:ReAuthenticateForm
railgun.website.views.scores()[source]

The scoring rules page in request locale. You may refer to translated_page() for more details about the translated manual page.

Route:/manual/scores/
Method:GET
Template:manual.html, manual/scores/<lang>.md
railgun.website.views.scores_source()[source]

The scoring rules source code page in request locale. You may refer to translated_page_source() for more details about the translated manual source code page.

Route:/manual/scores/source/
Method:GET
railgun.website.views.signin()[source]

The form page for users to login.

If the credential passes validation, the user will be redirected to index() view, unless the next argument is given in the query string, where a redirection to next will take place.

Route:/signin/
Method:GET, POST
Template:signin.html
Form:SigninForm
railgun.website.views.signout()[source]

Sign out the logged user, and redirect to index() view.

Route:/signout/
Method:GET
railgun.website.views.signup()[source]

The form page for anonymous user to create a new account.

If the requested operation is successful, the user will be redirected to signin() view. You may disable the registration by setting config.ALLOW_SIGNUP to False, where a 403 http error will be responded to request users.

Route:/signup/
Method:GET, POST
Template:signup.html
Form:SignupForm
railgun.website.views.userguide()[source]

The user’s guide page in request locale. You may refer to translated_page() for more details about the translated manual page.

Route:/manual/userguide/
Method:GET
Template:manual.html, manual/userguide/<lang>.md
railgun.website.views.userguide_source()[source]

The user’s guide source code page in request locale. You may refer to translated_page_source() for more details about the translated manual source code page.

Route:/manual/userguide/source/
Method:GET
railgun.website.views.vote_index(*args, **kwargs)[source]

The page to display vote options to the user.

Route:/vote/
Method:GET, POST
Template:vote_index.html
railgun.website.views.vote_result(*args, **kwargs)[source]

The page to display vote result to the user.

Route:/vote/result/
Method:GET
Template:vote_result.html
railgun.website.views.vote_signup(*args, **kwargs)[source]

The page to signup a project for voting.

Route:/vote/signup/
Method:GET, POST
Template:vote_signup.html
railgun.website.views.vote_static(filename)[source]

The static resources of the voting signup.

Route:/vote/static/
Method:GET

Views for Administrators

railgun.website.admin.adduser(*args, **kwargs)[source]

Admin page to create a new user.

The new users will have <username>@<config.EXAMPLE_USER_EMAIL_SUFFIX> as their initial email address. The system will require such users to fill in their real emails address when they log in for the first time.

If the user has been created successfully, the visitor will be redirected to users().

Route:/admin/adduser/
Method:GET, POST
Form:CreateUserForm
Template:admin.adduser.html
railgun.website.admin.admin_required(method)[source]

A decorator on Flask view functions that validate whether the request user is an administrator.

If not authenticated, the request user will be redirected to signin(). If not an administrator, an error message will be flashed and the request user will be redirected to index. If the session is stale, the request user will be redirected to reauthenticate().

Usage:

@bp.route('/')
@admin_required
def admin_index():
    return 'This page can only be accessed by admins.'
railgun.website.admin.clear_vote(*args, **kwargs)[source]

Admin page to clear the vote.

Route:/admin/vote/clear/
railgun.website.admin.delete_vote_signup(*args, **kwargs)[source]

The page to delete vote signup data.

Route:/admin/vote/signup/delete/<filename>/
Method:GET
railgun.website.admin.edit_vote(*args, **kwargs)[source]

Admin page to edit the vote.

Railgun only supports one vote at this time. The existing vote data will be purged.

Route:/admin/vote/
Method:GET, POST
Template:admin.edit_vote.html
railgun.website.admin.edit_vote_signup(*args, **kwargs)[source]

The page to edit vote signup data.

Route:/admin/vote/signup/edit/<filename>/
Method:GET
railgun.website.admin.get_longblob_patch_command(*args, **kwargs)[source]

As is mentioned in models.py, SQLAlchemy uses BLOB as the backend of PickleType in default, where the size of BLOB in MySQL is only 64K. This will cause some Handin records be truncated, thus they cannot be fetched back into Railgun.

We’ve now added patch to prevent this situation. The existing broken records may be repaired simply by purging its detailed report data. This view just provides the SQL command to do so.

railgun.website.admin.handins(*args, **kwargs)[source]

The admin page to show all submissions.

This page supports page navigation, thus accepts page and perpage query string argument, where page defines the navigated page id (>= 1), and perpage defines the page size (default 10).

Route:/admin/handin/
Method:GET
Template:admin.handins.html
railgun.website.admin.handins_for_user(*args, **kwargs)[source]

The admin page to show all submissions from a given user.

This page supports page navigation, thus accepts page and perpage query string argument, where page defines the navigated page id (>= 1), and perpage defines the page size (default 10).

Route:/admin/handin/<username>/
Method:GET
Template:admin.handins.html
Parameters:username (str) – The name of queried user.
railgun.website.admin.hwcharts(*args, **kwargs)[source]

The admin page to view various of charts of a given homework.

All users except the administrators will be considered to generate the charts.

Route:/admin/hwcharts/<hwid>/
Method:GET
Template:admin.hwcharts.html
railgun.website.admin.hwcharts_pack(*args, **kwargs)[source]

Download the packed homework chart data.

Route:/admin/hwcharts/<hwid>/pack/
Method:GET
railgun.website.admin.hwscores(*args, **kwargs)[source]

The admin page to view the student score table of a given homework.

All users except the administrators will be listed on the table, even if he or she does not upload any submission. Only the highest score of a user will be displayed.

The view accepts a query string argument csvfile, and if csvfile is set to 1, a csv data file will be responded to the visitor instead of a html table page.

Route:/admin/hwscores/<hwid>/
Method:GET
Template:admin.csvdata.html
railgun.website.admin.make_charts_data(hw)[source]

Make hwcharts data object.

railgun.website.admin.manage_vote_signup(*args, **kwargs)[source]

The page to manage vote signup data.

Route:/admin/vote/signup/
Method:GET
Template:admin.manage_vote_signup.html
railgun.website.admin.runqueue_clear(*args, **kwargs)[source]

Stop the runner queue, set the score of all pending and running submissions to 0.0, and the state to Rejected. The visitor will be redirected to handins() after the operation takes place.

Route:/admin/runqueue/clear/
Method:GET
railgun.website.admin.runqueue_rerun(*args, **kwargs)[source]

Reset the state of given submission and put it into runqueue again. This operation is only enabled if config.STORE_UPLOAD is turned on.

If the operation is successful, the visitor will be redirected to the query string argument next, or handins() if next is not given.

Route:/admin/runqueue/rerun/<handid>/
Method:GET
Parameters:handid (str) – The uuid of submission.
railgun.website.admin.scores(*args, **kwargs)[source]

The admin page to list all homework assignments, each with a link to the homework score table.

Route:/admin/scores/
Method:GET
Template:admin.scores.html
railgun.website.admin.switch_vote(*args, **kwargs)[source]

Admin page to open or close the vote.

Parameters:isopen – “1” or “0” to indicate whether to open the vote.
Route:/admin/switch/<isopen>/
Method:GET
railgun.website.admin.user_activate(*args, **kwargs)[source]

Activate the given user.

This view accepts an extra query string argument next, where the visitor will be redirected to next after the operation. If next is not given, the visitor will be redirected to users().

Route:/admin/users/<name>/activate/
Method:GET
Parameters:name (str) – The name of operated user.
railgun.website.admin.user_deactivate(*args, **kwargs)[source]

Deactivate the given user.

This view accepts an extra query string argument next, where the visitor will be redirected to next after the operation. If next is not given, the visitor will be redirected to users().

Note

An administrator cannot deactivate him or herself.

Route:/admin/users/<name>/deactivate/
Method:GET
Parameters:name (str) – The name of operated user.
railgun.website.admin.user_delete(*args, **kwargs)[source]

Delete the given user.

This view accepts an extra query string argument next, where the visitor will be redirected to next after the operation. If next is not given, the visitor will be redirected to users().

Note

An administrator cannot delete him or herself.

Route:/admin/users/<name>/delete/
Method:GET
Parameters:name (str) – The name of operated user.
railgun.website.admin.user_edit(*args, **kwargs)[source]

Admin page to modify an existing user.

For the accounts from third-party authentication providers, some fields of the form may be locked and cannot be modified. This feature isn’t implemented here, but in railgun.website.userauth.

If the user has been successfully updated, the visitor will be redirected to users().

Route:/admin/users/<name>/
Method:GET, POST
Form:AdminUserEditForm
Template:admin.user_edit.html
railgun.website.admin.users(*args, **kwargs)[source]

Admin page to manage registered users. Information of the users will be gathered on this page, and each record will be given a link to its editing page.

This page supports page navigation, thus accepts page and perpage query string argument, where page defines the navigated page id (>= 1), and perpage defines the page size (default 10).

Route:/admin/users/
Method:GET
Template:admin.users.html
railgun.website.admin.bp = <flask.blueprints.Blueprint object at 0x7f8dbf0ded50>

A Blueprint object. All the views for administration are registered to this blueprint.

Views for WebAPI

railgun.website.api.api_handin_proclog(*args, **kwargs)[source]

Store the process outputs for a given submission.

This api view is usually requested after the reports of the corresponding submission has been stored, so it would not change either the score or the state of the submission.

If the submission state is still Pending or Running, indicating that the reports have not been stored (probably the process exited abnormally before report the score), the state will be updated to Rejected.

This view will compare uuid in POST object to the uuid argument. If they are not equal, the operation will be rejected, since it is likely to be an attack.

Route:/api/handin/proclog/<uuid>/
Payload:
{"uuid": uuid of submission,
 "exitcode": The exitcode of the process,
 "stdout": The standard output of the process,
 "stderr": The standard error output of the process}
Parameters:uuid (str) – The uuid of submission.
Returns:OK if succeeded, error messages otherwise.
railgun.website.api.api_handin_report(*args, **kwargs)[source]

Store the final score and detailed reports of given submission.

This view will compare uuid in POST object to the uuid argument. If they are not equal, the operation will be rejected, since it is likely to be an attack.

If the submission state is neither Running nor Pending, the operation will be rejected, since it is likely to be a programmatic bug.

If the reported score is 0.0, but the state is Accepted, then it will be modified to Rejected, since it is wired for a zero-score submission to be Accepted.

If the reported brief result message is empty, it will be set to a translated version of “Your submission is accepted.” or “Your submission is rejected.”, depending on the reported state.

The FinalScore table records will also be updated in this view.

Route:/api/handin/report/<uuid>/
Payload:A serialized HwScore object.
Parameters:uuid (str) – The uuid of submission.
Returns:OK if succeeded, error messages otherwise.
railgun.website.api.api_handin_start(*args, **kwargs)[source]

Change the state of given submission from Pending to Running.

This view will compare uuid in POST object to the uuid argument. If they are not equal, the operation will be rejected, since it is likely to be an attack.

If the submission is not Pending, the operation will also be rejected, since it may be a duplicated request from the runner.

Payload:{“uuid”: uuid of submission}
Parameters:uuid (str) – The uuid of submission.
Returns:OK if succeeded, error messages otherwise.
railgun.website.api.api_myip()[source]

API routing that send back the visitor’s ip address. The Content-Type of response is text/plain.

Route:/api/myip/
Method:GET
Returns:The visitor’s ip address.
railgun.website.api.secret_api(method)[source]

Decorate the view method so that the POST data will be first decrypted via AES cipher, then decoded as JSON message. The resulted object will be stored in flask.request.payload.

This decorator expects the request header Content-Type: application/octet-stream, otherwise it will return a 400 http error. Also, if it could not decrypt the POST data via AES cipher, or if it could not decode the JSON message, it will return a 400 http error as well.

Parameters:method – The method to be decorated.

Manual Pages Utilities

This module formats markdown manuals into html pages.

The manual pages support translations. They are written in markdown and stored under railgun/website/manual. Each directory is one single manual page, where each locale holds the translated contents of this manual page in “[lang].md”.

railgun.website.manual.translated_page(name)[source]

Render the requested manual page to visitor. The best locale for current user will be detected automatically.

Template:manual.html
Parameters:name (str) – The manual page name.
railgun.website.manual.translated_page_source(name)[source]

Render the source of requested manual page to visitor. The best locale for current user will be detected automatically.

Parameters:name (str) – The manual page name.
railgun.website.manual.manual_pages

Cache all the manual page instances in memory, so that the markdown source code will only be formatted once.

Handle Submissions of Various Languages

Railgun homework can be configured to accept submissions in different programming languages.

The type of content for various languages may be different. For example, a Python submission may be an archive file, while a NetAPI submission may be just a url address. This requires Railgun to generate different forms for these languages, and to handle the POST data differently according to the submission type.

Thid module provides CodeLanguage as the base class to generate forms and to handle requests. Derived from CodeLanguage, PythonLanguage, JavaLanguage, NetApiLanguage and InputLanguage are the actual implementations.

class railgun.website.codelang.CodeLanguage(lang, lang_name)[source]

The base class for all programming language handlers. A derived class must override three methods: upload_form(), do_handle_upload() and do_handle_download().

Parameters:
  • lang (str) – The programming language identity.
  • lang_name (GetTextString) – The translated programming language name.
do_handle_download(stored_content)[source]

Called by handle_download() to help send the original submission data to the client. Derived classes should override this to extract data from stored objects, to set http headers, and finish other necessary process.

Parameters:stored_content (object) – The stored object of this submission. Loading object from disk is finished in do_handle_download().
do_handle_upload(handid, hw, form)[source]

Called by handle_upload() to help handle the submission. Derived classes should implement this to store the submission data, and to put this submission into runner queue.

Parameters:
do_rerun(handid, hw, stored_content)[source]

Called by rerun() to reput the submission into runqueue. Derived classes should implement this.

Parameters:
  • handid (str) – The submission uuid.
  • hw (Homework) – The homework instance.
  • stored_content (object) – The stored object of this submission. Loading object from disk is finished in do_handle_download().
handle_download(handid)[source]

Handle user’s request to download original uploaded data of the given submission.

Parameters:handid (str) – The submission uuid.
handle_upload(handid, hw, form)[source]

Handle the uploaded form data submitted to the given homework in this programming language.

Parameters:
load_content(handid)[source]

Load the original data of given submission from disk file.

Parameters:handid (str) – The submission uuid.
Returns:The loaded object, or None if data file not exist.
make_db_record(handid, hw)[source]

Create a new submission in the database.

The state of the new submission will be Pending. The associated user will be current_user, and the scale will be set to g.ddl_scale.

Note

g.ddl_scale is initialized in railgun.website.views.homework(). We use this scale instead of calculating on the fly because the time may change and the deadline may expire during the request lifetime.

Parameters:
  • handid (str) – The submission uuid.
  • hw (Homework) – The homework this submission belongs to.
Returns:

The created Handin object.

rerun(handid, hw, fullscale=False)[source]

Reput the submission into runqueue. This operation should be called only if config.STORE_UPLOAD is enabled.

Parameters:
  • handid (str) – The submission uuid.
  • hw (Homework) – The homework instance.
  • fullscale (bool) – Whether to set the submission score scale to 1.0?
Returns:

True if successfully put into runqueue, False if original file is not stored, raises otherwise.

store_content(handid, content)[source]

Store the original data of given submission onto disk.

Data file is placed under config.UPLOAD_STORE_DIR. If config.STORE_UPLOAD is disabled, this method will do nothing. content may be any type of object, which will be serialized before writing to disk file.

Parameters:
  • handid (str) – The submission uuid.
  • content (object) – The original data object.
upload_form(hw)[source]

Generate an upload form for the given homework in this language. Derived classes must override this method.

Parameters:hw (Homework) – The homework instance.
class railgun.website.codelang.InputLanguage[source]

The handler for input programming languages that accepts csv data as submission.

class railgun.website.codelang.JavaLanguage[source]

The handler for Java programing language. This is a derived class from StandardLanguage, which only overrides the language identity and translated name.

class railgun.website.codelang.NetApiLanguage[source]

The handler for netapi programming languages that accepts a url address as submission data, to check whether the remote server mentioned by this url works properly.

class railgun.website.codelang.PythonLanguage[source]

The handler for Python programing language. This is a derived class from StandardLanguage, which only overrides the language identity and translated name.

class railgun.website.codelang.StandardLanguage(lang, lang_name)[source]

The basic handler for standard programming languages (like Python and Java) that accepts archive files as submissions.

This handler class will store the uploaded file content as well as its file name onto disk, in that Railgun relies on file extension to detect the archive file format.

Parameters:
  • lang (str) – The programming language identity.
  • lang_name (GetTextString) – The translated programming language name.
railgun.website.codelang.languages

A dict(Language Identity -> CodeLanguage) that maps identities to programming language handlers.

HTML Formatters of Various Objects

This module provides the strategies to render various objects into html.

class railgun.website.renders.CoveragePartialScoreRender[source]

A default HwPartialScore renderer that formats the partial score generated by CoverageScorer.

Template:renders/PartialScore.coverage.html
class railgun.website.renders.DefaultPartialScoreRender[source]

A default HwPartialScore renderer that wraps each line of the detail message into a pair of pre tags.

Template:renders/PartialScore.default.html
class railgun.website.renders.InputDataPartialScoreRender[source]

A default HwPartialScore renderer that formats the partial score generated by InputDataScorer.

Template:renders/PartialScore.inputdata.html
class railgun.website.renders.ObjSchemaPartialScoreRender[source]

A default HwPartialScore renderer that formats the partial score generated by ObjSchemaScorer.

Template:renders/PartialScore.coverage.html
class railgun.website.renders.PartialScoreRender[source]

Base class to render a HwPartialScore.

static getRender(typeName)[source]

Get the renderer for HwPartialScore with given type name.

Parameters:typeName (str) – The type name of HwPartialScore.
render(partial)[source]

Render the given partial score object. Derived classes should override this to implement the renderer.

Parameters:partial (HwPartialScore) – The partial score object.
railgun.website.renders.renderPartialScore(partial)[source]

Shorcut to finding a suitable renderer for given partial score, and to get the rendered html text.

Parameters:partial (HwPartialScore) – The partial score object.