diff --git a/app/assets/javascripts/dashboard.js b/app/assets/javascripts/dashboard.js new file mode 100644 index 00000000..6f4cd720 --- /dev/null +++ b/app/assets/javascripts/dashboard.js @@ -0,0 +1,35 @@ +$(function() { + var REFRESH_INTERVAL = 5000; + + var refreshData = function() { + var jqxhr = $.ajax({ + dataType: 'json', + method: 'GET' + }); + jqxhr.done(updateView); + }; + + var updateProgressBar = function(progress_bar, data) { + var percentage = Math.round(data.quantity / data.pool_size * 100); + progress_bar.attr({ + 'aria-valuemax': data.pool_size, + 'aria-valuenow': data.quantity, + style: 'width: ' + percentage + '%' + }); + progress_bar.html(data.quantity); + }; + + var updateView = function(response) { + _.each(response.docker, function(data) { + var row = $('tbody tr[data-id=' + data.id + ']'); + $('.pool-size', row).html(data.pool_size); + var progress_bar = $('.quantity .progress .progress-bar', row); + updateProgressBar(progress_bar, data); + }); + }; + + if ($.isController('dashboard')) { + refreshData(); + setInterval(refreshData, REFRESH_INTERVAL); + } +}); diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb new file mode 100644 index 00000000..a3db4cf0 --- /dev/null +++ b/app/controllers/admin/dashboard_controller.rb @@ -0,0 +1,17 @@ +module Admin + class DashboardController < ApplicationController + include DashboardHelper + + def policy_class + DashboardPolicy + end + + def show + authorize(self) + respond_to do |format| + format.html + format.json { render(json: dashboard_data) } + end + end + end +end diff --git a/app/helpers/admin/dashboard_helper.rb b/app/helpers/admin/dashboard_helper.rb new file mode 100644 index 00000000..62fbb02a --- /dev/null +++ b/app/helpers/admin/dashboard_helper.rb @@ -0,0 +1,13 @@ +module Admin + module DashboardHelper + def dashboard_data + {docker: docker_data} + end + + def docker_data + ExecutionEnvironment.order(:id).select(:id, :permitted_execution_time, :pool_size).map do |execution_environment| + execution_environment.attributes.merge(quantity: DockerContainerPool.quantities[execution_environment.id]) + end + end + end +end diff --git a/app/policies/admin/dashboard_policy.rb b/app/policies/admin/dashboard_policy.rb new file mode 100644 index 00000000..5ac15c00 --- /dev/null +++ b/app/policies/admin/dashboard_policy.rb @@ -0,0 +1,4 @@ +module Admin + class DashboardPolicy < AdminOnlyPolicy + end +end diff --git a/app/views/admin/dashboard/show.html.slim b/app/views/admin/dashboard/show.html.slim new file mode 100644 index 00000000..b1ee33a8 --- /dev/null +++ b/app/views/admin/dashboard/show.html.slim @@ -0,0 +1,20 @@ +h1 = t('breadcrumbs.dashboard.show') + +h2 Docker + +- if DockerContainerPool.config[:active] + .table-responsive + table.table + thead + tr + th = t('activerecord.models.execution_environment.one') + th = t('activerecord.attributes.execution_environment.pool_size') + th = t('.quantity') + tbody + - ExecutionEnvironment.order(:name).each do |execution_environment| + tr data-id=execution_environment.id + td = link_to(execution_environment, execution_environment) + td.pool-size + td.quantity = progress_bar(0) +- else + p = t('.inactive') diff --git a/config/locales/de.yml b/config/locales/de.yml index a2d9dadc..65f9b8db 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -123,11 +123,18 @@ de: models: exercise: at_most_one_main_file: dürfen höchstens eine Hauptdatei enthalten + admin: + dashboard: + show: + inactive: Der Container-Pool ist nicht aktiv. + quantity: Verfügbare Container application: not_authorized: Sie Sind nicht berechtigt, diese Aktion auszuführen. breadcrumbs: application: welcome: Startseite + dashboard: + show: Dashboard sessions: destroy_through_lti: Code-Abgabe consumers: diff --git a/config/locales/en.yml b/config/locales/en.yml index 7586a178..c57c01e3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -123,11 +123,18 @@ en: models: exercise: at_most_one_main_file: must include at most one main file + admin: + dashboard: + show: + inactive: Container pooling is not enabled. + quantity: Available Containers application: not_authorized: You are not authorized to perform this action. breadcrumbs: application: welcome: Cover Page + dashboard: + show: Dashboard sessions: destroy_through_lti: Code Submission consumers: diff --git a/config/routes.rb b/config/routes.rb index 24810b64..9ba066ce 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,10 @@ FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP) Rails.application.routes.draw do root to: 'application#welcome' + namespace :admin do + get 'dashboard', to: 'dashboard#show' + end + get '/help', to: 'application#help' resources :consumers diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb new file mode 100644 index 00000000..d62bb0ee --- /dev/null +++ b/spec/controllers/admin/dashboard_controller_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe Admin::DashboardController do + before(:each) { allow(controller).to receive(:current_user).and_return(FactoryGirl.build(:admin)) } + + describe 'GET #show' do + describe 'with format HTML' do + before(:each) { get :show } + + expect_status(200) + expect_template(:show) + end + + describe 'with format JSON' do + before(:each) { get :show, format: :json } + + expect_json + expect_status(200) + end + end +end diff --git a/spec/policies/admin/dashboard_policy_spec.rb b/spec/policies/admin/dashboard_policy_spec.rb new file mode 100644 index 00000000..252b2395 --- /dev/null +++ b/spec/policies/admin/dashboard_policy_spec.rb @@ -0,0 +1,19 @@ +require 'rails_helper' + +describe Admin::DashboardPolicy do + subject { Admin::DashboardPolicy } + + permissions :show? do + it 'grants access to admins' do + expect(subject).to permit(FactoryGirl.build(:admin), :dashboard) + end + + it 'does not grant access to teachers' do + expect(subject).not_to permit(FactoryGirl.build(:teacher), :dashboard) + end + + it 'does not grant access to external users' do + expect(subject).not_to permit(FactoryGirl.build(:external_user), :dashboard) + end + end +end