Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to assignment upload to fix exisitng bugs and add new features (Part 1) #82

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,9 @@ end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

# to set timeout
gem 'timeout'

# to access rails variables in javascript
gem 'gon'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add nl?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have completed the change.

11 changes: 11 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ GEM
ffi (1.15.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
gon (6.4.0)
actionpack (>= 3.0.20)
i18n (>= 0.7)
multi_json
request_store (>= 1.0)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
jbuilder (2.11.2)
Expand All @@ -105,6 +110,7 @@ GEM
mini_mime (1.1.0)
minitest (5.14.4)
msgpack (1.4.2)
multi_json (1.15.0)
mysql2 (0.5.3)
nio4r (2.5.7)
nokogiri (1.11.4-x86_64-linux)
Expand Down Expand Up @@ -144,6 +150,8 @@ GEM
thor (>= 0.20.3, < 2.0)
rake (13.0.3)
regexp_parser (2.1.1)
request_store (1.5.0)
rack (>= 1.4)
rubyzip (2.3.0)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
Expand All @@ -168,6 +176,7 @@ GEM
thor (1.1.0)
thread_safe (0.3.6)
tilt (2.0.10)
timeout (0.1.1)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
Expand Down Expand Up @@ -202,6 +211,7 @@ DEPENDENCIES
byebug
capybara (>= 2.15)
coffee-rails (~> 5.0)
gon
jbuilder (~> 2.7)
jquery-rails (~> 4.4)
mysql2 (~> 0.5.3)
Expand All @@ -211,6 +221,7 @@ DEPENDENCIES
sass-rails (~> 6.0)
selenium-webdriver
sprockets-rails
timeout
turbolinks (~> 5)
tzinfo-data
uglifier (~> 4.2)
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/submission_similarities.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,4 @@ body.submission_similarities_show {
}
}
}
}
}
374 changes: 304 additions & 70 deletions app/controllers/assignments_controller.rb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion app/mailers/application_mailer.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com'
default from: ''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a default configurable address? Is this line necessary?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems related to L4-5 in app/mailers/assignment_mailer.rb

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will address this in the next PR which will include progress bar, assignment timeout and email functionalites.

layout 'mailer'
end
9 changes: 9 additions & 0 deletions app/mailers/assignment_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AssignmentMailer < ApplicationMailer

def assignment_email
@email_title = params[:email_title]
mail(to: '', subject: email_title)
end

end

10 changes: 10 additions & 0 deletions app/models/assignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Assignment < ActiveRecord::Base
has_many :confirmed_or_suspected_plagiarism_cases, -> { where("status = #{SubmissionSimilarity::STATUS_CONFIRMED_AS_PLAGIARISM} OR status = #{SubmissionSimilarity::STATUS_SUSPECTED_AS_PLAGIARISM}") }, class_name: "SubmissionSimilarity"
has_many :confirmed_plagiarism_cases, -> { where("status = #{SubmissionSimilarity::STATUS_CONFIRMED_AS_PLAGIARISM}") }, class_name: "SubmissionSimilarity"
has_many :submissions

belongs_to :course

validates :title, :language, :min_match_length, :ngram_size, presence: true
Expand Down Expand Up @@ -51,10 +52,19 @@ class Assignment < ActiveRecord::Base
ocaml: "ocaml"
}

FILE_STRUCTURES = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment as to what this FILE_STRUCTURES enum is doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added some comments.

all: "By Files",
dir: "By Directory"
}

def self.options_for_languages
LANGUAGES.to_a.collect { |pair| pair.reverse }
end

def self.options_for_file_structure
FILE_STRUCTURES.to_a.collect { |pair| pair.reverse }
end

