diff --git a/app/assets/javascripts/dolos.ts b/app/assets/javascripts/dolos.ts index bd2baea3ba..a23c747fed 100644 --- a/app/assets/javascripts/dolos.ts +++ b/app/assets/javascripts/dolos.ts @@ -5,13 +5,16 @@ import { html, render } from "lit"; import { i18n } from "i18n/i18n"; const LOADER_ID = "dolos-loader"; -const BTN_ID = "dolos-btn"; const DOLOS_URL = "/dolos_reports"; -export async function startDolos(url: string): Promise { +export function initDolosBtn(btnID: string, url: string): void { + const btn = document.getElementById(btnID) as HTMLLinkElement; + btn.addEventListener("click", () => startDolos(btn, url)); +} + +export async function startDolos(btn: HTMLLinkElement, url: string): Promise { const loader = document.getElementById(LOADER_ID) as LoadingBar; loader.show(); - const btn = document.getElementById(BTN_ID) as HTMLLinkElement; btn.classList.add("disabled"); const settings = new FormData(); @@ -43,7 +46,7 @@ export async function startDolos(url: string): Promise { loader.hide(); const newBtn = html` - + ${i18n.t("js.dolos.view_report")} `; diff --git a/app/assets/stylesheets/components/table.css.scss b/app/assets/stylesheets/components/table.css.scss index 4d4a8b866c..fe2d33e40f 100644 --- a/app/assets/stylesheets/components/table.css.scss +++ b/app/assets/stylesheets/components/table.css.scss @@ -57,11 +57,9 @@ .table-resource .actions { text-align: right; - &.submissions-table { - // by default the size of the element in the col is used as width if width is smaller than that element. - // This width is just here to force everything to the right - width: 1px; - } + // by default the size of the element in the col is used as width if width is smaller than that element. + // This width is just here to force everything to the right + width: 1px; } tr.gu-mirror { diff --git a/app/helpers/export_helper.rb b/app/helpers/export_helper.rb index 9930234993..cc045d1a05 100644 --- a/app/helpers/export_helper.rb +++ b/app/helpers/export_helper.rb @@ -6,8 +6,8 @@ class Zipper attr_reader :users, :item, :errors - CONVERT_TO_BOOL = %w[deadline only_last_submission with_info all with_labels].freeze - SUPPORTED_OPTIONS = %w[deadline filter_students group_by only_last_submission with_info all with_labels].freeze + CONVERT_TO_BOOL = %w[deadline only_last_submission with_info all with_labels evaluation].freeze + SUPPORTED_OPTIONS = %w[deadline filter_students group_by only_last_submission with_info all with_labels evaluation].freeze # Keywords used: # :item : A User, Course or Series for which submissions will be exported @@ -32,6 +32,7 @@ def initialize(**kwargs) .to_h { |m| [m.user, m.course_labels] } @users = @users_labels.keys if users.nil? + @users = @item.evaluation.users if evaluation? when Course @list = @item.series if all? @users_labels = @item.course_memberships @@ -92,6 +93,10 @@ def all? @options[:all].present? end + def evaluation? + @options[:evaluation].present? && @item.is_a?(Series) && @item.evaluation.present? + end + def zip_filename @item.is_a?(User) ? "#{@item.full_name.parameterize}.zip" : "#{@item.name.parameterize}.zip" end @@ -240,6 +245,8 @@ def bundle end def get_submissions_for_series(series, selected_exercises, users) + return policy_scope(series.evaluation.submissions.where(user_id: users.map(&:id), exercise_id: selected_exercises.map(&:id))) if evaluation? + submissions = policy_scope(Submission).where(user_id: users.map(&:id), exercise_id: selected_exercises.map(&:id), course: series.course_id).includes(:user, :exercise) submissions = submissions.before_deadline(@options[:deadline]) if deadline? submissions = submissions.group(:user_id, :exercise_id).most_recent if only_last_submission? diff --git a/app/javascript/packs/dolos.js b/app/javascript/packs/dolos.js index cfcf66c474..127de477f9 100644 --- a/app/javascript/packs/dolos.js +++ b/app/javascript/packs/dolos.js @@ -1,3 +1,3 @@ -import { startDolos } from "dolos.ts"; +import { initDolosBtn } from "dolos.ts"; -window.dodona.startDolos = startDolos; +window.dodona.initDolosBtn = initDolosBtn; diff --git a/app/models/evaluation.rb b/app/models/evaluation.rb index 0a9a2cafa8..20e68c1dbb 100644 --- a/app/models/evaluation.rb +++ b/app/models/evaluation.rb @@ -23,6 +23,7 @@ class Evaluation < ApplicationRecord has_many :users, through: :evaluation_users has_many :exercises, through: :evaluation_exercises has_many :score_items, through: :evaluation_exercises + has_many :submissions, through: :feedbacks has_many :annotated_submissions, -> { distinct }, through: :annotations, source: :submission diff --git a/app/views/evaluations/_exercises_progress_table.html.erb b/app/views/evaluations/_exercises_progress_table.html.erb index d0e3276c03..7b06cb6dfa 100644 --- a/app/views/evaluations/_exercises_progress_table.html.erb +++ b/app/views/evaluations/_exercises_progress_table.html.erb @@ -1,11 +1,15 @@ +<%= content_for :javascripts do %> + <%= javascript_include_tag 'dolos' %> +<% end %>
+ - + @@ -28,14 +32,27 @@ - <% end %> diff --git a/app/views/evaluations/show.html.erb b/app/views/evaluations/show.html.erb index 2b858da5e6..2c0c111434 100644 --- a/app/views/evaluations/show.html.erb +++ b/app/views/evaluations/show.html.erb @@ -5,21 +5,17 @@

<%= t('.title', series: @evaluation.series.name) %>

-
-
-
-

<%= t ".explanation_title" %>

- <% if @evaluation.score_items.empty? %> - <%= t ".explanation_no_grading_html" %> - <% else %> - <%= t ".explanation_yes_grading_html" %> - <% end %> -
-
-
-

<%= t '.deadline_html', users: @evaluation.users.count, exercises: @evaluation.exercises.count, deadline: l(@evaluation.deadline, format: :submission) %>

- <%= render partial: 'exercises_progress_table', locals: { metadata: @evaluation.metadata, series: @evaluation.series } %> -
+
+ <%= t ".explanation_title" %> + <% if @evaluation.score_items.empty? %> + <%= t ".explanation_no_grading_html" %> + <% else %> + <%= t ".explanation_yes_grading_html" %> + <% end %> +
+
+

<%= t '.deadline_html', users: @evaluation.users.count, exercises: @evaluation.exercises.count, deadline: l(@evaluation.deadline, format: :submission) %>

+ <%= render partial: 'exercises_progress_table', locals: { metadata: @evaluation.metadata, series: @evaluation.series } %>
diff --git a/app/views/submissions/index.html.erb b/app/views/submissions/index.html.erb index 78f60b1186..b099b00014 100644 --- a/app/views/submissions/index.html.erb +++ b/app/views/submissions/index.html.erb @@ -1,6 +1,8 @@ <%= render 'activities/navbar_links' if @activity %> <%= render 'courses/navbar_links' if @course && !@activity %> -<%= javascript_include_tag 'dolos' %> +<%= content_for :javascripts do %> + <%= javascript_include_tag 'dolos' %> +<% end %>
@@ -43,10 +45,17 @@ <% if current_user&.course_admin?(@course) %> <% actions << {icon: 'replay', text: t(".reevaluate_submissions"), confirm: t(".confirm_reevaluate_submissions"), action: mass_rejudge_submissions_path(user_id: @user&.id, activity_id: @activity&.id, course_id: @course&.id, series_id: @series&.id, judge_id: @judge&.id)} if policy(Submission).mass_rejudge? - actions << {icon: 'graph-outline', text: t('.detect_plagiarism'), js: "window.dodona.startDolos(\"#{series_exports_path(@series, token: (@series.access_token if @series.hidden?), selected_ids: [@activity.id])}\")", id: "dolos-btn" } if @series && @activity options << {label: t('.most_recent'), param: 'most_recent_per_user'} if @activity options << {label: t('.watch_submissions'), param: 'refresh'} %> + <% if @series.present? %> + <% actions << {icon: 'graph-outline', text: t('.detect_plagiarism'), id: "dolos-btn" } if @series && @activity %> + + <% end %> <% end %> <%= render partial: 'layouts/searchbar', locals: {actions: actions, options: options, course_labels: @course_labels, statuses: Submission.statuses.keys, refresh_element: "#refresh_element"} %>
diff --git a/config/locales/js/nl.yml b/config/locales/js/nl.yml index 049d6bab20..d7c4788906 100644 --- a/config/locales/js/nl.yml +++ b/config/locales/js/nl.yml @@ -333,5 +333,5 @@ nl: draft: Concept popularity: Populariteit dolos: - view_report: Plagiaat bekijken + view_report: Rapport bekijken diff --git a/config/locales/views/evaluations/en.yml b/config/locales/views/evaluations/en.yml index c0a617f9ac..7492003532 100644 --- a/config/locales/views/evaluations/en.yml +++ b/config/locales/views/evaluations/en.yml @@ -5,8 +5,8 @@ en: evaluation: "Evaluation" title: "Evaluation for %{series}" explanation_title: "How do I evaluate a submission?" - explanation_no_grading_html: "

In the summary table, you can click on the icon next to an exercise. You will then be taken to an incompleted submission of a random student. If you wish, you can leave feedback on the code and mark the submission as completed. Note that you can also mark a submission as completed without leaving feedback. To get started, we already marked all exercises without submissions as completed.

You can find more information in our documentation.

As a reminder, students will not be able to see the feedback you have given until you click the 'release feedback' button below.

" - explanation_yes_grading_html: "

In the summary table, you can click on the icon next to an exercise. You will then be taken to an incomplete submission of a random student. If you wish, you can leave feedback on the code and assign scores using the score items you configured. Note that you can also assign scores without leaving feedback. You can not give scores to exercises without submissions, these are automatically assigned a 0 in the grade overview.

You can find information in our documentation.

As a reminder, students will not be able to see the feedback and scores until you click the 'Release feedback' button below.

" + explanation_no_grading_html: "

In the summary table, you can click on the 'Evaluate' button next to an exercise. You will then be taken to an incompleted submission of a random student. If you wish, you can leave feedback on the code and mark the submission as completed. Note that you can also mark a submission as completed without leaving feedback. To get started, we already marked all exercises without submissions as completed.

You can find more information in our documentation.

As a reminder, students will not be able to see the feedback you have given until you click the 'release feedback' button below.

" + explanation_yes_grading_html: "

In the summary table, you can click on the 'Evaluate' button next to an exercise. You will then be taken to an incomplete submission of a random student. If you wish, you can leave feedback on the code and assign scores using the score items you configured. Note that you can also assign scores without leaving feedback. You can not give scores to exercises without submissions, these are automatically assigned a 0 in the grade overview.

You can find information in our documentation.

As a reminder, students will not be able to see the feedback and scores until you click the 'Release feedback' button below.

" release: Release feedback unrelease: Unrelease feedback deadline_html: This evaluation of %{users} students contains the submissions of %{exercises} exercises with %{deadline} as a deadline. @@ -96,3 +96,6 @@ en: remove_user_consequences: "If this student already received feedback, that feedback will be deleted. Are you sure you want to remove this student?" user_progress: not_submitted: Not submitted + exercises_progress_table: + evaluate: Evaluate + evaluated: Evaluated diff --git a/config/locales/views/evaluations/nl.yml b/config/locales/views/evaluations/nl.yml index 360b2e5958..fd5108e350 100644 --- a/config/locales/views/evaluations/nl.yml +++ b/config/locales/views/evaluations/nl.yml @@ -5,8 +5,8 @@ nl: evaluation: "Evaluatie" title: "Evaluatie voor %{series}" explanation_title: "Hoe evalueer ik een oplossing?" - explanation_no_grading_html: "

In de samenvattende tabel kan je naast een oefening op klikken. Je komt dan op een nog te bekijken oplossing van een willekeurige student terecht. Vervolgens kan je indien gewenst feedback op de code geven en de oplossing als afgewerkt markeren. Bemerk dat je ook een oplossing als afgewerkt kan markeren zonder feedback achter te laten. We hebben alvast alle oefeningen zonder ingediende oplossing als afgewerkt gemarkeerd.

Meer informatie kan je in onze documentatie vinden.

Ter herinnering, de studenten krijgen de gegeven feedback niet automatisch te zien, daarvoor dien je hieronder op 'Feedback vrijgeven' te klikken.

" - explanation_yes_grading_html: "

In de samenvattende tabel kan je naast een oefening op klikken. Je komt dan op een nog te bekijken oplossing van een willekeurige student terecht. Vervolgens kan je indien gewenst feedback op de code geven en scores toekennen via de scoreonderdelen die je geconfigureerd hebt. Je kan de scores ook invullen zonder feedback achter te laten. Oefeningen zonder ingediende oplossing kan je geen scores geven, deze zullen automatisch een 0 krijgen in het puntenoverzicht.

Meer informatie kan je in onze documentatie vinden.

Ter herinnering, de studenten krijgen de gegeven feedback en scores niet automatisch te zien, daarvoor dien je hieronder op 'Feedback vrijgeven' te klikken.

" + explanation_no_grading_html: "

In de samenvattende tabel kan je naast een oefening op 'Evalueren' klikken. Je komt dan op een nog te bekijken oplossing van een willekeurige student terecht. Vervolgens kan je indien gewenst feedback op de code geven en de oplossing als afgewerkt markeren. Bemerk dat je ook een oplossing als afgewerkt kan markeren zonder feedback achter te laten. We hebben alvast alle oefeningen zonder ingediende oplossing als afgewerkt gemarkeerd.

Meer informatie kan je in onze documentatie vinden.

Ter herinnering, de studenten krijgen de gegeven feedback niet automatisch te zien, daarvoor dien je hieronder op 'Feedback vrijgeven' te klikken.

" + explanation_yes_grading_html: "

In de samenvattende tabel kan je naast een oefening op 'Evalueren' klikken. Je komt dan op een nog te bekijken oplossing van een willekeurige student terecht. Vervolgens kan je indien gewenst feedback op de code geven en scores toekennen via de scoreonderdelen die je geconfigureerd hebt. Je kan de scores ook invullen zonder feedback achter te laten. Oefeningen zonder ingediende oplossing kan je geen scores geven, deze zullen automatisch een 0 krijgen in het puntenoverzicht.

Meer informatie kan je in onze documentatie vinden.

Ter herinnering, de studenten krijgen de gegeven feedback en scores niet automatisch te zien, daarvoor dien je hieronder op 'Feedback vrijgeven' te klikken.

" users: "studenten" release: Feedback vrijgeven unrelease: Feedback verbergen @@ -96,3 +96,6 @@ nl: remove_user_consequences: "Als deze student al feedback gekregen had, dan zal deze feedback verwijderd worden. Ben je zeker dat je deze student wil verwijderen?" user_progress: not_submitted: Niet ingediend + exercises_progress_table: + evaluate: Evalueren + evaluated: Geƫvalueerd
<%= t "activities.index.activity_title" %> <%= t "evaluations.show.evaluation_progress" %>
+ <% if meta[:next_incomplete_feedback].present? %> - <%= link_to meta[:next_incomplete_feedback], title: t('evaluations.show.next_incomplete_feedback'), class: 'btn btn-icon' do %> - + <%= link_to meta[:next_incomplete_feedback], title: t('evaluations.show.next_incomplete_feedback'), class: 'btn btn-outline with-icon' do %> + + <%= t(".evaluate") %> <% end %> <% else %> - + + + <%= t(".evaluated") %> + <% end %> + + + <%= t "submissions.index.detect_plagiarism" %> + +