diff --git a/app/controllers/concerns/point_visualisation.rb b/app/controllers/concerns/point_visualisation.rb
new file mode 100644
index 000000000..cf3bdcd8a
--- /dev/null
+++ b/app/controllers/concerns/point_visualisation.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true.
+module PointVisualisation
+ extend ActiveSupport::Concern
+
+ def define_point_stats(user)
+ # TODO: bit ugly
+ @awarded_points = Hash[AwardedPoint.where(id: AwardedPoint.all_awarded(user)).to_a.sort!.group_by(&:course_id).map { |k, v| [k, v.map(&:name)] }]
+ @courses = []
+ @missing_points = {}
+ @percent_completed = {}
+ @group_completion_ratios = {}
+ @awarded_points.keys.each do |course_id|
+ course = Course.find(course_id)
+ next if course.hide_submissions?
+ @courses << course
+
+ awarded = @awarded_points[course.id]
+ missing = AvailablePoint.course_points(course).order!.map(&:name) - awarded
+ @missing_points[course_id] = missing
+
+ @percent_completed[course_id] =
+ if (awarded.size + missing.size).positive?
+ 100 * (awarded.size.to_f / (awarded.size + missing.size))
+ else
+ 0
+ end
+ @group_completion_ratios[course_id] = course.exercise_group_completion_ratio_for_user(user)
+ end
+ end
+end
diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb
index 4c3b86257..ec3342dbe 100644
--- a/app/controllers/courses_controller.rb
+++ b/app/controllers/courses_controller.rb
@@ -4,6 +4,7 @@
require 'exercise_completion_status_generator'
class CoursesController < ApplicationController
+ include PointVisualisation
before_action :set_organization
before_action :set_course, except: [:help, :index, :show_json]
@@ -41,6 +42,8 @@ def show
authorize! :read, @course
UncomputedUnlock.resolve(@course, current_user)
+ define_point_stats(current_user)
+
respond_to do |format|
format.html do
assign_show_view_vars
diff --git a/app/controllers/participants_controller.rb b/app/controllers/participants_controller.rb
index 1167e9bea..41e73b7df 100644
--- a/app/controllers/participants_controller.rb
+++ b/app/controllers/participants_controller.rb
@@ -1,6 +1,7 @@
require 'portable_csv'
class ParticipantsController < ApplicationController
+ include PointVisualisation
before_action :set_organization, only: [:index]
def index
@@ -61,8 +62,6 @@ def index
def show
@user = User.find(params[:id])
authorize! :view_participant_information, @user
- # TODO: bit ugly
- @awarded_points = Hash[AwardedPoint.where(id: AwardedPoint.all_awarded(@user)).to_a.sort!.group_by(&:course_id).map { |k, v| [k, v.map(&:name)] }]
if current_user.administrator?
add_breadcrumb 'Participants', :participants_path
@@ -71,27 +70,7 @@ def show
add_breadcrumb 'My stats', participant_path(@user)
end
- @courses = []
- @missing_points = {}
- @percent_completed = {}
- @group_completion_ratios = {}
- for course_id in @awarded_points.keys
- course = Course.find(course_id)
- if !course.hide_submissions?
- @courses << course
-
- awarded = @awarded_points[course.id]
- missing = AvailablePoint.course_points(course).order!.map(&:name) - awarded
- @missing_points[course_id] = missing
-
- if awarded.size + missing.size > 0
- @percent_completed[course_id] = 100 * (awarded.size.to_f / (awarded.size + missing.size))
- else
- @percent_completed[course_id] = 0
- end
- @group_completion_ratios[course_id] = course.exercise_group_completion_ratio_for_user(@user)
- end
- end
+ define_point_stats(@user)
if current_user.administrator? || current_user.id == @user.id
@submissions = @user.submissions.order('created_at DESC').includes(:user).includes(:course)
@@ -102,7 +81,6 @@ def show
@submissions = @submissions.limit(100) unless !!params[:view_all]
Submission.eager_load_exercises(@submissions)
-
end
def me
diff --git a/app/controllers/points_controller.rb b/app/controllers/points_controller.rb
index 577cfc16f..a6c491071 100644
--- a/app/controllers/points_controller.rb
+++ b/app/controllers/points_controller.rb
@@ -81,6 +81,9 @@ def show
end
respond_to do |format|
format.html
+ format.csv do
+ render_csv(filename: "#{@course.name}_#{@sheetname}_points.csv")
+ end
format.json do
output = {
api_version: ApiVersion::API_VERSION,
diff --git a/app/views/courses/show.html.erb b/app/views/courses/show.html.erb
index 19c5083c8..dfd39cd3a 100644
--- a/app/views/courses/show.html.erb
+++ b/app/views/courses/show.html.erb
@@ -246,6 +246,10 @@
%>
+ <% if signed_in? %>
+ <%= render 'layouts/points', courses: [@course], title: 'My points', show_course_name: false %>
+ <% end %>
+
<% if @submissions %>
Latest submissions
diff --git a/app/views/layouts/_points.html.erb b/app/views/layouts/_points.html.erb
new file mode 100644
index 000000000..61e17ff2e
--- /dev/null
+++ b/app/views/layouts/_points.html.erb
@@ -0,0 +1,70 @@
+
+
+ <%= title %>
+
+ <% for course in courses %>
+ <% if course && @percent_completed[course.id] %>
+
+
+ <% if show_course_name %>
+
+ <% if can? :read, course %>
+ <%= link_to course.title, organization_course_path(course.organization, course) %>
+ <% else %>
+ <%= course.title %>
+ <% end %>
+
+
+ <% end %>
+ <% if can? :see_points, course%>
+
Awarded points
+
+
+ <%= sprintf("%.0f", @percent_completed[course.id]) %>%
+
+
+ <% if @group_completion_ratios[course.id] %>
+ <% @group_completion_ratios[course.id].each do |group, ratio| %>
+
+
Awarded points for <%= group %>
+
+ <% unless ratio.zero? %>
+
+ <%= sprintf("%.0f", ratio * 100) %>%
+
+ <% end %>
+
+ <% end %>
+
+ <% end %>
+
+
+
+
+ |
+ Point names |
+
+
+
+
+ Awarded points
+ | <%= points_list(@awarded_points[course.id]) %> |
+
+
+ Missing points
+ | <%= points_list(@missing_points[course.id]) %> |
+
+
+
+ <% else %>
+ For this course points are not visible.
+ <% end %>
+
+
+ <%else%>
+ You don't have any points for this course
+
+ <% end %>
+
+ <% end %>
+
diff --git a/app/views/participants/show.html.erb b/app/views/participants/show.html.erb
index fe8e2363c..e32ddc625 100644
--- a/app/views/participants/show.html.erb
+++ b/app/views/participants/show.html.erb
@@ -18,75 +18,7 @@
-<% unless @user.email_verified? %>
-
- Your email address is not verified yet. <%= link_to 'Resend verification email', send_verification_email_path(@user), method: :post %>.
-
-<% end %>
-
-
-
- Points
-
- <% for course in @courses %>
-
-
-
- <% if can? :read, course %>
- <%= link_to course.title, organization_course_path(course.organization, course) %>
- <% else %>
- <%= course.title %>
- <% end %>
-
-
- <% if can? :see_points, course %>
-
Awarded points
-
-
- <%= sprintf("%.0f", @percent_completed[course.id]) %>%
-
-
- <% if @group_completion_ratios[course.id] %>
- <% @group_completion_ratios[course.id].each do |group, ratio| %>
-
-
Awarded points for <%= group %>
-
- <% unless ratio.zero? %>
-
- <%= sprintf("%.0f", ratio * 100) %>%
-
- <% end %>
-
- <% end %>
-
- <% end %>
-
-
-
-
- |
- Point names |
-
-
-
-
- Awarded points
- | <%= points_list(@awarded_points[course.id]) %> |
-
-
- Missing points
- | <%= points_list(@missing_points[course.id]) %> |
-
-
-
- <% else %>
- For this course points are not visible.
- <% end %>
-
-
-
- <% end %>
-
+<%= render 'layouts/points', courses: @courses, title: 'Points', show_course_name: true %>
Submissions
diff --git a/app/views/points/show.csv.erb b/app/views/points/show.csv.erb
new file mode 100644
index 000000000..c450c42e5
--- /dev/null
+++ b/app/views/points/show.csv.erb
@@ -0,0 +1,29 @@
+<% require 'portable_csv' %>
+<%= PortableCSV.generate(:force_quotes => true) do |csv|
+ arr = ["Username"]
+ arr += @user_fields.map(&:label) if @user_fields
+ arr += @exercises.map{ |exercise| [exercise[:name]] + (exercise.available_points.length > 1 ? [nil] * (exercise.available_points.length-1) : []) }.flatten
+ csv << arr
+
+ arr = [nil]
+ arr += [nil] * @user_fields.length if @user_fields
+ arr += @exercises.map{|exercise| exercise.available_points.sort.map{|point| point.name }}.flatten
+ csv << arr
+
+ @users.each do |user, index|
+ user_points = @users_to_points[user.login]
+ arr = [user.login]
+ if @user_fields
+ @user_fields.each do |field|
+ value = user.user_field_values.find { |o| o.field_name == field.name }
+ arr += [value.value] if value
+ end
+ end
+ @exercises.each do |exercise|
+ exercise.available_points.sort.each do |p|
+ arr += user_points.include?(p.name) ? [1] : [0]
+ end
+ end
+ csv << arr
+ end
+end.html_safe %>
diff --git a/app/views/points/show.html.erb b/app/views/points/show.html.erb
index b2d552195..9b1df2075 100644
--- a/app/views/points/show.html.erb
+++ b/app/views/points/show.html.erb
@@ -6,11 +6,14 @@
<% if params[:sort_by].blank? %>
- <%= link_to('Sort by points', organization_course_point_path(@organization, @course, @sheetname, sort_by: 'points', show_attempted: params[:show_attempted]), class: "btn btn-primary")%>
+ <%= link_to 'Sort by points', organization_course_point_path(@organization, @course, @sheetname, :sort_by => 'points'),class: "btn btn-primary" %>
<% else %>
<%= link_to('Sort by username', organization_course_point_path(@organization, @course, @sheetname, show_attempted: params[:show_attempted]), class: "btn btn-primary") %>
<% end %>
-
+ <% if can?(:teach, @course) %>
+ <%= link_to('Export as CSV', organization_course_point_path(@organization, @course, @sheetname, :sort_by => params[:sort_by], :format => 'csv'), class: "btn btn-primary") %>
+ <% end %>
+
<% if can? :refresh_gdocs_spreadsheet, @course %>
<% link_to 'Refresh Google Docs worksheet', refresh_gdocs_organization_course_point_path(@organization, @course, @sheetname), class: "btn btn-primary" %>
<% end %>
diff --git a/ext/tmc-langs b/ext/tmc-langs
index 01c61d96e..6693f225f 160000
--- a/ext/tmc-langs
+++ b/ext/tmc-langs
@@ -1 +1 @@
-Subproject commit 01c61d96e3832fb39e03504ecd331a9e9527e2dd
+Subproject commit 6693f225f79d4a39311280c5328a863544e342f3
diff --git a/ext/tmc-sandbox b/ext/tmc-sandbox
index da1da949a..40fc24b64 160000
--- a/ext/tmc-sandbox
+++ b/ext/tmc-sandbox
@@ -1 +1 @@
-Subproject commit da1da949a64df5803544798314fad4693b692288
+Subproject commit 40fc24b64a59070f12c59effc4d6aac7a301fa11