def prettify_js_lang
"lang-#{PRETTIFY_LANGUAGES[self.language]}"
end
Expand Down
10 changes: 10 additions & 0 deletions app/views/assignment_mailer/assignment_email.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
</head>
<body>
<p> The assignment you uploaded has completed processing. </p>
<p> Please view the outcome at <%= @assignment_url %></p>
</body>
</html>
54 changes: 44 additions & 10 deletions app/views/assignments/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</ul>
</div>
<% end %>
<p> Hover over ⓘ for more details about any terminology.</p>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
Expand All @@ -18,40 +19,73 @@
<%= f.select :language, Assignment.options_for_languages %>
</div>
<div>
<%= f.label :min_match_length, "Minimum Match Length [?]" , title: "For Java, only '.java' files will be processed." %>
<%= f.label :min_match_length, "Minimum Match Length \u24D8" , title: "The least number of contiguous identical statements required to flag a match." %>
<%= f.text_field :min_match_length %>
</div>
<div>
<%= f.label :ngram_size, "Size of n-gram [?]", title: "An n-gram is a contiguous subsequence of n tokens of a given sequence." %>
<%= f.label :ngram_size, "Size of n-gram \u24D8", title: "An n-gram is a contiguous subsequence of n tokens of a given sequence." %>
<%= f.text_field :ngram_size %>
</div>
<div>
<%= f.label :file, "Student submissions (zip) [?]", title: "The zip file should contain one folder for each student's code files; folders are named according to student ID; Name your folder as \"skeleton\" for the codes you wish to exclude from the system" %>
<%= f.label :file_structure, "File Structure \u24D8", title: "This refers to the way the files are arranged in your zip folder. \n\nTo recursively search for all files in the provided directories and compare every file with each other file, select \"By Files\". \n\nInstead, if you don't want to compare files in the same leaf directory (for example, if the zip file is split into per-student directories and you don't care about self plagiarism, select \"By Directory\"" %>
<%= f.select :file_structure, Assignment.options_for_file_structure %>
</div>
<div>
<%= f.label :file, "Student submissions (zip) \u24D8", title: "The zip file should contain files according to the way you selected the \"File Structure \" option above. \n\nIf you selected \"By Files\", then, all the files in the zip folder will be compared with each other. Name your file as \"skeleton \" for the codes you wish to exclude from the system \n\nIf you selected the other option (\"By Directory\"), ensure that the zip file contains one folder for each student's code files. Ensure that the folders are named according to student ID and name your folder as \"skeleton\" for the codes you wish to exclude from the system" %>
<%= f.file_field :file %>
</div>


<div>
<%= f.label :mapbox, "Upload map file?" %>
<%= f.label :mapbox, "Add more options? \u24D8", title: "Check the box to include additional optional options"%>
<%= f.check_box :mapbox,{}, "Yes", "No"%>
</div>
<div id="map_file_details" style="display:none;">
<%= f.label :mapfile, "Mapping file (csv) [?]", title: "The zip file should contain one folder for each student's code files; folders are named according to student ID; Name your folder as \"skeleton\" for the codes you wish to exclude from the system" %>
<%= f.label :mapfile, "Mapping file (csv) \u24D8", title: "If you have a map file that maps the filenames to other attributes such as student's names, github handle etc, upload the file here. Ensure that the file is a csv file and follows the conventions according to the user guide." %>
<%= f.file_field :mapfile, accept: '.csv' %>

</div>
<div id="file_to_ignore_details" style="display:none;">
<%= f.label :filesIgnore, "Files to be ignored \u24D8" , title: "If you want to ignore certain files from the code zip file that you uploaded, add those filenmes under here. \n\nIf you want to ignore filenames that exactly match a certain filename, enter the filename as it is. For instance, entering \"question1\", will mean that all the filenames that are named as \"question1\" (irrespective of their file extensions) will all be ignored. \n\nIf you want to ignore filenames that partially contain a substring, add \"/ \" in front of the filename. For instance, if you enter, \"/question1\", filenames such as \"alice_question1.py\", \"bob_question1Test.c\", \"question1_charlie.java\" will all get ignored. \n\nIf you need to add muliple regex, leave a space in between them. For instance, if you enter \"question1 /question2 \", filesnames that either exactly match \"question1 \" or contain the substring \"question2 \" will all get ignored" %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggested defaults? .pdf .doc(x)? .bin .ps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to ignore code files. For non code files like pdf and doc files, these files will naturally get ignored without any users' input.

<%= f.text_field :filesIgnore %>
</div>
<div id="file_to_process_details" style="display:none;">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May need to be clear which of ignore or to process has precedence. Should be ordered as such too (overriding one listed first).

<%= f.label :filesProcess, "Files to be only processed \u24D8" , title: "If you want to process and compare only certain files from the code zip file that you uploaded, add those filenmes under here. \n\nIf you want to process filenames that exactly match a certain filename, enter the filename as it is. For instance, entering \"question1\", will mean that all the filenames that are named as \"question1\" (irrespective of their file extensions) will all be processed and the rest will be ignored. \n\nIf you want to only process filenames that partially contain a substring, add \"/ \" in front of the filename. For instance, if you enter, \"/question1\", filenames such as \"alice_question1.py\", \"bob_question1Test.c\", \"question1_charlie.java\" will all get processed and the rest that does not contain the substring will all get ignored. \n\nIf you need to add muliple regex, leave a space in between them. For instance, if you enter \"question1 /question2 \", filesnames that either exactly match \"question1 \" or contain the substring \"question2 \" will all get processed." %>
<%= f.text_field :filesProcess %>
</div>
<div id="ref_dir_details" style="display:none;">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will need to point to documentation about how this works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will create an issue to update user guide so that I can add documentation for all newly added changes.

