devilry_qualifiesforexam2
¶
Database models, APIs and UI for qualifying students for final exams.
UI workflow¶
Step 1, 3 and 4 is provided with the app, and step 2 is the actual “plugin” that’s implemented, but more on that further down in the Creating a plugin-section.
The numbered list below shows the preferable UI-workflow of a plugin.
- Choose a plugin:
List the title and description of each plugin and let the user select the plugin they want to use.
- Plugin(this is usually the only part that needs to be implemented):
This part is completely controlled by the plugin and may be more than one page if that should be needed by the plugin. This is where you crunch the data needed to display the final result.
- Preview:
Preview the results with the option to save or go back to the previous page.
- Final state:
When the preview is saved, a new view with the result should be shown. This view may “look” the same as the preview view. I most cases this will be a list of all students and their current status on whether they are qualified or not and status for each assignment.
Creating a plugin¶
Using the already existing approved/not approved plugin as an example. The plugin shows a view where you can select the assignments that needs to be approved for a student to qualify for the final exam.
A plugin can do whatever fits its purpose, just remember to follow the workflow above.
1: In you app create a view or views for the plugin. Using a multiselect view which lists all the assignments, and makes
it possible to either select or deselect each assignment.
You must create a form to be used, and the actual view to use by extending the needed classes provided in
devilry.devilry_qualifiesforexam.views.plugin_base_views.base_multiselect_view
.
# Devilry imports
from devilry.devilry_qualifiesforexam.views.plugin_base_views import base_multiselect_view
from devilry.devilry_qualifiesforexam_plugin_approved import resultscollector
from devilry.devilry_qualifiesforexam.views import plugin_mixin
class PluginSelectAssignmentsView(base_multiselect_view.QualificationItemListView, plugin_mixin.PluginMixin):
plugintypeid = 'devilry_qualifiesforexam_plugin_approved.plugin_select_assignments'
def get_period_result_collector_class(self):
return resultscollector.PeriodResultSetCollector
2: As you can see, we have defined a function get_period_result_collector_class in the class defined in step 1.
If you need to base the qualification status of a student on the achievements on assignments, you will need to
create a class for your plugin that extends devilry.devilry_qualifiesforexam.pluginhelpers.PeriodResultSet
(as we need to do in this example).
# Devilry imports
from devilry.devilry_qualifiesforexam.pluginhelpers import PeriodResultsCollector
class PeriodResultSetCollector(PeriodResultsCollector):
"""
A subset of assignments are evaluated for the period.
"""
def student_qualifies_for_exam(self, aggregated_relstudentinfo):
for assignmentid, groupfeedbacksetlist in aggregated_relstudentinfo.assignments.iteritems():
if assignmentid in self.qualifying_assignment_ids or len(self.qualifying_assignment_ids) == 0:
feedbackset = groupfeedbacksetlist.get_feedbackset_with_most_points()
if not feedbackset:
return False
elif feedbackset.grading_points < feedbackset.group.parentnode.passing_grade_min_points:
return False
return True
3: In your app, create a plugin.py file.
Inside the plugin.py create a class that extends
devilry.devilry_qualifiesforexam.plugintyperegistry.PluginType
.
This defines the plugin you just created the view logic for in step 1.
from devilry.devilry_qualifiesforexam.plugintyperegistry import PluginType
from .views import select_assignment
class SelectAssignmentsPlugin(PluginType):
plugintypeid = 'your_app_name.plugin_pluginname'
human_readable_name = 'Select assignments'
description = 'Some description of what the plugins does.'
def get_plugin_view_class(self):
return select_assignment.PluginSelectAssignmentsView
4: Register the plugin with the devilry_qualifiesforexam app by creating an apps.py file and load the plugin to the registry when the app is loaded.
from django.apps import AppConfig
class DevilryQualifiesForExamAppConfig(AppConfig):
name = 'devilry.devilry_qualifiesforexam_plugin_approved'
verbose_name = 'Devilry qualifies for exam plugin approved'
def ready(self):
from devilry.devilry_qualifiesforexam import plugintyperegistry
from . import plugin
plugintyperegistry.Registry.get_instance().add(plugin.SelectAssignmentsPlugin)
Datamodel API¶
- class devilry.devilry_qualifiesforexam.models.DeadlineTag(id, timestamp, tag)¶
- exception DoesNotExist¶
- exception MultipleObjectsReturned¶
- class devilry.devilry_qualifiesforexam.models.PeriodTag(period, deadlinetag)¶
- exception DoesNotExist¶
- exception MultipleObjectsReturned¶
- class devilry.devilry_qualifiesforexam.models.StatusQuerySet(model=None, query=None, using=None, hints=None)¶
QuerySet-manager for
Status
.- get_last_status_in_period(period, prefetch_relations=True)¶
Get the last
Status
for the period.Prefetches all
QualifiesForFinalExam
for theStatus
objects and selects the last status.- Parameters:
period – current period.
prefetch_relations – Prefetches the period for the Status and the related
QualifiesForFinalExam
.
- Returns:
The latest
Status
.
- class devilry.devilry_qualifiesforexam.models.Status(id, status, period, createtime, message, user, plugin, plugin_data, exported_timestamp)¶
- objects = <django.db.models.manager.ManagerFromStatusQuerySet object>¶
Sets
StatusQuerySet
as manager for model.
- READY = 'ready'¶
The list of qualified students are ready for export. Usually this means that all students have received a feedback on their assignments.
This should be the default when creating a new Status for a Period.
- ALMOSTREADY = 'almostready'¶
Most students are ready for export. Almost all students have received a feedback on their assignments. TODO: Legacy, to be removed.
- NOTREADY = 'notready'¶
Students have pending feedbacks. This should be used when students students are awaiting feedback on assignments. Typically the status is
not ready
if no students(or just a few) have received a feedback on the last assignment.
- STATUS_CHOICES = [('ready', 'Ready for export'), ('almostready', 'Most students are ready for export'), ('notready', 'Not ready for export (retracted)')]¶
Choice list for status on the qualification list.
- status¶
The status of the qualification list. Note: The statuses does not represent any final state in the system, and the admin can use these statuses as they see fit
- period¶
Period the qualifications are for.
- createtime¶
Status created datetime. This is changed if the list updated.
- message¶
Message provided with the qualification list of why it was retracted.
- user¶
The user that created the qualification list(an admin).
- plugin¶
The plugin used.
- plugin_data¶
Plugin related data.
- exported_timestamp¶
The datetime when the list was exported. If this is null, this is considered a draft for preview before it’s saved.
- clean()¶
Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.
- exception DoesNotExist¶
- exception MultipleObjectsReturned¶
- class devilry.devilry_qualifiesforexam.models.QualifiesForFinalExam(id, relatedstudent, status, qualifies)¶
The related
RelatedStudent
the qualification is for.
- qualifies¶
True
if the student qualifies for the exam, elseFalse
.
- clean()¶
Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.
- exception DoesNotExist¶
- exception MultipleObjectsReturned¶
Views API¶
- class devilry.devilry_qualifiesforexam.views.plugin_base_views.base_multiselect_view.SelectedQualificationForm(*args, **kwargs)¶
Subclass this if extra fields needs to be added.
- qualification_modelclass¶
alias of
Assignment
- selected_items¶
The items selected as ModelMultipleChoiceField. If some or all items should be selected by default, override this.
- property media¶
Return all media required to render the widgets on this form.
- class devilry.devilry_qualifiesforexam.views.plugin_base_views.base_multiselect_view.SelectedQualificationItem(value)¶
The selected item from the list.
This can be subclassed if needed.
- Parameters:
value – The value to render. Typically a django model object.
- get_title()¶
Get the title of an item.
- Returns:
SelectedQualificationItem.value
string representation.- Return type:
str
- class devilry.devilry_qualifiesforexam.views.plugin_base_views.base_multiselect_view.SelectableQualificationItemValue(*args, **kwargs)¶
- Parameters:
target_dom_id – See
get_target_dom_id()
.is_selected – Mark the item as selected on load.
- selected_item_renderer_class¶
alias of
SelectedQualificationItem
- get_title()¶
Get the title of the box.
Defaults to
str(self.value)
.
- get_description()¶
Get the description (shown below the title).
Defaults to
None
, which means that no description is rendered.
- class devilry.devilry_qualifiesforexam.views.plugin_base_views.base_multiselect_view.QualificationItemTargetRenderer(form, dom_id=None, without_items_text=None, with_items_title=None, no_items_selected_text=None, submit_button_text=None, form_action=None, empty_selection_allowed=False)¶
Target renderer for selected items.
Provides default descriptions for target status when no items are selected, items are selected and the submit button text.
Subclass this if changes to these labels need to be made.
- Parameters:
form – A django Form object.
dom_id – See
get_dom_id()
.without_items_text – See
get_without_items_text()
.no_items_selected_text – See
get_no_items_selected_text()
.with_items_title – See
get_with_items_title()
.submit_button_text – See
get_submit_button_text()
.form_action – See
get_form_action()
.
- selected_target_renderer¶
The selected item as it is shown when selected. By default this is
SelectedQualificationItem
.alias of
SelectedQualificationItem
- descriptive_item_name = 'assignments'¶
A descriptive name for the items selected.
- get_submit_button_text()¶
- Returns:
The text that should be shown on the submit button.
- Return type:
str
- get_with_items_title()¶
- Returns:
The text that should be shown when items are selected.
- Return type:
str
- get_without_items_text()¶
- Returns:
The text that should be shown when no items are selected.
- Return type:
str
- class devilry.devilry_qualifiesforexam.views.plugin_base_views.base_multiselect_view.QualificationItemListView(**kwargs)¶
This class provides a basic multiselect preset.
By default, this class uses
Assignment
as model. This means that this class lists all theAssignments
for thePeriod
as selectable items and all items are selected by default.If you only want to list
Assignment
as selectable items and no extra fields needs to be added to the form, just subclass this and overrideget_period_result_collector_class()
.Examples
Here is an example of a plugin view that uses
Assignment
as listing values and nothing else:from devilry.devilry_qualifiesforexam.views.plugin_base_views import base_multiselect_view from devilry.devilry_qualifiesforexam.views import plugin_mixin class PluginSelectAssignmentsView(base_multiselect_view.QualificationItemListView, plugin_mixin.PluginMixin): plugintypeid = 'devilry_qualifiesforexam_plugin_approved.plugin_select_assignments' def get_period_result_collector_class(self): return some_result_collector_for_the_plugin
Here is an example that uses
Assignment
as selectable items but an extra field is added, to achieve this, the base form and target renderer must be subclassed:from devilry.devilry_qualifiesforexam.views.plugin_base_views import base_multiselect_view from devilry.devilry_qualifiesforexam_plugin_points import resultscollector from devilry.devilry_qualifiesforexam.views import plugin_mixin class PluginSelectAssignmentsAndPoints(base_multiselect_view.SelectedQualificationForm): min_points_to_achieve = forms.IntegerField( min_value=0, required=False, ) class WithPointsFormDataTargetRenderer(base_multiselect_view.QualificationItemTargetRenderer): def get_field_layout(self): return [ 'min_points_to_achieve' ] class PluginSelectAssignmentsAndPointsView(base_multiselect_view.QualificationItemListView, plugin_mixin.PluginMixin): plugintypeid = 'devilry_qualifiesforexam_plugin_approved.plugin_select_assignments_and_points' def get_period_result_collector_class(self): return resultscollector.PeriodResultSetCollector def get_form_class(self): return PluginSelectAssignmentsAndPoints def get_target_renderer_class(self): return WithPointsFormDataTargetRenderer
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- model¶
The model represented as a selectable item.
alias of
Assignment
- value_renderer_class¶
alias of
SelectableQualificationItemValue
- dispatch(request, *args, **kwargs)¶
Check if a
Status
withstatus
set toready
exists for the period. If it exists, redirect to the final export view.
- get_queryset_for_role(role)¶
Get a queryset of all the objects for the
QualificationItemListView.model
.This can be be customized with a call to super, and the filtering needed.
- Parameters:
role – The cradmin_role(Period).
- Returns:
queryset of from specified model.
- Return type:
QuerySet
- get_inititially_selected_queryset()¶
Get queryset of initially selected items.
Defaults to
self.model.object.none()
, and you should override this if you want to select any objects on load.
- get_target_renderer_class()¶
Get the target renderer class.
If a subclass of
QualificationItemTargetRenderer
is created, override this method by returning the subclass.- Returns:
- get_form_class()¶
Get a subclass of
SelectedQualificationForm
.Must be implemented in subclass.
- Raises:
NotImplementedError – If not implemented by subclass.
- get_period_result_collector_class()¶
Must be implemented by subclass if needed.
- Returns:
A subclass of
devilry.devilry_qualifiesforexam.pluginshelper.PeriodResultsCollector
- Raises:
NotImplementedError –
- get_qualifying_itemids(posted_form)¶
Get the ID of the items that qualify.
- Parameters:
posted_form – The posted form containing the items selected.
- Returns:
List of
self.model.id
that were selected.
- get_form_kwargs()¶
Return the keyword arguments for instantiating the form.
- form_valid(form)¶
Provides some basic functionality. If custom fields are added to the form, this function must be overridden in subclass to handle the posted data.
- Parameters:
form – Posted form with ids of selected items.