<%= f.label :refDir, "Reference Directory (zip) \u24D8", title: "If you have additional files that you want to compare, but do not want to check for plagiarism (for example, if you want to also compare submissions from previous semesters), zip those files and upload it here." %>
<%= f.file_field :refDir %>
</div>
<div id="email_details" style="display:none;">
<%= f.label :email, "Send email upon completion \u24D8" , title: "If you want to receive an email to notify you after the assignment has been processed, enter the email here. Ensure that you enter a valid email." %>
<%= f.text_field :email %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fill in with user's default email, to be edited?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, I have changed it accordingly.

</div>
<div>
<%= f.submit class: "submit" %>
<%= f.submit 'Create Assignment', name: 'create' %>
<%= f.submit 'Preview Uploaded Code Files', name: 'preview' %>
</div>
<% end %>

<script type="text/javascript">
var checkbox = document.getElementById('assignment_mapbox');
var details_div = document.getElementById('map_file_details');
var map_details_div = document.getElementById('map_file_details');
var files_ignore_details_div = document.getElementById('file_to_ignore_details');
var files_process_details_div = document.getElementById('file_to_process_details');
var ref_dir_details_div = document.getElementById('ref_dir_details');
var email_details_div = document.getElementById('email_details');
checkbox.onchange = function() {
if(this.checked) {
details_div.style['display'] = 'block';
map_details_div.style['display'] = 'block';
files_ignore_details_div.style['display'] = 'block';
files_process_details_div.style['display'] = 'block';
ref_dir_details_div.style['display'] = 'block';
email_details.style['display'] = 'block';
} else {
details_div.style['display'] = 'none';
map_details_div.style['display'] = 'none';
files_ignore_details_div.style['display'] = 'none';
files_process_details_div.style['display'] = 'none';
ref_dir_details_div.style['display'] = 'none';
email_details.style['display'] = 'none';
}
};
</script>
Expand Down
167 changes: 167 additions & 0 deletions app/views/assignments/preview_files.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>jQuery UI Dialog - Modal message</title>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix title, even if not shown.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the title accordingly.

<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<style>
h1 { font-size: 1.2em; margin: .6em 0; }
div#users-contain { width: 600px; margin: 20px 0; }
div#users-contain table { margin: 1em 0; border-collapse: collapse; width: 100%; }
div#users-contain table td, div#users-contain table th { border: 1px solid #eee; padding: .6em 10px; text-align: left; }
.ui-dialog .ui-state-error { padding: .3em; }
.validateTips { border: 1px solid transparent; padding: 0.3em; }
</style>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$( function() {
$( "#dialog-message" ).dialog({
modal: true,
height: 700,
width: 700,
buttons: {
'Reupload New Code Zip File': function() {
$( this ).dialog( "close" );
},
'Create Assignment': function() {
$("#form").submit();
}
}
});

var cal = document.getElementById('myHeader').innerText;
console.log(cal)

$(".ui-dialog-buttonpane button:contains('Create Assignment')")
.button((cal == "SSID cannot process the assignment due to empty/insufficient files to compare") ? "disable" : "enable");


} );
</script>
</head>
<body>

<h2>Assignment title: <%= @assignment.title %></h2>

<pre>
Language: <%= @assignment.language%>
Min Match Length: <%= @assignment.min_match_length%>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested defaults and \u24D8 for this line and next?

NGram Size: <%= @assignment.ngram_size%>
</pre>

<% if @assignment.errors.any? %>
<div id="error_explanation">
<h3><%= pluralize(@assignment.errors.count, "error") %> prohibited this assignment from being saved:</h3>
<ul>
<% @assignment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

<%= form_for([@course, @assignment], :id => 'form') do |f| %>
<div>
<%= f.label :file_structure, "File Structure \u24D8", title: "This refers to the way the files are arranged in your zip folder. \n\nTo recursively search for all files in the provided directories and compare every file with each other file, select \"By Files\". \n\nInstead, if you don't want to compare files in the same leaf directory (for example, if the zip file is split into per-student directories and you don't care about self plagiarism, select \"By Directory\"" %>
<%= f.select :file_structure, Assignment.options_for_file_structure %>
</div>
<div>
<%= f.label :file, "Student submissions (zip) [?]", title: "The zip file should contain one folder for each student's code files; folders are named according to student ID; Name your folder as \"skeleton\" for the codes you wish to exclude from the system" %>
<%= f.file_field :file %>
</div>

<div>
<%= f.label :mapbox, "Add more options? \u24D8", title: "Check the box to include additional optional options"%>
<%= f.check_box :mapbox,{}, "Yes", "No"%>
</div>
<div id="map_file_details" style="display:none;">
<%= f.label :mapfile, "Mapping file (csv) \u24D8", title: "If you have a map file that maps the filenames to other attributes such as student's names, github handle etc, upload the file here. Ensure that the file is a csv file and follows the conventions according to the user guide." %>
<%= f.file_field :mapfile, accept: '.csv' %>
</div>
<div id="file_to_ignore_details" style="display:none;">
<%= f.label :filesIgnore, "Files to be ignored \u24D8" , title: "If you want to ignore certain files from the code zip file that you uploaded, add those filenmes under here. \n\nIf you want to ignore filenames that exactly match a certain filename, enter the filename as it is. For instance, entering \"question1\", will mean that all the filenames that are named as \"question1\" (irrespective of their file extensions) will all be ignored. \n\nIf you want to ignore filenames that partially contain a substring, add \"/ \" in front of the filename. For instance, if you enter, \"/question1\", filenames such as \"alice_question1.py\", \"bob_question1Test.c\", \"question1_charlie.java\" will all get ignored. \n\nIf you need to add muliple regex, leave a space in between them. For instance, if you enter \"question1 /question2 \", filesnames that either exactly match \"question1 \" or contain the substring \"question2 \" will all get ignored" %>
<%= f.text_field :filesIgnore, {:value => @assignment.filesIgnore} %>
</div>
<div id="file_to_process_details" style="display:none;">
<%= f.label :filesProcess, "Files to be only processed \u24D8" , title: "If you want to process and compare only certain files from the code zip file that you uploaded, add those filenmes under here. \n\nIf you want to process filenames that exactly match a certain filename, enter the filename as it is. For instance, entering \"question1\", will mean that all the filenames that are named as \"question1\" (irrespective of their file extensions) will all be processed and the rest will be ignored. \n\nIf you want to only process filenames that partially contain a substring, add \"/ \" in front of the filename. For instance, if you enter, \"/question1\", filenames such as \"alice_question1.py\", \"bob_question1Test.c\", \"question1_charlie.java\" will all get processed and the rest that does not contain the substring will all get ignored. \n\nIf you need to add muliple regex, leave a space in between them. For instance, if you enter \"question1 /question2 \", filesnames that either exactly match \"question1 \" or contain the substring \"question2 \" will all get processed." %>
<%= f.text_field :filesProcess, {:value => @assignment.filesProcess} %>
</div>
<div id="ref_dir_details" style="display:none;">
<%= f.label :refDir, "Reference Directory (zip) \u24D8", title: "If you have additional files that you want to compare, but do not want to check for plagiarism (for example, if you want to also compare submissions from previous semesters), zip those files and upload it here." %>
<%= f.file_field :refDir %>
</div>
<div id="email_details" style="display:none;">
<%= f.label :email, "Send email upon completion \u24D8" , title: "If you want to receive an email to notify you after the assignment has been processed, enter the email here. Ensure that you enter a valid email." %>
<%= f.text_field :email, {:value => @assignment.email} %>
</div>
<div>
<%= f.submit 'Create Assignment', name: 'create' %>
<%= f.submit 'Preview Uploaded Code Files', name: 'preview' %>
</div>
<% end %>

<script type="text/javascript">
var checkbox = document.getElementById('assignment_mapbox');
var map_details_div = document.getElementById('map_file_details');
var files_ignore_details_div = document.getElementById('file_to_ignore_details');
var files_process_details_div = document.getElementById('file_to_process_details');
var ref_dir_details_div = document.getElementById('ref_dir_details');
var email_details_div = document.getElementById('email_details');
checkbox.onchange = function() {
if(this.checked) {
map_details_div.style['display'] = 'block';
files_ignore_details_div.style['display'] = 'block';
files_process_details_div.style['display'] = 'block';
ref_dir_details_div.style['display'] = 'block';
email_details.style['display'] = 'block';
} else {
map_details_div.style['display'] = 'none';
files_ignore_details_div.style['display'] = 'none';
files_process_details_div.style['display'] = 'none';
ref_dir_details_div.style['display'] = 'none';
email_details.style['display'] = 'none';
}
};
</script>




<div id="dialog-message" title="Uploaded File Preview">
<div id="users-contain" class="ui-widget">
<h1 id="myHeader"> <%= @statusStatement %> </h1>
<h1 id="myHeader2"> <%= @previewStatement %> </h1>
<p>
Under "File Structure", if you have clicked "By Directory", please ensure that the zip file contains 1 root folder for each students' code. Otherwise, if you want to recursively search and compare the files with each other, please click "By Files" under "File Drectory"
</p>
<table id="users" class="ui-widget ui-widget-content">
<thead>
<tr class="ui-widget-header ">
<th> <%= @column1Title %> </th>
<th> <%= @column2Title %> </th>
</tr>
</thead>
<tbody>
<% @dirHash.each { |key, value| %>
<tr>
<td><%= key %></td>
<td>
<ul>
<% value.each do |val| %>
<li><%= val %></li>
<% end %>
</ul>
</td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>


</body>
</html>

Loading