diff --git a/tutor/.gitignore b/tutor/.gitignore
index 5830e151..0cf0cc9a 100644
--- a/tutor/.gitignore
+++ b/tutor/.gitignore
@@ -20,7 +20,6 @@
/public/uploads/*
*.rbc
capybara-*.html
-.rspec
/coverage/
/spec/tmp
**.orig
@@ -40,4 +39,5 @@ models.svg
# Ignoring .java and .class files
/students_solutions/Java/*.java
-/students_solutions/Class/*.class
\ No newline at end of file
+/students_solutions/Class/*.class
+/students_solutions/*
diff --git a/tutor/Gemfile b/tutor/Gemfile
index 09df078a..268ec354 100644
--- a/tutor/Gemfile
+++ b/tutor/Gemfile
@@ -62,6 +62,8 @@ gem 'foreman'
# Use composite primary keys in models
gem 'composite_primary_keys', '~> 6.0.1'
+gem 'activeadmin', github: 'gregbell/active_admin'
+
#
gem 'acts_as_list'
@@ -72,6 +74,9 @@ gem 'json_builder', '3.1.0'
# whenever gem for scheduling tasks
gem 'whenever', :require => false
+#pagination
+gem 'will_paginate', '~> 3.0.5'
+
group :development, :test do
gem 'rspec-rails', '~> 3.0.0.beta'
end
diff --git a/tutor/Gemfile.lock b/tutor/Gemfile.lock
index e2362805..66c415e6 100644
--- a/tutor/Gemfile.lock
+++ b/tutor/Gemfile.lock
@@ -1,3 +1,20 @@
+GIT
+ remote: git://github.com/gregbell/active_admin.git
+ revision: 109ab88cb7c0777d3460cbfac3cc217068ecfabf
+ specs:
+ activeadmin (1.0.0.pre)
+ arbre (~> 1.0)
+ bourbon
+ coffee-rails
+ formtastic (~> 2.3.0.rc3)
+ inherited_resources (~> 1.3)
+ jquery-rails
+ jquery-ui-rails
+ kaminari (~> 0.15)
+ rails (>= 3.2, < 4.2)
+ ransack (~> 1.0)
+ sass-rails
+
GEM
remote: https://rubygems.org/
specs:
@@ -28,8 +45,13 @@ GEM
acts_as_list (0.4.0)
activerecord (>= 3.0)
ansi (1.4.3)
+ arbre (1.0.1)
+ activesupport (>= 3.0.0)
arel (4.0.2)
bcrypt (3.1.7)
+ bourbon (3.2.1)
+ sass (~> 3.2)
+ thor
builder (3.1.4)
carrierwave (0.10.0)
activemodel (>= 3.2.0)
@@ -60,19 +82,35 @@ GEM
foreman (0.63.0)
dotenv (>= 0.7)
thor (>= 0.13.6)
+<<<<<<< HEAD
googlecharts (1.6.8)
+=======
+ formtastic (2.3.0.rc3)
+ actionpack (>= 3.0)
+ has_scope (0.6.0.rc)
+ actionpack (>= 3.2, < 5)
+ activesupport (>= 3.2, < 5)
+>>>>>>> a5626aae62dca494a449268dc151db45acefa5e9
hashr (0.0.22)
hike (1.2.3)
i18n (0.6.9)
+ inherited_resources (1.4.1)
+ has_scope (~> 0.6.0.rc)
+ responders (~> 1.0.0.rc)
jbuilder (1.5.3)
activesupport (>= 3.0.0)
multi_json (>= 1.2.0)
jquery-rails (3.1.0)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
+ jquery-ui-rails (4.2.1)
+ railties (>= 3.2.16)
json (1.8.1)
json_builder (3.1.0)
activesupport (>= 2.0.0)
+ kaminari (0.15.1)
+ actionpack (>= 3.0.0)
+ activesupport (>= 3.0.0)
kgio (2.9.2)
mail (2.5.4)
mime-types (~> 1.16)
@@ -81,6 +119,8 @@ GEM
minitest (4.7.5)
multi_json (1.9.2)
orm_adapter (0.5.0)
+ polyamorous (1.0.0)
+ activerecord (>= 3.0)
polyglot (0.3.4)
rack (1.5.2)
rack-test (0.6.2)
@@ -100,8 +140,16 @@ GEM
thor (>= 0.18.1, < 2.0)
raindrops (0.13.0)
rake (10.3.1)
+ ransack (1.2.3)
+ actionpack (>= 3.0)
+ activerecord (>= 3.0)
+ activesupport (>= 3.0)
+ i18n
+ polyamorous (~> 1.0.0)
rdoc (4.1.1)
json (~> 1.4)
+ responders (1.0.0)
+ railties (>= 3.2, < 5)
rest-client (1.6.7)
mime-types (>= 1.16)
rmagick (2.13.2)
@@ -176,6 +224,7 @@ PLATFORMS
ruby
DEPENDENCIES
+ activeadmin!
acts_as_list
carrierwave
chartkick
diff --git a/tutor/app/admin/admin_user.rb b/tutor/app/admin/admin_user.rb
new file mode 100644
index 00000000..433be5d7
--- /dev/null
+++ b/tutor/app/admin/admin_user.rb
@@ -0,0 +1,28 @@
+ActiveAdmin.register AdminUser do
+ permit_params :email, :password, :password_confirmation
+
+ index do
+ selectable_column
+ id_column
+ column :email
+ column :current_sign_in_at
+ column :sign_in_count
+ column :created_at
+ actions
+ end
+
+ filter :email
+ filter :current_sign_in_at
+ filter :sign_in_count
+ filter :created_at
+
+ form do |f|
+ f.inputs "Admin Details" do
+ f.input :email
+ f.input :password
+ f.input :password_confirmation
+ end
+ f.actions
+ end
+
+end
diff --git a/tutor/app/admin/dashboard.rb b/tutor/app/admin/dashboard.rb
new file mode 100644
index 00000000..eae7baf0
--- /dev/null
+++ b/tutor/app/admin/dashboard.rb
@@ -0,0 +1,33 @@
+ActiveAdmin.register_page "Dashboard" do
+
+ menu priority: 1, label: proc{ I18n.t("active_admin.dashboard") }
+
+ content title: proc{ I18n.t("active_admin.dashboard") } do
+ div class: "blank_slate_container", id: "dashboard_default_message" do
+ span class: "blank_slate" do
+ span I18n.t("active_admin.dashboard_welcome.welcome")
+ small I18n.t("active_admin.dashboard_welcome.call_to_action")
+ end
+ end
+
+ # Here is an example of a simple dashboard with columns and panels.
+ #
+ # columns do
+ # column do
+ # panel "Recent Posts" do
+ # ul do
+ # Post.recent(5).map do |post|
+ # li link_to(post.title, admin_post_path(post))
+ # end
+ # end
+ # end
+ # end
+
+ # column do
+ # panel "Info" do
+ # para "Welcome to ActiveAdmin."
+ # end
+ # end
+ # end
+ end # content
+end
diff --git a/tutor/app/assets/images/note.png b/tutor/app/assets/images/note.png
new file mode 100644
index 00000000..453fad5c
Binary files /dev/null and b/tutor/app/assets/images/note.png differ
diff --git a/tutor/app/assets/images/tick.png b/tutor/app/assets/images/tick.png
new file mode 100644
index 00000000..481b90a8
Binary files /dev/null and b/tutor/app/assets/images/tick.png differ
diff --git a/tutor/app/assets/javascripts/_solutions.js b/tutor/app/assets/javascripts/_solutions.js
new file mode 100644
index 00000000..1f174be2
--- /dev/null
+++ b/tutor/app/assets/javascripts/_solutions.js
@@ -0,0 +1,81 @@
+// [Editor cusomization - Story 3.19]
+// Enable method "selectpicker" from the javascript file.
+// Parameters: none.
+// Returns: none.
+// Author: Mimi
+$(document).ready(function(e){
+ $('.selectpicker').selectpicker();
+});
+
+// [Editor cusomization - Story 3.19]
+// change the theme of the editor.
+// Parameters:
+// The selected input from the 'theme' drop-downlist.
+// Returns: none.
+// Author: Mimi
+function changeTheme(){
+ var new_theme = $('#theme').val();
+ editor.setTheme("ace/theme/"+new_theme);
+}
+
+// [Editor cusomization - Story 3.19]
+// change the mode of the editor.
+// Parameters:
+// The selected input from the 'mode' drop-downlist.
+// Returns: none.
+// Author: Mimi
+function changeMode(){
+ var new_mode = $('#mode').val();
+ edit_session.setMode("ace/mode/"+new_mode);
+}
+
+// [Editor cusomization - Story 3.19]
+// change the font-size of the editor.
+// Parameters:
+// The selected input from the 'font' drop-downlist.
+// Returns: none.
+// Author: Mimi
+function changeFont(){
+ var new_font = $('#font').val();
+ editor.setFontSize(parseInt(new_font));
+}
+
+// [Mark Solution - Story 4.29]
+// shows the add note button when hovering over the the line
+// Parameters:
+// id: id of the div that was hovered over
+// Returns: none
+// Author: Abdullrahman Elhusseny
+function show(id){
+ id += "add_button"
+ document.getElementById(id).style.opacity = 1;
+}
+
+// [Mark Solution - Story 4.29]
+// hides the add note button when the crusor leaves the the line
+// Parameters:
+// id: id of the div that was hovered over
+// Returns: none
+// Author: Abdullrahman Elhusseny
+function hide(id){
+ id += "add_button"
+ document.getElementById(id).style.opacity = 0;
+}
+
+// [Mark Solution - Story 4.29]
+// pops a dialog box when trying to add or update note
+// Parameters:
+// id: id of the button that was pressed
+// Returns: none
+// Author: Abdullrahman Elhusseny
+function pop(id){
+ id += "form"
+ document.getElementById(id).style.display = 'initial'
+ $(document.getElementById(id)).bPopup({
+ modalClose: false,
+ opacity: 0.6,
+ positionStyle: 'fixed',
+ speed: 650,
+ transition: 'slideIn'
+ });
+}
\ No newline at end of file
diff --git a/tutor/app/assets/javascripts/application.js b/tutor/app/assets/javascripts/application.js
index ed3af524..dbe4160e 100644
--- a/tutor/app/assets/javascripts/application.js
+++ b/tutor/app/assets/javascripts/application.js
@@ -12,6 +12,19 @@
//
//= require jquery
//= require jquery_ujs
+//= require jquery-ui-1.10.4
//= require_tree .
+//= require bootstrap
//= require jquery.tokeninput
-//= require utilities
\ No newline at end of file
+//= require utilities
+
+// [Simple Search auto-complete - Story 1.23]
+// autocomplete for the search bar
+// Parameters: search term
+// Returns: Array with the matched results
+// Author: Ahmed Elassuty
+$(function(){
+ $('#search_field').autocomplete({
+ source: $('#search_field').data("autocomplete-source")
+ });
+});
diff --git a/tutor/app/assets/javascripts/assignment_problems.js.coffee b/tutor/app/assets/javascripts/assignment_problems.js.coffee
new file mode 100644
index 00000000..ff7fdf48
--- /dev/null
+++ b/tutor/app/assets/javascripts/assignment_problems.js.coffee
@@ -0,0 +1,3 @@
+@showDiv = ->
+ ocument.getElementById("test").style.display = ""
+ return
\ No newline at end of file
diff --git a/tutor/app/assets/javascripts/assignments.js.coffee b/tutor/app/assets/javascripts/assignments.js.coffee
new file mode 100644
index 00000000..24f83d18
--- /dev/null
+++ b/tutor/app/assets/javascripts/assignments.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/tutor/app/assets/javascripts/ace.js b/tutor/app/assets/javascripts/editor/ace.js
similarity index 100%
rename from tutor/app/assets/javascripts/ace.js
rename to tutor/app/assets/javascripts/editor/ace.js
diff --git a/tutor/app/assets/javascripts/mode-java.js b/tutor/app/assets/javascripts/editor/mode-java.js
similarity index 100%
rename from tutor/app/assets/javascripts/mode-java.js
rename to tutor/app/assets/javascripts/editor/mode-java.js
diff --git a/tutor/app/assets/javascripts/editor/mode-python.js b/tutor/app/assets/javascripts/editor/mode-python.js
new file mode 100644
index 00000000..b2d02e1a
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/mode-python.js
@@ -0,0 +1,296 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/mode/python', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text', 'ace/mode/python_highlight_rules', 'ace/mode/folding/pythonic', 'ace/range'], function(require, exports, module) {
+
+
+var oop = require("../lib/oop");
+var TextMode = require("./text").Mode;
+var PythonHighlightRules = require("./python_highlight_rules").PythonHighlightRules;
+var PythonFoldMode = require("./folding/pythonic").FoldMode;
+var Range = require("../range").Range;
+
+var Mode = function() {
+ this.HighlightRules = PythonHighlightRules;
+ this.foldingRules = new PythonFoldMode("\\:");
+};
+oop.inherits(Mode, TextMode);
+
+(function() {
+
+ this.lineCommentStart = "#";
+
+ this.getNextLineIndent = function(state, line, tab) {
+ var indent = this.$getIndent(line);
+
+ var tokenizedLine = this.getTokenizer().getLineTokens(line, state);
+ var tokens = tokenizedLine.tokens;
+
+ if (tokens.length && tokens[tokens.length-1].type == "comment") {
+ return indent;
+ }
+
+ if (state == "start") {
+ var match = line.match(/^.*[\{\(\[\:]\s*$/);
+ if (match) {
+ indent += tab;
+ }
+ }
+
+ return indent;
+ };
+
+ var outdents = {
+ "pass": 1,
+ "return": 1,
+ "raise": 1,
+ "break": 1,
+ "continue": 1
+ };
+
+ this.checkOutdent = function(state, line, input) {
+ if (input !== "\r\n" && input !== "\r" && input !== "\n")
+ return false;
+
+ var tokens = this.getTokenizer().getLineTokens(line.trim(), state).tokens;
+
+ if (!tokens)
+ return false;
+ do {
+ var last = tokens.pop();
+ } while (last && (last.type == "comment" || (last.type == "text" && last.value.match(/^\s+$/))));
+
+ if (!last)
+ return false;
+
+ return (last.type == "keyword" && outdents[last.value]);
+ };
+
+ this.autoOutdent = function(state, doc, row) {
+
+ row += 1;
+ var indent = this.$getIndent(doc.getLine(row));
+ var tab = doc.getTabString();
+ if (indent.slice(-tab.length) == tab)
+ doc.remove(new Range(row, indent.length-tab.length, row, indent.length));
+ };
+
+ this.$id = "ace/mode/python";
+}).call(Mode.prototype);
+
+exports.Mode = Mode;
+});
+
+ace.define('ace/mode/python_highlight_rules', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/text_highlight_rules'], function(require, exports, module) {
+
+
+var oop = require("../lib/oop");
+var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
+
+var PythonHighlightRules = function() {
+
+ var keywords = (
+ "and|as|assert|break|class|continue|def|del|elif|else|except|exec|" +
+ "finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|" +
+ "raise|return|try|while|with|yield"
+ );
+
+ var builtinConstants = (
+ "True|False|None|NotImplemented|Ellipsis|__debug__"
+ );
+
+ var builtinFunctions = (
+ "abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|" +
+ "eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|" +
+ "binfile|iter|property|tuple|bool|filter|len|range|type|bytearray|" +
+ "float|list|raw_input|unichr|callable|format|locals|reduce|unicode|" +
+ "chr|frozenset|long|reload|vars|classmethod|getattr|map|repr|xrange|" +
+ "cmp|globals|max|reversed|zip|compile|hasattr|memoryview|round|" +
+ "__import__|complex|hash|min|set|apply|delattr|help|next|setattr|" +
+ "buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern"
+ );
+ var keywordMapper = this.createKeywordMapper({
+ "invalid.deprecated": "debugger",
+ "support.function": builtinFunctions,
+ "constant.language": builtinConstants,
+ "keyword": keywords
+ }, "identifier");
+
+ var strPre = "(?:r|u|ur|R|U|UR|Ur|uR)?";
+
+ var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))";
+ var octInteger = "(?:0[oO]?[0-7]+)";
+ var hexInteger = "(?:0[xX][\\dA-Fa-f]+)";
+ var binInteger = "(?:0[bB][01]+)";
+ var integer = "(?:" + decimalInteger + "|" + octInteger + "|" + hexInteger + "|" + binInteger + ")";
+
+ var exponent = "(?:[eE][+-]?\\d+)";
+ var fraction = "(?:\\.\\d+)";
+ var intPart = "(?:\\d+)";
+ var pointFloat = "(?:(?:" + intPart + "?" + fraction + ")|(?:" + intPart + "\\.))";
+ var exponentFloat = "(?:(?:" + pointFloat + "|" + intPart + ")" + exponent + ")";
+ var floatNumber = "(?:" + exponentFloat + "|" + pointFloat + ")";
+
+ var stringEscape = "\\\\(x[0-9A-Fa-f]{2}|[0-7]{3}|[\\\\abfnrtv'\"]|U[0-9A-Fa-f]{8}|u[0-9A-Fa-f]{4})";
+
+ this.$rules = {
+ "start" : [ {
+ token : "comment",
+ regex : "#.*$"
+ }, {
+ token : "string", // multi line """ string start
+ regex : strPre + '"{3}',
+ next : "qqstring3"
+ }, {
+ token : "string", // " string
+ regex : strPre + '"(?=.)',
+ next : "qqstring"
+ }, {
+ token : "string", // multi line ''' string start
+ regex : strPre + "'{3}",
+ next : "qstring3"
+ }, {
+ token : "string", // ' string
+ regex : strPre + "'(?=.)",
+ next : "qstring"
+ }, {
+ token : "constant.numeric", // imaginary
+ regex : "(?:" + floatNumber + "|\\d+)[jJ]\\b"
+ }, {
+ token : "constant.numeric", // float
+ regex : floatNumber
+ }, {
+ token : "constant.numeric", // long integer
+ regex : integer + "[lL]\\b"
+ }, {
+ token : "constant.numeric", // integer
+ regex : integer + "\\b"
+ }, {
+ token : keywordMapper,
+ regex : "[a-zA-Z_$][a-zA-Z0-9_$]*\\b"
+ }, {
+ token : "keyword.operator",
+ regex : "\\+|\\-|\\*|\\*\\*|\\/|\\/\\/|%|<<|>>|&|\\||\\^|~|<|>|<=|=>|==|!=|<>|="
+ }, {
+ token : "paren.lparen",
+ regex : "[\\[\\(\\{]"
+ }, {
+ token : "paren.rparen",
+ regex : "[\\]\\)\\}]"
+ }, {
+ token : "text",
+ regex : "\\s+"
+ } ],
+ "qqstring3" : [ {
+ token : "constant.language.escape",
+ regex : stringEscape
+ }, {
+ token : "string", // multi line """ string end
+ regex : '"{3}',
+ next : "start"
+ }, {
+ defaultToken : "string"
+ } ],
+ "qstring3" : [ {
+ token : "constant.language.escape",
+ regex : stringEscape
+ }, {
+ token : "string", // multi line ''' string end
+ regex : "'{3}",
+ next : "start"
+ }, {
+ defaultToken : "string"
+ } ],
+ "qqstring" : [{
+ token : "constant.language.escape",
+ regex : stringEscape
+ }, {
+ token : "string",
+ regex : "\\\\$",
+ next : "qqstring"
+ }, {
+ token : "string",
+ regex : '"|$',
+ next : "start"
+ }, {
+ defaultToken: "string"
+ }],
+ "qstring" : [{
+ token : "constant.language.escape",
+ regex : stringEscape
+ }, {
+ token : "string",
+ regex : "\\\\$",
+ next : "qstring"
+ }, {
+ token : "string",
+ regex : "'|$",
+ next : "start"
+ }, {
+ defaultToken: "string"
+ }]
+ };
+};
+
+oop.inherits(PythonHighlightRules, TextHighlightRules);
+
+exports.PythonHighlightRules = PythonHighlightRules;
+});
+
+ace.define('ace/mode/folding/pythonic', ['require', 'exports', 'module' , 'ace/lib/oop', 'ace/mode/folding/fold_mode'], function(require, exports, module) {
+
+
+var oop = require("../../lib/oop");
+var BaseFoldMode = require("./fold_mode").FoldMode;
+
+var FoldMode = exports.FoldMode = function(markers) {
+ this.foldingStartMarker = new RegExp("([\\[{])(?:\\s*)$|(" + markers + ")(?:\\s*)(?:#.*)?$");
+};
+oop.inherits(FoldMode, BaseFoldMode);
+
+(function() {
+
+ this.getFoldWidgetRange = function(session, foldStyle, row) {
+ var line = session.getLine(row);
+ var match = line.match(this.foldingStartMarker);
+ if (match) {
+ if (match[1])
+ return this.openingBracketBlock(session, match[1], row, match.index);
+ if (match[2])
+ return this.indentationBlock(session, row, match.index + match[2].length);
+ return this.indentationBlock(session, row);
+ }
+ }
+
+}).call(FoldMode.prototype);
+
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-chaos.js b/tutor/app/assets/javascripts/editor/theme-chaos.js
new file mode 100644
index 00000000..a58e6a9a
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-chaos.js
@@ -0,0 +1,181 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright 2011 Irakli Gozalishvili. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/chaos', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = true;
+exports.cssClass = "ace-chaos";
+exports.cssText = ".ace-chaos .ace_gutter {\
+background: #141414;\
+color: #595959;\
+border-right: 1px solid #282828;\
+}\
+.ace-chaos .ace_gutter-cell.ace_warning {\
+background-image: none;\
+background: #FC0;\
+border-left: none;\
+padding-left: 0;\
+color: #000;\
+}\
+.ace-chaos .ace_gutter-cell.ace_error {\
+background-position: -6px center;\
+background-image: none;\
+background: #F10;\
+border-left: none;\
+padding-left: 0;\
+color: #000;\
+}\
+.ace-chaos .ace_print-margin {\
+border-left: 1px solid #555;\
+right: 0;\
+background: #1D1D1D;\
+}\
+.ace-chaos {\
+background-color: #161616;\
+color: #E6E1DC;\
+}\
+.ace-chaos .ace_cursor {\
+border-left: 2px solid #FFFFFF;\
+}\
+.ace-chaos .ace_cursor.ace_overwrite {\
+border-left: 0px;\
+border-bottom: 1px solid #FFFFFF;\
+}\
+.ace-chaos .ace_marker-layer .ace_selection {\
+background: #494836;\
+}\
+.ace-chaos .ace_marker-layer .ace_step {\
+background: rgb(198, 219, 174);\
+}\
+.ace-chaos .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #FCE94F;\
+}\
+.ace-chaos .ace_marker-layer .ace_active-line {\
+background: #333;\
+}\
+.ace-chaos .ace_gutter-active-line {\
+background-color: #222;\
+}\
+.ace-chaos .ace_invisible {\
+color: #404040;\
+}\
+.ace-chaos .ace_keyword {\
+color:#00698F;\
+}\
+.ace-chaos .ace_keyword.ace_operator {\
+color:#FF308F;\
+}\
+.ace-chaos .ace_constant {\
+color:#1EDAFB;\
+}\
+.ace-chaos .ace_constant.ace_language {\
+color:#FDC251;\
+}\
+.ace-chaos .ace_constant.ace_library {\
+color:#8DFF0A;\
+}\
+.ace-chaos .ace_constant.ace_numeric {\
+color:#58C554;\
+}\
+.ace-chaos .ace_invalid {\
+color:#FFFFFF;\
+background-color:#990000;\
+}\
+.ace-chaos .ace_invalid.ace_deprecated {\
+color:#FFFFFF;\
+background-color:#990000;\
+}\
+.ace-chaos .ace_support {\
+color: #999;\
+}\
+.ace-chaos .ace_support.ace_function {\
+color:#00AEEF;\
+}\
+.ace-chaos .ace_function {\
+color:#00AEEF;\
+}\
+.ace-chaos .ace_string {\
+color:#58C554;\
+}\
+.ace-chaos .ace_comment {\
+color:#555;\
+font-style:italic;\
+padding-bottom: 0px;\
+}\
+.ace-chaos .ace_variable {\
+color:#997744;\
+}\
+.ace-chaos .ace_meta.ace_tag {\
+color:#BE53E6;\
+}\
+.ace-chaos .ace_entity.ace_other.ace_attribute-name {\
+color:#FFFF89;\
+}\
+.ace-chaos .ace_markup.ace_underline {\
+text-decoration: underline;\
+}\
+.ace-chaos .ace_fold-widget {\
+text-align: center;\
+}\
+.ace-chaos .ace_fold-widget:hover {\
+color: #777;\
+}\
+.ace-chaos .ace_fold-widget.ace_start,\
+.ace-chaos .ace_fold-widget.ace_end,\
+.ace-chaos .ace_fold-widget.ace_closed{\
+background: none;\
+border: none;\
+box-shadow: none;\
+}\
+.ace-chaos .ace_fold-widget.ace_start:after {\
+content: '▾'\
+}\
+.ace-chaos .ace_fold-widget.ace_end:after {\
+content: '▴'\
+}\
+.ace-chaos .ace_fold-widget.ace_closed:after {\
+content: '‣'\
+}\
+.ace-chaos .ace_indent-guide {\
+border-right:1px dotted #333;\
+margin-right:-1px;\
+}\
+.ace-chaos .ace_fold { \
+background: #222; \
+border-radius: 3px; \
+color: #7AF; \
+border: none; \
+}\
+.ace-chaos .ace_fold:hover {\
+background: #CCC; \
+color: #000;\
+}\
+";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+
+});
\ No newline at end of file
diff --git a/tutor/app/assets/javascripts/editor/theme-chrome.js b/tutor/app/assets/javascripts/editor/theme-chrome.js
new file mode 100644
index 00000000..33f83f5f
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-chrome.js
@@ -0,0 +1,160 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/chrome', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = false;
+exports.cssClass = "ace-chrome";
+exports.cssText = ".ace-chrome .ace_gutter {\
+background: #ebebeb;\
+color: #333;\
+overflow : hidden;\
+}\
+.ace-chrome .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8;\
+}\
+.ace-chrome {\
+background-color: #FFFFFF;\
+color: black;\
+}\
+.ace-chrome .ace_cursor {\
+color: black;\
+}\
+.ace-chrome .ace_invisible {\
+color: rgb(191, 191, 191);\
+}\
+.ace-chrome .ace_constant.ace_buildin {\
+color: rgb(88, 72, 246);\
+}\
+.ace-chrome .ace_constant.ace_language {\
+color: rgb(88, 92, 246);\
+}\
+.ace-chrome .ace_constant.ace_library {\
+color: rgb(6, 150, 14);\
+}\
+.ace-chrome .ace_invalid {\
+background-color: rgb(153, 0, 0);\
+color: white;\
+}\
+.ace-chrome .ace_fold {\
+}\
+.ace-chrome .ace_support.ace_function {\
+color: rgb(60, 76, 114);\
+}\
+.ace-chrome .ace_support.ace_constant {\
+color: rgb(6, 150, 14);\
+}\
+.ace-chrome .ace_support.ace_type,\
+.ace-chrome .ace_support.ace_class\
+.ace-chrome .ace_support.ace_other {\
+color: rgb(109, 121, 222);\
+}\
+.ace-chrome .ace_variable.ace_parameter {\
+font-style:italic;\
+color:#FD971F;\
+}\
+.ace-chrome .ace_keyword.ace_operator {\
+color: rgb(104, 118, 135);\
+}\
+.ace-chrome .ace_comment {\
+color: #236e24;\
+}\
+.ace-chrome .ace_comment.ace_doc {\
+color: #236e24;\
+}\
+.ace-chrome .ace_comment.ace_doc.ace_tag {\
+color: #236e24;\
+}\
+.ace-chrome .ace_constant.ace_numeric {\
+color: rgb(0, 0, 205);\
+}\
+.ace-chrome .ace_variable {\
+color: rgb(49, 132, 149);\
+}\
+.ace-chrome .ace_xml-pe {\
+color: rgb(104, 104, 91);\
+}\
+.ace-chrome .ace_entity.ace_name.ace_function {\
+color: #0000A2;\
+}\
+.ace-chrome .ace_heading {\
+color: rgb(12, 7, 255);\
+}\
+.ace-chrome .ace_list {\
+color:rgb(185, 6, 144);\
+}\
+.ace-chrome .ace_marker-layer .ace_selection {\
+background: rgb(181, 213, 255);\
+}\
+.ace-chrome .ace_marker-layer .ace_step {\
+background: rgb(252, 255, 0);\
+}\
+.ace-chrome .ace_marker-layer .ace_stack {\
+background: rgb(164, 229, 101);\
+}\
+.ace-chrome .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid rgb(192, 192, 192);\
+}\
+.ace-chrome .ace_marker-layer .ace_active-line {\
+background: rgba(0, 0, 0, 0.07);\
+}\
+.ace-chrome .ace_gutter-active-line {\
+background-color : #dcdcdc;\
+}\
+.ace-chrome .ace_marker-layer .ace_selected-word {\
+background: rgb(250, 250, 255);\
+border: 1px solid rgb(200, 200, 250);\
+}\
+.ace-chrome .ace_storage,\
+.ace-chrome .ace_keyword,\
+.ace-chrome .ace_meta.ace_tag {\
+color: rgb(147, 15, 128);\
+}\
+.ace-chrome .ace_string.ace_regex {\
+color: rgb(255, 0, 0)\
+}\
+.ace-chrome .ace_string {\
+color: #1A1AA6;\
+}\
+.ace-chrome .ace_entity.ace_other.ace_attribute-name {\
+color: #994409;\
+}\
+.ace-chrome .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
+}\
+";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-clouds.js b/tutor/app/assets/javascripts/editor/theme-clouds.js
new file mode 100644
index 00000000..9433cded
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-clouds.js
@@ -0,0 +1,128 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/clouds', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = false;
+exports.cssClass = "ace-clouds";
+exports.cssText = ".ace-clouds .ace_gutter {\
+background: #ebebeb;\
+color: #333\
+}\
+.ace-clouds .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8\
+}\
+.ace-clouds {\
+background-color: #FFFFFF;\
+color: #000000\
+}\
+.ace-clouds .ace_cursor {\
+color: #000000\
+}\
+.ace-clouds .ace_marker-layer .ace_selection {\
+background: #BDD5FC\
+}\
+.ace-clouds.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px #FFFFFF;\
+border-radius: 2px\
+}\
+.ace-clouds .ace_marker-layer .ace_step {\
+background: rgb(255, 255, 0)\
+}\
+.ace-clouds .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #BFBFBF\
+}\
+.ace-clouds .ace_marker-layer .ace_active-line {\
+background: #FFFBD1\
+}\
+.ace-clouds .ace_gutter-active-line {\
+background-color : #dcdcdc\
+}\
+.ace-clouds .ace_marker-layer .ace_selected-word {\
+border: 1px solid #BDD5FC\
+}\
+.ace-clouds .ace_invisible {\
+color: #BFBFBF\
+}\
+.ace-clouds .ace_keyword,\
+.ace-clouds .ace_meta,\
+.ace-clouds .ace_support.ace_constant.ace_property-value {\
+color: #AF956F\
+}\
+.ace-clouds .ace_keyword.ace_operator {\
+color: #484848\
+}\
+.ace-clouds .ace_keyword.ace_other.ace_unit {\
+color: #96DC5F\
+}\
+.ace-clouds .ace_constant.ace_language {\
+color: #39946A\
+}\
+.ace-clouds .ace_constant.ace_numeric {\
+color: #46A609\
+}\
+.ace-clouds .ace_constant.ace_character.ace_entity {\
+color: #BF78CC\
+}\
+.ace-clouds .ace_invalid {\
+background-color: #FF002A\
+}\
+.ace-clouds .ace_fold {\
+background-color: #AF956F;\
+border-color: #000000\
+}\
+.ace-clouds .ace_storage,\
+.ace-clouds .ace_support.ace_class,\
+.ace-clouds .ace_support.ace_function,\
+.ace-clouds .ace_support.ace_other,\
+.ace-clouds .ace_support.ace_type {\
+color: #C52727\
+}\
+.ace-clouds .ace_string {\
+color: #5D90CD\
+}\
+.ace-clouds .ace_comment {\
+color: #BCC8BA\
+}\
+.ace-clouds .ace_entity.ace_name.ace_tag,\
+.ace-clouds .ace_entity.ace_other.ace_attribute-name {\
+color: #606060\
+}\
+.ace-clouds .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y\
+}";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-dreamweaver.js b/tutor/app/assets/javascripts/editor/theme-dreamweaver.js
new file mode 100644
index 00000000..cdd3ae07
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-dreamweaver.js
@@ -0,0 +1,170 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/dreamweaver', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+exports.isDark = false;
+exports.cssClass = "ace-dreamweaver";
+exports.cssText = ".ace-dreamweaver .ace_gutter {\
+background: #e8e8e8;\
+color: #333;\
+}\
+.ace-dreamweaver .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8;\
+}\
+.ace-dreamweaver {\
+background-color: #FFFFFF;\
+color: black;\
+}\
+.ace-dreamweaver .ace_fold {\
+background-color: #757AD8;\
+}\
+.ace-dreamweaver .ace_cursor {\
+color: black;\
+}\
+.ace-dreamweaver .ace_invisible {\
+color: rgb(191, 191, 191);\
+}\
+.ace-dreamweaver .ace_storage,\
+.ace-dreamweaver .ace_keyword {\
+color: blue;\
+}\
+.ace-dreamweaver .ace_constant.ace_buildin {\
+color: rgb(88, 72, 246);\
+}\
+.ace-dreamweaver .ace_constant.ace_language {\
+color: rgb(88, 92, 246);\
+}\
+.ace-dreamweaver .ace_constant.ace_library {\
+color: rgb(6, 150, 14);\
+}\
+.ace-dreamweaver .ace_invalid {\
+background-color: rgb(153, 0, 0);\
+color: white;\
+}\
+.ace-dreamweaver .ace_support.ace_function {\
+color: rgb(60, 76, 114);\
+}\
+.ace-dreamweaver .ace_support.ace_constant {\
+color: rgb(6, 150, 14);\
+}\
+.ace-dreamweaver .ace_support.ace_type,\
+.ace-dreamweaver .ace_support.ace_class {\
+color: #009;\
+}\
+.ace-dreamweaver .ace_support.ace_php_tag {\
+color: #f00;\
+}\
+.ace-dreamweaver .ace_keyword.ace_operator {\
+color: rgb(104, 118, 135);\
+}\
+.ace-dreamweaver .ace_string {\
+color: #00F;\
+}\
+.ace-dreamweaver .ace_comment {\
+color: rgb(76, 136, 107);\
+}\
+.ace-dreamweaver .ace_comment.ace_doc {\
+color: rgb(0, 102, 255);\
+}\
+.ace-dreamweaver .ace_comment.ace_doc.ace_tag {\
+color: rgb(128, 159, 191);\
+}\
+.ace-dreamweaver .ace_constant.ace_numeric {\
+color: rgb(0, 0, 205);\
+}\
+.ace-dreamweaver .ace_variable {\
+color: #06F\
+}\
+.ace-dreamweaver .ace_xml-pe {\
+color: rgb(104, 104, 91);\
+}\
+.ace-dreamweaver .ace_entity.ace_name.ace_function {\
+color: #00F;\
+}\
+.ace-dreamweaver .ace_heading {\
+color: rgb(12, 7, 255);\
+}\
+.ace-dreamweaver .ace_list {\
+color:rgb(185, 6, 144);\
+}\
+.ace-dreamweaver .ace_marker-layer .ace_selection {\
+background: rgb(181, 213, 255);\
+}\
+.ace-dreamweaver .ace_marker-layer .ace_step {\
+background: rgb(252, 255, 0);\
+}\
+.ace-dreamweaver .ace_marker-layer .ace_stack {\
+background: rgb(164, 229, 101);\
+}\
+.ace-dreamweaver .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid rgb(192, 192, 192);\
+}\
+.ace-dreamweaver .ace_marker-layer .ace_active-line {\
+background: rgba(0, 0, 0, 0.07);\
+}\
+.ace-dreamweaver .ace_marker-layer .ace_selected-word {\
+background: rgb(250, 250, 255);\
+border: 1px solid rgb(200, 200, 250);\
+}\
+.ace-dreamweaver .ace_meta.ace_tag {\
+color:#009;\
+}\
+.ace-dreamweaver .ace_meta.ace_tag.ace_anchor {\
+color:#060;\
+}\
+.ace-dreamweaver .ace_meta.ace_tag.ace_form {\
+color:#F90;\
+}\
+.ace-dreamweaver .ace_meta.ace_tag.ace_image {\
+color:#909;\
+}\
+.ace-dreamweaver .ace_meta.ace_tag.ace_script {\
+color:#900;\
+}\
+.ace-dreamweaver .ace_meta.ace_tag.ace_style {\
+color:#909;\
+}\
+.ace-dreamweaver .ace_meta.ace_tag.ace_table {\
+color:#099;\
+}\
+.ace-dreamweaver .ace_string.ace_regex {\
+color: rgb(255, 0, 0)\
+}\
+.ace-dreamweaver .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
+}";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-eclipse.js b/tutor/app/assets/javascripts/editor/theme-eclipse.js
new file mode 100644
index 00000000..c9bbdd0a
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-eclipse.js
@@ -0,0 +1,127 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/eclipse', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+
+exports.isDark = false;
+exports.cssText = ".ace-eclipse .ace_gutter {\
+background: #ebebeb;\
+border-right: 1px solid rgb(159, 159, 159);\
+color: rgb(136, 136, 136);\
+}\
+.ace-eclipse .ace_print-margin {\
+width: 1px;\
+background: #ebebeb;\
+}\
+.ace-eclipse {\
+background-color: #FFFFFF;\
+color: black;\
+}\
+.ace-eclipse .ace_fold {\
+background-color: rgb(60, 76, 114);\
+}\
+.ace-eclipse .ace_cursor {\
+color: black;\
+}\
+.ace-eclipse .ace_storage,\
+.ace-eclipse .ace_keyword,\
+.ace-eclipse .ace_variable {\
+color: rgb(127, 0, 85);\
+}\
+.ace-eclipse .ace_constant.ace_buildin {\
+color: rgb(88, 72, 246);\
+}\
+.ace-eclipse .ace_constant.ace_library {\
+color: rgb(6, 150, 14);\
+}\
+.ace-eclipse .ace_function {\
+color: rgb(60, 76, 114);\
+}\
+.ace-eclipse .ace_string {\
+color: rgb(42, 0, 255);\
+}\
+.ace-eclipse .ace_comment {\
+color: rgb(113, 150, 130);\
+}\
+.ace-eclipse .ace_comment.ace_doc {\
+color: rgb(63, 95, 191);\
+}\
+.ace-eclipse .ace_comment.ace_doc.ace_tag {\
+color: rgb(127, 159, 191);\
+}\
+.ace-eclipse .ace_constant.ace_numeric {\
+color: darkblue;\
+}\
+.ace-eclipse .ace_tag {\
+color: rgb(25, 118, 116);\
+}\
+.ace-eclipse .ace_type {\
+color: rgb(127, 0, 127);\
+}\
+.ace-eclipse .ace_xml-pe {\
+color: rgb(104, 104, 91);\
+}\
+.ace-eclipse .ace_marker-layer .ace_selection {\
+background: rgb(181, 213, 255);\
+}\
+.ace-eclipse .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid rgb(192, 192, 192);\
+}\
+.ace-eclipse .ace_meta.ace_tag {\
+color:rgb(25, 118, 116);\
+}\
+.ace-eclipse .ace_invisible {\
+color: #ddd;\
+}\
+.ace-eclipse .ace_entity.ace_other.ace_attribute-name {\
+color:rgb(127, 0, 127);\
+}\
+.ace-eclipse .ace_marker-layer .ace_step {\
+background: rgb(255, 255, 0);\
+}\
+.ace-eclipse .ace_marker-layer .ace_active-line {\
+background: rgb(232, 242, 254);\
+}\
+.ace-eclipse .ace_marker-layer .ace_selected-word {\
+border: 1px solid rgb(181, 213, 255);\
+}\
+.ace-eclipse .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
+}";
+
+exports.cssClass = "ace-eclipse";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-github.js b/tutor/app/assets/javascripts/editor/theme-github.js
new file mode 100644
index 00000000..8f344b54
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-github.js
@@ -0,0 +1,133 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/github', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = false;
+exports.cssClass = "ace-github";
+exports.cssText = "/* CSS style content from github's default pygments highlighter template.\
+Cursor and selection styles from textmate.css. */\
+.ace-github .ace_gutter {\
+background: #e8e8e8;\
+color: #AAA;\
+}\
+.ace-github {\
+background: #fff;\
+color: #000;\
+}\
+.ace-github .ace_keyword {\
+font-weight: bold;\
+}\
+.ace-github .ace_string {\
+color: #D14;\
+}\
+.ace-github .ace_variable.ace_class {\
+color: teal;\
+}\
+.ace-github .ace_constant.ace_numeric {\
+color: #099;\
+}\
+.ace-github .ace_constant.ace_buildin {\
+color: #0086B3;\
+}\
+.ace-github .ace_support.ace_function {\
+color: #0086B3;\
+}\
+.ace-github .ace_comment {\
+color: #998;\
+font-style: italic;\
+}\
+.ace-github .ace_variable.ace_language {\
+color: #0086B3;\
+}\
+.ace-github .ace_paren {\
+font-weight: bold;\
+}\
+.ace-github .ace_boolean {\
+font-weight: bold;\
+}\
+.ace-github .ace_string.ace_regexp {\
+color: #009926;\
+font-weight: normal;\
+}\
+.ace-github .ace_variable.ace_instance {\
+color: teal;\
+}\
+.ace-github .ace_constant.ace_language {\
+font-weight: bold;\
+}\
+.ace-github .ace_cursor {\
+color: black;\
+}\
+.ace-github .ace_marker-layer .ace_active-line {\
+background: rgb(255, 255, 204);\
+}\
+.ace-github .ace_marker-layer .ace_selection {\
+background: rgb(181, 213, 255);\
+}\
+.ace-github.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px white;\
+border-radius: 2px;\
+}\
+/* bold keywords cause cursor issues for some fonts */\
+/* this disables bold style for editor and keeps for static highlighter */\
+.ace-github.ace_nobold .ace_line > span {\
+font-weight: normal !important;\
+}\
+.ace-github .ace_marker-layer .ace_step {\
+background: rgb(252, 255, 0);\
+}\
+.ace-github .ace_marker-layer .ace_stack {\
+background: rgb(164, 229, 101);\
+}\
+.ace-github .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid rgb(192, 192, 192);\
+}\
+.ace-github .ace_gutter-active-line {\
+background-color : rgba(0, 0, 0, 0.07);\
+}\
+.ace-github .ace_marker-layer .ace_selected-word {\
+background: rgb(250, 250, 255);\
+border: 1px solid rgb(200, 200, 250);\
+}\
+.ace-github .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8;\
+}\
+.ace-github .ace_indent-guide {\
+background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==\") right repeat-y;\
+}";
+
+ var dom = require("../lib/dom");
+ dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-tomorrow_night_blue.js b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_blue.js
new file mode 100644
index 00000000..3782918c
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_blue.js
@@ -0,0 +1,139 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/tomorrow_night_blue', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = true;
+exports.cssClass = "ace-tomorrow-night-blue";
+exports.cssText = ".ace-tomorrow-night-blue .ace_gutter {\
+background: #00204b;\
+color: #7388b5\
+}\
+.ace-tomorrow-night-blue .ace_print-margin {\
+width: 1px;\
+background: #00204b\
+}\
+.ace-tomorrow-night-blue {\
+background-color: #002451;\
+color: #FFFFFF\
+}\
+.ace-tomorrow-night-blue .ace_constant.ace_other,\
+.ace-tomorrow-night-blue .ace_cursor {\
+color: #FFFFFF\
+}\
+.ace-tomorrow-night-blue .ace_marker-layer .ace_selection {\
+background: #003F8E\
+}\
+.ace-tomorrow-night-blue.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px #002451;\
+border-radius: 2px\
+}\
+.ace-tomorrow-night-blue .ace_marker-layer .ace_step {\
+background: rgb(127, 111, 19)\
+}\
+.ace-tomorrow-night-blue .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #404F7D\
+}\
+.ace-tomorrow-night-blue .ace_marker-layer .ace_active-line {\
+background: #00346E\
+}\
+.ace-tomorrow-night-blue .ace_gutter-active-line {\
+background-color: #022040\
+}\
+.ace-tomorrow-night-blue .ace_marker-layer .ace_selected-word {\
+border: 1px solid #003F8E\
+}\
+.ace-tomorrow-night-blue .ace_invisible {\
+color: #404F7D\
+}\
+.ace-tomorrow-night-blue .ace_keyword,\
+.ace-tomorrow-night-blue .ace_meta,\
+.ace-tomorrow-night-blue .ace_storage,\
+.ace-tomorrow-night-blue .ace_storage.ace_type,\
+.ace-tomorrow-night-blue .ace_support.ace_type {\
+color: #EBBBFF\
+}\
+.ace-tomorrow-night-blue .ace_keyword.ace_operator {\
+color: #99FFFF\
+}\
+.ace-tomorrow-night-blue .ace_constant.ace_character,\
+.ace-tomorrow-night-blue .ace_constant.ace_language,\
+.ace-tomorrow-night-blue .ace_constant.ace_numeric,\
+.ace-tomorrow-night-blue .ace_keyword.ace_other.ace_unit,\
+.ace-tomorrow-night-blue .ace_support.ace_constant,\
+.ace-tomorrow-night-blue .ace_variable.ace_parameter {\
+color: #FFC58F\
+}\
+.ace-tomorrow-night-blue .ace_invalid {\
+color: #FFFFFF;\
+background-color: #F99DA5\
+}\
+.ace-tomorrow-night-blue .ace_invalid.ace_deprecated {\
+color: #FFFFFF;\
+background-color: #EBBBFF\
+}\
+.ace-tomorrow-night-blue .ace_fold {\
+background-color: #BBDAFF;\
+border-color: #FFFFFF\
+}\
+.ace-tomorrow-night-blue .ace_entity.ace_name.ace_function,\
+.ace-tomorrow-night-blue .ace_support.ace_function,\
+.ace-tomorrow-night-blue .ace_variable {\
+color: #BBDAFF\
+}\
+.ace-tomorrow-night-blue .ace_support.ace_class,\
+.ace-tomorrow-night-blue .ace_support.ace_type {\
+color: #FFEEAD\
+}\
+.ace-tomorrow-night-blue .ace_heading,\
+.ace-tomorrow-night-blue .ace_markup.ace_heading,\
+.ace-tomorrow-night-blue .ace_string {\
+color: #D1F1A9\
+}\
+.ace-tomorrow-night-blue .ace_entity.ace_name.ace_tag,\
+.ace-tomorrow-night-blue .ace_entity.ace_other.ace_attribute-name,\
+.ace-tomorrow-night-blue .ace_meta.ace_tag,\
+.ace-tomorrow-night-blue .ace_string.ace_regexp,\
+.ace-tomorrow-night-blue .ace_variable {\
+color: #FF9DA4\
+}\
+.ace-tomorrow-night-blue .ace_comment {\
+color: #7285B7\
+}\
+.ace-tomorrow-night-blue .ace_indent-guide {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYJDzqfwPAANXAeNsiA+ZAAAAAElFTkSuQmCC) right repeat-y\
+}";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/editor/theme-tomorrow_night_eighties.js b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_eighties.js
new file mode 100644
index 00000000..ef7360f7
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-tomorrow_night_eighties.js
@@ -0,0 +1,141 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/tomorrow_night_eighties', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = true;
+exports.cssClass = "ace-tomorrow-night-eighties";
+exports.cssText = ".ace-tomorrow-night-eighties .ace_gutter {\
+background: #272727;\
+color: #CCC\
+}\
+.ace-tomorrow-night-eighties .ace_print-margin {\
+width: 1px;\
+background: #272727\
+}\
+.ace-tomorrow-night-eighties {\
+background-color: #2D2D2D;\
+color: #CCCCCC\
+}\
+.ace-tomorrow-night-eighties .ace_constant.ace_other,\
+.ace-tomorrow-night-eighties .ace_cursor {\
+color: #CCCCCC\
+}\
+.ace-tomorrow-night-eighties .ace_marker-layer .ace_selection {\
+background: #515151\
+}\
+.ace-tomorrow-night-eighties.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px #2D2D2D;\
+border-radius: 2px\
+}\
+.ace-tomorrow-night-eighties .ace_marker-layer .ace_step {\
+background: rgb(102, 82, 0)\
+}\
+.ace-tomorrow-night-eighties .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #6A6A6A\
+}\
+.ace-tomorrow-night-bright .ace_stack {\
+background: rgb(66, 90, 44)\
+}\
+.ace-tomorrow-night-eighties .ace_marker-layer .ace_active-line {\
+background: #393939\
+}\
+.ace-tomorrow-night-eighties .ace_gutter-active-line {\
+background-color: #393939\
+}\
+.ace-tomorrow-night-eighties .ace_marker-layer .ace_selected-word {\
+border: 1px solid #515151\
+}\
+.ace-tomorrow-night-eighties .ace_invisible {\
+color: #6A6A6A\
+}\
+.ace-tomorrow-night-eighties .ace_keyword,\
+.ace-tomorrow-night-eighties .ace_meta,\
+.ace-tomorrow-night-eighties .ace_storage,\
+.ace-tomorrow-night-eighties .ace_storage.ace_type,\
+.ace-tomorrow-night-eighties .ace_support.ace_type {\
+color: #CC99CC\
+}\
+.ace-tomorrow-night-eighties .ace_keyword.ace_operator {\
+color: #66CCCC\
+}\
+.ace-tomorrow-night-eighties .ace_constant.ace_character,\
+.ace-tomorrow-night-eighties .ace_constant.ace_language,\
+.ace-tomorrow-night-eighties .ace_constant.ace_numeric,\
+.ace-tomorrow-night-eighties .ace_keyword.ace_other.ace_unit,\
+.ace-tomorrow-night-eighties .ace_support.ace_constant,\
+.ace-tomorrow-night-eighties .ace_variable.ace_parameter {\
+color: #F99157\
+}\
+.ace-tomorrow-night-eighties .ace_invalid {\
+color: #CDCDCD;\
+background-color: #F2777A\
+}\
+.ace-tomorrow-night-eighties .ace_invalid.ace_deprecated {\
+color: #CDCDCD;\
+background-color: #CC99CC\
+}\
+.ace-tomorrow-night-eighties .ace_fold {\
+background-color: #6699CC;\
+border-color: #CCCCCC\
+}\
+.ace-tomorrow-night-eighties .ace_entity.ace_name.ace_function,\
+.ace-tomorrow-night-eighties .ace_support.ace_function,\
+.ace-tomorrow-night-eighties .ace_variable {\
+color: #6699CC\
+}\
+.ace-tomorrow-night-eighties .ace_support.ace_class,\
+.ace-tomorrow-night-eighties .ace_support.ace_type {\
+color: #FFCC66\
+}\
+.ace-tomorrow-night-eighties .ace_heading,\
+.ace-tomorrow-night-eighties .ace_markup.ace_heading,\
+.ace-tomorrow-night-eighties .ace_string {\
+color: #99CC99\
+}\
+.ace-tomorrow-night-eighties .ace_comment {\
+color: #999999\
+}\
+.ace-tomorrow-night-eighties .ace_entity.ace_name.ace_tag,\
+.ace-tomorrow-night-eighties .ace_entity.ace_other.ace_attribute-name,\
+.ace-tomorrow-night-eighties .ace_meta.ace_tag,\
+.ace-tomorrow-night-eighties .ace_variable {\
+color: #F2777A\
+}\
+.ace-tomorrow-night-eighties .ace_indent-guide {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y\
+}";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/theme-twilight.js b/tutor/app/assets/javascripts/editor/theme-twilight.js
similarity index 99%
rename from tutor/app/assets/javascripts/theme-twilight.js
rename to tutor/app/assets/javascripts/editor/theme-twilight.js
index 5376cecb..f02d3aa1 100644
--- a/tutor/app/assets/javascripts/theme-twilight.js
+++ b/tutor/app/assets/javascripts/editor/theme-twilight.js
@@ -1,5 +1,7 @@
// Source: Ace editor
// Integrated by: Mimi
+// Source: Ace editor
+// Integrated by: Mimi
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
diff --git a/tutor/app/assets/javascripts/editor/theme-xcode.js b/tutor/app/assets/javascripts/editor/theme-xcode.js
new file mode 100644
index 00000000..1abacd93
--- /dev/null
+++ b/tutor/app/assets/javascripts/editor/theme-xcode.js
@@ -0,0 +1,121 @@
+// Source: Ace editor
+// Integrated by: Mimi
+/* ***** BEGIN LICENSE BLOCK *****
+ * Distributed under the BSD license:
+ *
+ * Copyright (c) 2010, Ajax.org B.V.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Ajax.org B.V. nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ace.define('ace/theme/xcode', ['require', 'exports', 'module' , 'ace/lib/dom'], function(require, exports, module) {
+
+exports.isDark = false;
+exports.cssClass = "ace-xcode";
+exports.cssText = "/* THIS THEME WAS AUTOGENERATED BY Theme.tmpl.css (UUID: EE3AD170-2B7F-4DE1-B724-C75F13FE0085) */\
+.ace-xcode .ace_gutter {\
+background: #e8e8e8;\
+color: #333\
+}\
+.ace-xcode .ace_print-margin {\
+width: 1px;\
+background: #e8e8e8\
+}\
+.ace-xcode {\
+background-color: #FFFFFF;\
+color: #000000\
+}\
+.ace-xcode .ace_cursor {\
+color: #000000\
+}\
+.ace-xcode .ace_marker-layer .ace_selection {\
+background: #B5D5FF\
+}\
+.ace-xcode.ace_multiselect .ace_selection.ace_start {\
+box-shadow: 0 0 3px 0px #FFFFFF;\
+border-radius: 2px\
+}\
+.ace-xcode .ace_marker-layer .ace_step {\
+background: rgb(198, 219, 174)\
+}\
+.ace-xcode .ace_marker-layer .ace_bracket {\
+margin: -1px 0 0 -1px;\
+border: 1px solid #BFBFBF\
+}\
+.ace-xcode .ace_marker-layer .ace_active-line {\
+background: rgba(0, 0, 0, 0.071)\
+}\
+.ace-xcode .ace_gutter-active-line {\
+background-color: rgba(0, 0, 0, 0.071)\
+}\
+.ace-xcode .ace_marker-layer .ace_selected-word {\
+border: 1px solid #B5D5FF\
+}\
+.ace-xcode .ace_constant.ace_language,\
+.ace-xcode .ace_keyword,\
+.ace-xcode .ace_meta,\
+.ace-xcode .ace_variable.ace_language {\
+color: #C800A4\
+}\
+.ace-xcode .ace_invisible {\
+color: #BFBFBF\
+}\
+.ace-xcode .ace_constant.ace_character,\
+.ace-xcode .ace_constant.ace_other {\
+color: #275A5E\
+}\
+.ace-xcode .ace_constant.ace_numeric {\
+color: #3A00DC\
+}\
+.ace-xcode .ace_entity.ace_other.ace_attribute-name,\
+.ace-xcode .ace_support.ace_constant,\
+.ace-xcode .ace_support.ace_function {\
+color: #450084\
+}\
+.ace-xcode .ace_fold {\
+background-color: #C800A4;\
+border-color: #000000\
+}\
+.ace-xcode .ace_entity.ace_name.ace_tag,\
+.ace-xcode .ace_support.ace_class,\
+.ace-xcode .ace_support.ace_type {\
+color: #790EAD\
+}\
+.ace-xcode .ace_storage {\
+color: #C900A4\
+}\
+.ace-xcode .ace_string {\
+color: #DF0002\
+}\
+.ace-xcode .ace_comment {\
+color: #008E00\
+}\
+.ace-xcode .ace_indent-guide {\
+background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==) right repeat-y\
+}";
+
+var dom = require("../lib/dom");
+dom.importCssString(exports.cssText, exports.cssClass);
+});
diff --git a/tutor/app/assets/javascripts/notes.js.coffee b/tutor/app/assets/javascripts/notes.js.coffee
new file mode 100644
index 00000000..24f83d18
--- /dev/null
+++ b/tutor/app/assets/javascripts/notes.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/tutor/app/assets/javascripts/problems.js.coffee b/tutor/app/assets/javascripts/problems.js.coffee
index d27fb224..a537d1c2 100644
--- a/tutor/app/assets/javascripts/problems.js.coffee
+++ b/tutor/app/assets/javascripts/problems.js.coffee
@@ -24,12 +24,12 @@ timer = ->
document.getElementById("secs").innerHTML = digit
i = 0
while true
- hint = $('#hint' + i)
- if typeof(hint.attr 'time') == 'undefined'
+ tip = $('#tip' + i)
+ if typeof(tip.attr 'time') == 'undefined'
break
- time = hint.attr 'time'
+ time = tip.attr 'time'
if time <= min*60 + parseInt(digit, 10)
- hint.attr 'class', ''
+ tip.attr 'class', ''
i++
activate = ->
diff --git a/tutor/app/assets/javascripts/solutions.js.coffee b/tutor/app/assets/javascripts/solutions.js.coffee
index 910eeb33..ae0df5de 100644
--- a/tutor/app/assets/javascripts/solutions.js.coffee
+++ b/tutor/app/assets/javascripts/solutions.js.coffee
@@ -2,34 +2,39 @@
variables = null
# The number of the current index of the line to be executed
index_number = 0
+# The status of the debugging session
+status = null
# [Debugger: Debug - Story 3.6]
# Sends the code to the server and changes the Variables to the data recieved
# Parameters:
# problem_id: The id of the problem being solved
+# problem_type: The type of the problem to be submitted
# Returns: none
# Author: Mussab ElDash
-@start_debug = (problem_id) ->
- input = $('#solution_code').val()
+@start_debug = (problem_id, problem_type) ->
+ input = get_editor_session().getValue()
if input.length is 0
alert "You didn't write any code"
return
test = $('#solution_input').val()
+ class_name = $("#class_name").val()
start_spin()
toggle_code_area()
$.ajax
type: "POST"
url: '/debuggers/' + problem_id
- data: {code : input , case : test}
+ data: {code: input, case: test, class_name: class_name,\
+ problem_type: problem_type}
datatype: 'json'
success: (data) ->
clear_console()
- variables = data
stop_spin()
if !data["success"]
compilation_error data["data"]["errors"]
return
variables = data["data"]
+ status = data["status"]
toggleDebug(1)
debug_console()
jump_state 0
@@ -44,11 +49,13 @@ index_number = 0
# Sends the code to the server to be compiled
# Parameters:
# problem_id: The id of the problem being solved
+# problem_type: The type of the problem to be submitted
# Returns:
# none
# Author: Ahmed Akram
-@compile = (problem_id) ->
- input = $('#solution_code').val()
+@compile = (problem_id, problem_type) ->
+ input = get_editor_session().getValue()
+ class_name = $("#class_name").val()
if input.length is 0
alert "You didn't write any code"
return
@@ -57,7 +64,8 @@ index_number = 0
$.ajax
type: "POST"
url: '/solutions/compile_solution'
- data: {code : input, problem_id : problem_id}
+ data: {code: input, problem_id: problem_id,\
+ class_name: class_name, problem_type: problem_type}
datatype: 'json'
success: (unique) ->
clear_console()
@@ -78,21 +86,24 @@ index_number = 0
# Sends the code and the test case to the server to be tested
# Parameters:
# problem_id: The id of the problem being solved
+# problem_type: The type of the problem to be submitted
# Returns:
# none
# Author: Ahmed Akram
-@run_input = (problem_id) ->
- code = $('#solution_code').val()
+@run_input = (problem_id, problem_type) ->
+ code = get_editor_session().getValue()
if code.length is 0
alert "You didn't write any code"
return
test = $('#solution_input').val()
+ class_name = $("#class_name").val()
start_spin()
toggle_code_area()
$.ajax
type: "POST"
url: '/solutions/execute'
- data: {code : code, problem_id : problem_id, input : test}
+ data: {code: code, problem_id: problem_id, input: test,\
+ class_name: class_name, problem_type: problem_type}
datatype: 'json'
success: (data) ->
clear_console()
@@ -222,7 +233,7 @@ debug_console = ->
$('#nextButton').toggle(state)
$('#previousButton').toggle(state)
$('#stopButton').toggle(state)
- editor = ace.edit("editor")
+ editor = get_editor()
editor.setReadOnly(state)
normal_theme = "ace/theme/twilight"
debug_theme = normal_theme + "-debug"
@@ -244,7 +255,7 @@ debug_console = ->
alert "The online debugger can only step forward 100 times."
jump_state index_number
else
- alert "The program has terminated."
+ alert status
jump_state index_number
# [Execute Line By Line - Story 3.8]
@@ -266,7 +277,7 @@ debug_console = ->
# Returns: none
# Author: Rami Khalil
@highlight_line = (line) ->
- editor = ace.edit("editor")
+ editor = get_editor()
editor.gotoLine line, 0, true
# [Execute Line By Line - Story 3.8]
@@ -322,24 +333,27 @@ debug_console = ->
# using ajax showing an alert box for success and failure scenarios
# Parameters:
# problem_id: the id of the problem being solved
+# problem_type: The type of the problem to be submitted
# Returns: a json object containing two arrays one for the errors
# of the current code and the other containing success messages
# and the success and failure messages are displayed in a table
# Author: MOHAMEDSAEED
-@validate_code = (problem_id) ->
- code = $('#solution_code').val()
+@validate_code = (problem_id, problem_type) ->
+ code = get_editor_session().getValue()
mins = parseInt($('#mins').text())
secs = parseInt($('#secs').text())
time = (mins * 60) + secs
if code.length is 0
alert 'Blank submissions are not allowed'
return
+ class_name = $("#class_name").val()
toggle_code_area()
start_spin()
$.ajax
type: "POST"
url: '/solutions'
- data: {problem_id: problem_id, code: code, time: time}
+ data: {problem_id: problem_id, code: code, time: time,\
+ class_name: class_name, problem_type: problem_type}
datatype: 'json'
success: (data) ->
clear_console()
@@ -379,10 +393,17 @@ debug_console = ->
# Parameters: none
# Returns: none
# Author: MOHAMEDSAEED
-@reload_template = () ->
- editor = ace.edit("editor");
- edit_session = editor.getSession();
- template = "public class CoolSoft {\n"
- template += "\tpublic static void main(String [] args) {\n\t\t\n\t}\n}"
- edit_session.setValue(template);
- return
\ No newline at end of file
+@reload_template = () ->
+ disabled = $('#solution_code').prop 'disabled'
+ unless disabled
+ template = "public class CoolSoft {\n"
+ template += "\tpublic static void main(String [] args) {\n\t\t\n\t}\n}"
+ get_editor_session().setValue(template);
+ $('#class_name').val("CoolSoft");
+ return
+
+get_editor = ->
+ ace.edit("editor")
+
+get_editor_session = ->
+ get_editor().getSession()
diff --git a/tutor/app/assets/javascripts/topics.js.coffee b/tutor/app/assets/javascripts/topics.js.coffee
index 6891ae5b..30016472 100644
--- a/tutor/app/assets/javascripts/topics.js.coffee
+++ b/tutor/app/assets/javascripts/topics.js.coffee
@@ -3,7 +3,12 @@
# Author: Mussab ElDash
@newTrack = ->
create = document.getElementById("create_track")
+ new_track = document.getElementById("new_track")
create.hidden = !create.hidden
+ if create.hidden
+ new_track.innerText = "New"
+ else
+ new_track.innerText = "Hide"
return
# [Edit Track Rating - Story 4.13]
diff --git a/tutor/app/assets/stylesheets/application.css b/tutor/app/assets/stylesheets/application.css
index d13f78b7..fd4dc0eb 100644
--- a/tutor/app/assets/stylesheets/application.css
+++ b/tutor/app/assets/stylesheets/application.css
@@ -10,6 +10,7 @@
*
*= require_self
*= require_tree .
+ *= require bootstrap
*/
= require posts
@@ -37,6 +38,7 @@
.footer {
width:100%;
border-radius: 2px;
+ margin-top: 1.65%;
}
.auto-height {
@@ -54,4 +56,39 @@
.home {
overflow:auto;
+}
+
+.zooming {
+ -ms-transform: scale(0.8); /* IE 9 */
+ -ms-transform-origin: left top;
+ -webkit-transform: scale(0.8); /* Chrome, Safari, Opera */
+ -webkit-transform-origin: left top;
+ transform: scale(0.8);
+ transform-origin: left top;
+ -moz-transform: scale(0.8);
+ -moz-transform-origin: left top;
+}
+
+.unzooming, #editor{
+ -ms-transform: scale(1.25); /* IE 9 */
+ -ms-transform-origin: left top;
+ -webkit-transform: scale(1.25); /* Chrome, Safari, Opera */
+ -webkit-transform-origin: left top;
+ transform: scale(1.25);
+ transform-origin: left top;
+ -moz-transform: scale(1.25);
+ -moz-transform-origin: left top;
+}
+
+.zoom125{
+ height: 125%;
+ width: 125%;
+}
+
+#editor{
+ width: 80%;
+}
+
+.no-scroll-x{
+ overflow-x: hidden;
}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/assignment_problems.css.scss b/tutor/app/assets/stylesheets/assignment_problems.css.scss
new file mode 100644
index 00000000..6c584478
--- /dev/null
+++ b/tutor/app/assets/stylesheets/assignment_problems.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the assignment_problems controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/tutor/app/assets/stylesheets/assignments.css.scss b/tutor/app/assets/stylesheets/assignments.css.scss
new file mode 100644
index 00000000..193577b0
--- /dev/null
+++ b/tutor/app/assets/stylesheets/assignments.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the assignments controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/tutor/app/assets/stylesheets/notes.css.scss b/tutor/app/assets/stylesheets/notes.css.scss
new file mode 100644
index 00000000..56d31f58
--- /dev/null
+++ b/tutor/app/assets/stylesheets/notes.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the notes controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/tutor/app/assets/stylesheets/solutions.css b/tutor/app/assets/stylesheets/solutions.css
index ab2e5dc5..55fd94b9 100644
--- a/tutor/app/assets/stylesheets/solutions.css
+++ b/tutor/app/assets/stylesheets/solutions.css
@@ -42,14 +42,12 @@ textarea {
.fixed-content {
max-height: 200px;
- overflow-y:scroll;
- overflow-x:hidden;
+ overflow: auto;
}
.fixed-horizontal {
max-width: 175px;
- overflow-x:scroll;
- overflow-y:hidden;
+ overflow: auto;
}
.editor {
@@ -74,9 +72,7 @@ textarea {
max-height: 150px;
min-height: 150px;
background: white;
- overflow: scroll;
- overflow-y: scroll;
- overflow-x: scroll;
+ overflow: auto;
max-width: 750px;
min-width: 750px;
}
@@ -118,4 +114,38 @@ textarea {
.hidden {
visibility: false;
+}
+
+.edit_button {
+ width: 20%;
+ height: 1%;
+ margin_top: 10px;
+ cursor: pointer;
+}
+
+.add_note_button {
+ opacity: 0;
+ width: 25%;
+ height: 15%;
+ margin_top: 10px;
+ cursor: pointer;
+}
+
+.form_div {
+ display: none;
+ width: 60%;
+}
+
+.x_button {
+ width: 18%;
+ height: 15%;
+ margin_top: 10px;
+ cursor: default;
+}
+
+.tick_button {
+ width: 20%;
+ height: 20%;
+ margin_top: 10px;
+ cursor: default;
}
\ No newline at end of file
diff --git a/tutor/app/controllers/assignment_problems_controller.rb b/tutor/app/controllers/assignment_problems_controller.rb
new file mode 100644
index 00000000..96e716b7
--- /dev/null
+++ b/tutor/app/controllers/assignment_problems_controller.rb
@@ -0,0 +1,39 @@
+class AssignmentProblemsController < ApplicationController
+ # [View List Of Assignments - Story 4.24]
+ # Lists the list of problems in a
+ # particular assignment
+ # Parameters:
+ # id: Id of the problem.
+ # Returns: none
+ # Author: Lin Kassem
+ def show
+ @problem = AssignmentProblem.find_by_id(params[:id])
+ if @problem.nil?
+ render "problem_not_found"
+ else
+ @assignment = @problem.assignment
+ @course = @assignment.course
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
+ end
+ end
+
+ # [View List Of Assignments - Story 4.24]
+ # Allows the editing of a particular problem
+ # in a particular assignment
+ # Parameters:
+ # id: Id of the problem.
+ # Returns: none
+ # Author: Lin Kassem
+ def edit
+ @problem = AssignmentProblem.find_by_id(params[:id])
+ if @problem.nil?
+ render "problem_not_found"
+ else
+ @assignment = @problem.assignment
+ @course = @assignment.course
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
+ end
+ end
+end
\ No newline at end of file
diff --git a/tutor/app/controllers/assignments_controller.rb b/tutor/app/controllers/assignments_controller.rb
new file mode 100644
index 00000000..1488d623
--- /dev/null
+++ b/tutor/app/controllers/assignments_controller.rb
@@ -0,0 +1,39 @@
+class AssignmentsController < ApplicationController
+ # [View List Of Assignments - Story 4.24]
+ # Shows a particular assignment in a course
+ # Parameters:
+ # id: Id of the assignment.
+ # Returns:
+ # The view of the assignmet if found,
+ # redirects to 'public/404' otherwise.
+ # Author: Lin Kassem
+ def show
+ id = params[:id]
+ @assignment = Assignment.find_by_id(id)
+ if @assignment
+ @course = @assignment.course
+ @problems = @assignment.problems
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
+ else
+ render ('public/404')
+ end
+ end
+
+ # [View List Of Assignments - Story 4.24]
+ # Allows the editing of a particular assignment
+ # Parameters:
+ # id: Id of the assignment.
+ # Returns: none
+ # Author: Lin Kassem
+ def edit
+ @assignment = Assignment.find_by_id(params[:id])
+ if @assignment.nil?
+ render "problem_not_found"
+ else
+ @course = @assignment.course
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
+ end
+ end
+end
\ No newline at end of file
diff --git a/tutor/app/controllers/courses_controller.rb b/tutor/app/controllers/courses_controller.rb
index 8881048e..0590b38f 100644
--- a/tutor/app/controllers/courses_controller.rb
+++ b/tutor/app/controllers/courses_controller.rb
@@ -37,6 +37,10 @@ def sign_up
student = Student.find(current_student.id)
if student.courses.find_by_id(@course.id) == nil
student.courses << @course
+ @course.topics.each do |topic|
+ progress = TrackProgression.create(level: 0, topic_id: topic.id)
+ student.progressions << progress
+ end
else
@status = "7"
end
@@ -102,7 +106,7 @@ def new
# none
# Returns:
# none
- # Author: Mohamed Mamdouh
+ # Author: Mohamed Mamdouh + Ahmed Elassuty
def create
@new_course = Course.new
@new_course.name = course_params[:name]
@@ -110,6 +114,7 @@ def create
@new_course.year = course_params[:year]
@new_course.semester = course_params[:semester]
@new_course.description = course_params[:description]
+ @new_course.link = course_params[:link]
@new_course.university = current_lecturer.university
if @new_course.save
current_lecturer.courses << @new_course
@@ -155,7 +160,14 @@ def show
tracks = []
@topics.each do |t|
tracks = tracks + t.tracks
+ end
+ @assignments = @course.assignments
+ assignment_problems = []
+ @assignments.each do |a|
+ assignment_problems = assignment_problems + a.problems
end
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
else
render ('public/404')
end
@@ -212,7 +224,7 @@ def share
private
def course_params
- params.require(:course).permit(:name,:code,:year,:semester,:description)
+ params.require(:course).permit(:name,:code,:year,:semester,:description,:link)
end
# [Share Performance - Story 5.2, 5.13]
diff --git a/tutor/app/controllers/debuggers_controller.rb b/tutor/app/controllers/debuggers_controller.rb
index b3a78bae..d698dd9b 100644
--- a/tutor/app/controllers/debuggers_controller.rb
+++ b/tutor/app/controllers/debuggers_controller.rb
@@ -16,8 +16,10 @@ def start
id = current_student.id
pid = params[:id]
code = params[:code]
+ problem_type = params[:problem_type]
cases = if params[:case] then params[:case] else "" end
- result = SolutionsLayer.debug "java", code, id, pid, cases
+ class_name = params[:class_name]
+ result = SolutionsLayer.debug "java", code, id, pid, problem_type, class_name, cases
render json: result
end
diff --git a/tutor/app/controllers/notes_controller.rb b/tutor/app/controllers/notes_controller.rb
new file mode 100644
index 00000000..086afb97
--- /dev/null
+++ b/tutor/app/controllers/notes_controller.rb
@@ -0,0 +1,72 @@
+class NotesController < ApplicationController
+
+ # [Mark Assignment - 4.29]
+ # creates a new record to Note Table
+ # Parameters:
+ # content: note's content through note_params
+ # solution_id: the id of the solution that the note belongs to
+ # line: integer represents the order of the line that the note belongs to
+ # Returns: none
+ # Author: Abdullrahman Elhusseny
+ def create
+ note = Note.new(note_params)
+ if lecturer_signed_in?
+ note.owner_id = current_lecturer.id
+ note.owner_type = "lecturer"
+ elsif teaching_assistant_signed_in?
+ note.owner_id = current_teaching_assistant.id
+ note_type = "teaching assistant"
+ end
+ if note.save
+ flash.keep[:notice] = "Note added! Hold your cursor over line of code to see the note"
+ else
+ flash.keep[:notice] = "Note wasn't added"
+ end
+ redirect_to :back
+ end
+
+ # [Mark Assignment - 4.29]
+ # updates a record to Note Table
+ # Parameters:
+ # content: note's content through note_params
+ # solution_id: the id of the solution that the note belongs to
+ # line: integer represents the order of the line that the note belongs to
+ # Returns: none
+ # Author: Abdullrahman Elhusseny
+ def update
+ @note = Note.find_by_id(params[:id])
+ if @note.update_attributes(note_params)
+ flash.keep[:notice] = "Note Updated"
+ else
+ flash.keep[:notice] = "Note wasn't updated"
+ end
+ redirect_to :back
+ end
+
+ # [Mark Assignment - 4.29]
+ # Removes a record to Note Table
+ # Parameters:
+ # id: the id of the note to be deleted through params
+ # Returns: none
+ # Author: Abdullrahman Elhusseny
+ def destroy
+ if Note.find_by_id(params[:id]).destroy
+ flash[:notice] = "Note Deleted"
+ redirect_to :back
+ end
+ end
+
+ # [Mark Assignment - Story 4.29]
+ # passes the input of the form as paramaters for create & update action
+ # Parameters:
+ # content: note's content through note_params
+ # solution_id: the id of the solution that the note belongs to
+ # line: integer represents the order of the line that the note belongs to
+ # Returns:
+ # params to create action & update action
+ # Author: Abdullrahman Elhusseny
+ private
+ def note_params
+ params.require(:Note).permit(:content, :solution_id, :line)
+ end
+end
diff --git a/tutor/app/controllers/problems_controller.rb b/tutor/app/controllers/problems_controller.rb
index dc839854..3d8c38d2 100644
--- a/tutor/app/controllers/problems_controller.rb
+++ b/tutor/app/controllers/problems_controller.rb
@@ -63,6 +63,7 @@ def edit
@track = Track.find_by_id(@problem.track_id)
@topic = Topic.find_by_id(@track.topic_id)
@tracks = Track.where(topic_id: @track.topic_id)
+ @snippet = @problem.snippet
else
render ('public/404')
end
@@ -112,7 +113,7 @@ def destroy
# problem_params: a problem's title, description & track_id
# Returns:
# Refreshes divisions in the page using AJAX without refreshing the whole page
- # Author: Abdullrahman Elhusseny
+ # Author: Abdullrahman Elhusseny + Rami Khalil
def update
@problem = Problem.find_by_id(params[:id])
@track = Track.find_by_id(@problem.track_id)
@@ -123,13 +124,16 @@ def update
@message = "Description updated"
elsif problem_params[:track_id].to_i != @problem.track_id
@message = "Problem is moved to Track #{problem_params[:track_id]}"
+ elsif problem_params[:snippet] != @problem.snippet
+ @message = "Problem code template updated."
else
- flash.keep[:notice] = "You have entered the same paramater no change has been made!"
+ flash.keep[:notice] = "You have entered the same parameters. No change has been made!"
end
if problem_params[:track_id].to_i == @problem.track_id
if problem_params[:title] != @problem.title ||
- problem_params[:description] != @problem.description
+ problem_params[:description] != @problem.description ||
+ problem_params[:snippet] != @problem.snippet
begin
if @problem.update_attributes(problem_params)
flash.keep[:notice] = @message
@@ -204,16 +208,13 @@ def done
# [Add Problem - 4.4]
# Passes the input of the form as paramaters for create action to use it
- # Parameters:
- # title: problem's title
- # description: problem's description
- # track_id: problem's track id
+ # Parameters: none
# Returns:
- # Params to create action & update action
- # Author: Abdullrahman Elhusseny
+ # Filtered parameter list for problems
+ # Author: Abdullrahman Elhusseny + Rami Khalil
private
def problem_params
- params.require(:Problem).permit(:title, :description, :track_id)
+ params.require(:Problem).permit(:title, :description, :track_id, :snippet)
end
end
\ No newline at end of file
diff --git a/tutor/app/controllers/solutions_controller.rb b/tutor/app/controllers/solutions_controller.rb
index 99e7cb36..5d562e68 100644
--- a/tutor/app/controllers/solutions_controller.rb
+++ b/tutor/app/controllers/solutions_controller.rb
@@ -13,7 +13,10 @@ def create
student = current_student.id
problem = param[:problem_id]
time = param[:time]
- result = SolutionsLayer.validate "java", code, student, problem, time
+ problem_type = param[:problem_type]
+ class_name = param[:class_name]
+ result = SolutionsLayer.validate("java", code, student, problem,
+ problem_type, class_name, time)
render json: result
end
@@ -31,14 +34,16 @@ def execute
id = current_student.id
pid = params[:problem_id]
code = params[:code]
+ problem_type = params[:problem_type]
+ class_name = params[:class_name]
cases = if params[:input] then params[:input] else "" end
- result = SolutionsLayer.execute "java", code, id, pid, cases
+ result = SolutionsLayer.execute("java", code, id, pid,
+ problem_type, class_name, cases)
render json: result
end
# [Compiler: Compile - Story 3.4]
# Creates a soution for the current problem in the database and compiles it.
- # Then it places the previous code and the compilation results and feedback in the flash hash.
# Parameters:
# solution_params: submitted from the form_for
# Returns: none
@@ -48,10 +53,69 @@ def compile_solution
code = param[:code]
student = current_student.id
problem = param[:problem_id]
- compiler_feedback = SolutionsLayer.compile "java", code, student, problem
+ problem_type = param[:problem_type]
+ class_name = param[:class_name]
+ compiler_feedback = SolutionsLayer.compile("java", code, student,
+ problem, problem_type, class_name)
render json: compiler_feedback
end
+ # [Mark Solution - Story 4.29]
+ # Allows a TA to mark a solution of a student
+ # Parameters:
+ # submission_id: the id of the solution to be marked
+ # Returns: none
+ # Author: Abdullrahman Elhusseny
+ def mark_solution
+ if lecturer_signed_in? || teaching_assistant_signed_in?
+ @solution = Solution.find_by_id(params[:submission_id])
+ if !@solution.blank?
+ @lines = @solution.code.split("\n")
+ @notes = Hash.new
+ @counter = 0
+ @lines.each do |line|
+ @counter+= 1
+ @notes[@counter]= Note.where(solution_id: @solution.id, line: @counter).last
+ end
+ @student = Student.find_by_id(@solution.student_id)
+ @problem = AssignmentProblem.find_by_id(@solution.problem_id)
+ @course = @problem.assignment.course
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
+ if !@can_edit
+ render ('public/404')
+ end
+ else
+ render ('public/404')
+ end
+ else
+ render ('public/404')
+ end
+ end
+
+ # [Mark Solution - Story 4.29]
+ # Allows a TA to view all submitted solutions to a specific assignment
+ # Parameters:
+ # problem_id: the id of the assignment problem to view its submissions
+ # Returns: none
+ # Author: Abdullrahman Elhusseny
+ def view_submissions
+ @problem = AssignmentProblem.find_by_id(params[:problem_id])
+ @submissions = @problem.solutions.group(:student_id)
+ @students = Hash.new
+ @counter = 0
+ @submissions.each do |submission|
+ @counter+=1
+ @students[@counter] = Student.find_by_id(submission.student_id)
+ end
+ @course = @problem.assignment.course
+ @can_edit = @course.can_edit(current_lecturer)
+ @can_edit||= @course.can_edit(current_teaching_assistant)
+ if !@can_edit
+ render ('public/404')
+ end
+ end
+
private
# [Code Editor: Write Code - Story 3.3]
# Permits the id of the problem, code from the form_for
@@ -62,7 +126,7 @@ def compile_solution
# none
# Author: MOHAMEDSAEED
def solution_params
- params.permit(:code, :problem_id, :time)
+ params.permit(:code, :problem_id, :time, :class_name, :problem_type)
end
# [Compiler: Test - Story 3.15]
diff --git a/tutor/app/controllers/students_controller.rb b/tutor/app/controllers/students_controller.rb
index c7ca2dc0..75614bf4 100644
--- a/tutor/app/controllers/students_controller.rb
+++ b/tutor/app/controllers/students_controller.rb
@@ -14,9 +14,8 @@ def get_performance
@solved = Attempt.where(student_id:params[:student_id], success:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count
@failed = Attempt.where(student_id:params[:student_id], failure:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count
@incomplete = Attempt.where(student_id:params[:student_id], incomplete:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count
- #Recheck here for the missing DB
- #@solved_contest = Attempt.where(student_id:params[:student_id], success:true).joins(contest_problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count
- #@failed_contest = Attempt.where(student_id:params[:student_id], failure:true).joins(contest_problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id").count
+ @solved_contest = ContesProgress.where(student_id:params[:student_id], status:true).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id").count
+ @solved_contest = ContesProgress.where(student_id:params[:student_id], status:false).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id").count
end
def list_courses
@@ -55,8 +54,37 @@ def incomplete_problems
@failure_list = Attempt.where(student_id:params[:student_id], icnomplete:true).joins(problem: {track: :topic}).where('topics.course_id' => params[:course_id]).select("DISTINCT problem_id")
end
+ # [Contest statistics - Story 5.30]
+ # This method retrieve variables from tables in the database
+ # Parameters:
+ # params: A hash of the request URL attributes
+ # Returns:
+ # An array of solved contest problems
+ # Author: Mahdi
+ def solved_contest_problems
+ @solved_contest_list = ContesProgress.where(student_id:params[:student_id], status:true).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id")
+ end
+
+ # [Performance of a student - Story 5.3]
+ # This method retrieve variables from tables in the database
+ # Parameters:
+ # params: A hash of the request URL attributes
+ # Returns:
+ # An array of failed problems
+ # Author: Mahdi
+ def failed_contest_problems
+ @failure_list = ContesProgress.where(student_id:params[:student_id], status:false).joins(:contest).where('contests.course_id' => params[:course_id]).select("DISTINCT cproblem_id")
+ end
+
+ # [Contest Registrants - Story 5.27]
+ # This method retrieve variables from tables in the database
+ # Parameters:
+ # params: A hash of the request URL attributes
+ # Returns:
+ # An array of contest registrants
+ # Author: Mahdi
def view_registrants
- @contest_registrants = Contest.find_by_id(2).students
+ @contest_registrants = Contest.find_by_id(params[:id]).students
end
# [Profile - Story 5.8]
diff --git a/tutor/app/controllers/topics_controller.rb b/tutor/app/controllers/topics_controller.rb
index 8e37bba1..52f51af4 100644
--- a/tutor/app/controllers/topics_controller.rb
+++ b/tutor/app/controllers/topics_controller.rb
@@ -38,6 +38,22 @@ def new
@new_topic = Topic.new
end
+ # [Delete Topic - Story 1.34]
+ # This action takes the topic id, remove it from the database
+ # and then redirects the user to the show courses page accompanied
+ # with a "Topic deleted" message.
+ # Parameters:
+ # params[:id]: The current topic's id
+ # Returns: none
+ # Author: Mohamed Saeed
+ def destroy
+ topic = Topic.find_by_id(params[:id])
+ topic.destroy
+ flash[:success_deletion] = "Topic deleted."
+ @course = topic.course
+ redirect_to :controller => 'courses', :action => 'show', :id => @course.id
+ end
+
# [Specify Topics - Story 1.2]
# Description: This action takes the passed course id and assings
# the respective topics of that course to an instance
@@ -62,23 +78,24 @@ def index
# topic_params[]: A list that has all fields entered by the user to in the
# create_topic_form
# Returns:
- # flash[:notice]: A message indicating the success or failure of the creation
- # Author: Ahmed Akram
+ # flash[:notice]: A message indicating the success or failure of the creation
+ # Author: Ahmed Akram + Mohamed Saeed
def create
@new_topic = Topic.new
@new_topic.title = topic_params[:title]
@new_topic.description = topic_params[:description]
bool = @new_topic.save
- if bool == true
+ if bool == true
flash[:notice] = "Topic successfully created"
@course = Course.find(course_params[:course_id])
@course.topics << @new_topic
redirect_to :action => 'index'
+ Topic.update_track_progression @new_topic
else
if @new_topic.errors.any?
flash[:notice] = @new_topic.errors.full_messages.first
end
- render :action => 'new'
+ render :action => 'new'
end
end
diff --git a/tutor/app/controllers/utilities_controller.rb b/tutor/app/controllers/utilities_controller.rb
index 2f31e5a1..df079999 100644
--- a/tutor/app/controllers/utilities_controller.rb
+++ b/tutor/app/controllers/utilities_controller.rb
@@ -7,9 +7,35 @@ class UtilitiesController < ApplicationController
# Author: Ahmed Elassuty
def simple_search
@lecturers = Lecturer.simple_search(params[:search])
+ .paginate(:page => params[:page], :per_page => 3)
@students = Student.simple_search(params[:search])
+ .paginate(:page => params[:page], :per_page => 3)
@teaching_assisstants = TeachingAssistant.simple_search(params[:search])
+ .paginate(:page => params[:page], :per_page => 3)
@courses = Course.simple_search(params[:search])
+ .paginate(:page => params[:page], :per_page => 3)
+ @objects = @courses
+ if @lecturers.count > @students.count
+ if @lecturers.count > @teaching_assisstants.count
+ if @lecturers.count > @courses.count
+ @objects = @lecturers
+ end
+ else
+ if @teaching_assisstants.count > @courses.count
+ @objects = @teaching_assistants
+ end
+ end
+ else
+ if @students.count > @teaching_assisstants.count
+ if @students.count > @courses.count
+ @objects = @students
+ end
+ else
+ if @teaching_assisstants.count > @courses.count
+ @objects = @teaching_assisstants
+ end
+ end
+ end
respond_to do |format|
format.js
format.html
@@ -52,11 +78,16 @@ def advanced_search
# Returns: A hashes with search results according to the keyword
# Author: Ahmed Elassuty
def auto_complete
- @objects = Lecturer.simple_search(params[:q]).take(1)
- respond_to do |format|
- format.json {render :template => 'utilities/auto_complete',
- :formats => [], :handlers => [:json_builder], :layout=>false}
- end
+ objects = Lecturer.simple_search(params[:term]).take(2)
+ auto_complete = []
+ auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil?
+ objects = Student.simple_search(params[:term]).take(2)
+ auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil?
+ objects = TeachingAssistant.simple_search(params[:term]).take(2)
+ auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil?
+ objects = Course.simple_search(params[:term]).take(2)
+ auto_complete = auto_complete.concat(objects.map(&:name)) unless objects.nil?
+ render json: auto_complete
end
-end
+end
\ No newline at end of file
diff --git a/tutor/app/helpers/assignment_problems_helper.rb b/tutor/app/helpers/assignment_problems_helper.rb
new file mode 100644
index 00000000..16025111
--- /dev/null
+++ b/tutor/app/helpers/assignment_problems_helper.rb
@@ -0,0 +1,2 @@
+module AssignmentProblemsHelper
+end
diff --git a/tutor/app/helpers/assignments_helper.rb b/tutor/app/helpers/assignments_helper.rb
new file mode 100644
index 00000000..6f7c33b9
--- /dev/null
+++ b/tutor/app/helpers/assignments_helper.rb
@@ -0,0 +1,2 @@
+module AssignmentsHelper
+end
diff --git a/tutor/app/helpers/notes_helper.rb b/tutor/app/helpers/notes_helper.rb
new file mode 100644
index 00000000..8078f730
--- /dev/null
+++ b/tutor/app/helpers/notes_helper.rb
@@ -0,0 +1,2 @@
+module NotesHelper
+end
diff --git a/tutor/app/models/admin.rb b/tutor/app/models/admin.rb
deleted file mode 100644
index fe0bd288..00000000
--- a/tutor/app/models/admin.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class Admin < ActiveRecord::Base
-
- #Validations
-
- #Relations
-
- #Scoops
-
- #Methods
-
-end
diff --git a/tutor/app/models/admin_user.rb b/tutor/app/models/admin_user.rb
new file mode 100644
index 00000000..318ec397
--- /dev/null
+++ b/tutor/app/models/admin_user.rb
@@ -0,0 +1,6 @@
+class AdminUser < ActiveRecord::Base
+ # Include default devise modules. Others available are:
+ # :confirmable, :lockable, :timeoutable and :omniauthable
+ devise :database_authenticatable,
+ :recoverable, :rememberable, :trackable, :validatable
+end
diff --git a/tutor/app/models/assignment.rb b/tutor/app/models/assignment.rb
index 826e990f..ffb88c4e 100644
--- a/tutor/app/models/assignment.rb
+++ b/tutor/app/models/assignment.rb
@@ -1,6 +1,7 @@
class Assignment < ActiveRecord::Base
#Validations
+ validates :title, presence:true
#Relations
belongs_to :course
diff --git a/tutor/app/models/assignment_problem.rb b/tutor/app/models/assignment_problem.rb
index 903336ef..ad007c41 100644
--- a/tutor/app/models/assignment_problem.rb
+++ b/tutor/app/models/assignment_problem.rb
@@ -1,6 +1,7 @@
class AssignmentProblem < ActiveRecord::Base
#Validations
+ validates :title, presence:true
#Relations
belongs_to :owner, polymorphic: true
diff --git a/tutor/app/models/course.rb b/tutor/app/models/course.rb
index 1207b51f..ecfd9993 100644
--- a/tutor/app/models/course.rb
+++ b/tutor/app/models/course.rb
@@ -5,7 +5,8 @@ class Course < ActiveRecord::Base
include Tire::Model::Callbacks
#Validations
- validates :name, presence: true
+ validates :name, presence: true
+ validates :link, :format => /\Ahttps?:\/\/((w{3})[.])?([\w]|-)+(\.com|\.org|\.guc|\.edu|\.eg|\.gov|\.biz|\.info|\.net)+((\?|#|\/)([\S])*)?\z/
validates :description, presence: true
validates :code, presence: true
validates :year, presence: true, numericality: {only_integer: true, greater_than_or_equal_to: Date.today.year,
diff --git a/tutor/app/models/java_compiler.rb b/tutor/app/models/java_compiler.rb
index 9803be7d..0dec7ab3 100644
--- a/tutor/app/models/java_compiler.rb
+++ b/tutor/app/models/java_compiler.rb
@@ -1,10 +1,11 @@
-class JavaCompiler
+class JavaCompiler < ActiveRecord::Base
# Methods
# [Compiler: Compile - Story 3.4]
- # Writes the given code to a .java file with the name st[student_id]pr[problem_id]so[solution_id]
- # using java_file_name/2. Then it compiles that file and returns the compiler's feedback.
+ # Writes the given code to a .java file in a folder with the name
+ # st[student_id]pr[problem_id]so[solution_id] using folder_name/0.
+ # Then it compiles that file and returns the compiler's feedback.
# Parameters:
# solution: The submitted solution.
# code: The code to be compiled.
@@ -12,26 +13,25 @@ class JavaCompiler
# The compiler's feedback.
# Author: Ahmed Moataz
def self.compile(solution, code)
- %x[ #{'mkdir -p ' + Solution::JAVA_PATH} ]
- file_name = solution.java_file_name(true, true)
- File.open(file_name, 'w') { |file| file.write(code) }
- %x[ #{'mkdir -p ' + Solution::CLASS_PATH} ]
- return %x[ #{'javac -g -d ' + Solution::CLASS_PATH + ' ' + file_name + ' 2>&1'} ]
+ return solution.check_class_name if solution.check_class_name != ""
+ folder_name = solution.folder_name
+ file_path = solution.file_path
+ %x[ #{'mkdir -p ' + Solution::SOLUTION_PATH + folder_name} ]
+ File.open(file_path, 'w') { |file| file.write(code) }
+ return %x[ #{'javac -g ' + file_path + ' 2>&1'} ]
end
# [Compiler: Compile - Story 3.4]
- # Adds the class enclosure to the submitted code using append_class/4.
- # Then compiles it using compile/4 and passes its result and feedback in a list.
+ # Compiles the solution's code using compile/2
+ # and returns its result and feedback in a list.
# Parameters:
# solution: The submitted solution.
# Returns:
- # A list. The first element is a boolean indicating the result of the compilation process.
- # The second element contains the compilation errors if any.
- # The third element contains the compiled code.
+ # A list. The first element is a boolean indicating the result of the
+ # compilation process. The second element contains the compilation errors if any.
# Author: Ahmed Moataz
def self.compiler_feedback(solution)
- new_code = append_class(solution)
- feedback = compile(solution, new_code)
+ feedback = compile(solution, solution.code)
if feedback == ""
return {success: true, errors: nil}
else
@@ -41,29 +41,16 @@ def self.compiler_feedback(solution)
end
# [Compiler: Compile - Story 3.4]
- # Appends the class enclosure to the to submitted code.
- # Parameters:
- # solution: The submitted solution.
- # Returns:
- # The code with the class enclosure.
- # Author: Ahmed Moataz
- def self.append_class(solution)
- name = solution.file_name
- code = solution.code
- return 'public class ' + name + " {\n" + code
- end
-
- # [Compiler: Compile - Story 3.4]
- # Changes the error headers to CoolSoft.
+ # Changes the error headers to the solution's class name.
# Parameters:
# solution: The submitted solution.
# feedback: The compiler's feedback.
# Returns:
- # The compiler's feedback with the error headers changed to CoolSoft.
+ # The compiler's feedback with the error headers changed to the solution's class name.
# Author: Ahmed Moataz
def self.change_error_headers(solution, feedback)
- header = solution.file_name
- return feedback.gsub('students_solutions/Java/' + header, 'CoolSoft').gsub(header, 'CoolSoft')
+ header = Solution::SOLUTION_PATH + solution.folder_name + solution.class_name
+ return feedback.gsub(header, solution.class_name).gsub(header, solution.class_name)
end
end
\ No newline at end of file
diff --git a/tutor/app/models/java_debugger.rb b/tutor/app/models/java_debugger.rb
index 3a7e19fb..ebb632fb 100644
--- a/tutor/app/models/java_debugger.rb
+++ b/tutor/app/models/java_debugger.rb
@@ -1,4 +1,5 @@
require "open3"
+require "fileutils"
class JavaDebugger
#Methods
@@ -60,33 +61,32 @@ def input(input)
# input : The arguments to be passed to the main method
# Returns: A List of all 100 steps ahead
# Authors: Mussab ElDash + Rami Khalil
- def start(class_name, input)
+ def start(class_name, input, time = 10)
$all = []
- source_path = "#{Rails.root.to_s}/#{Solution::JAVA_PATH}"
- Dir.chdir(Solution::CLASS_PATH){
- begin
- $input, $output, $error, $wait_thread = Open3.popen3("jdb",
- "-sourcepath", source_path, class_name, *input)
- buffer_until_ready
- input "stop in #{class_name}.main"
- buffer_until_ready
- input "run"
- nums = get_line
- locals = get_variables
- nums[:locals] = locals
- $all << nums
+ status = "The debugging session was successful."
+ begin
+ $input, $output, $error, $wait_thread = Open3.popen3("jdb", class_name, *input)
+ buffer_until_ready
+ input "stop in #{class_name}.main"
+ buffer_until_ready
+ input "run"
+ nums = get_line
+ locals = get_variables
+ nums[:locals] = locals
+ $all << nums
+ status = TimeLimit.start(time) {
debug
- rescue => e
- unless e.message === 'Exited'
- return false
- end
+ }
+ rescue => e
+ unless e.message === 'Exited'
+ return false
end
- }
+ end
begin
Process.kill("TERM", $wait_thread.pid)
rescue => e
end
- return $all
+ return $all, status
end
# [Debugger: Debug - Story 3.6]
@@ -210,13 +210,15 @@ def get_exception
# Author: Mussab ElDash
def self.debug(solution, input)
debugger = JavaDebugger.new
- class_name = solution.file_name
- debugging = debugger.start(class_name, input.split(" "))
- java_file = solution.java_file_name true, true
- class_file = solution.class_file_name true, true
- File.delete(java_file)
- File.delete(class_file)
- return {:success => true, data: debugging}
+ class_name = solution.class_name
+ folder_name = Solution::SOLUTION_PATH + solution.folder_name
+ debugging = ""
+ status = ""
+ Dir.chdir(folder_name) {
+ debugging, status = debugger.start(class_name, input.split(" "))
+ }
+ FileUtils.rm_rf(folder_name)
+ return {:success => true, data: debugging, status: status}
end
# [Debugger: View Variables - Story 3.7]
diff --git a/tutor/app/models/java_executer.rb b/tutor/app/models/java_executer.rb
index 7bff6e38..6b466bcd 100644
--- a/tutor/app/models/java_executer.rb
+++ b/tutor/app/models/java_executer.rb
@@ -10,11 +10,13 @@ class JavaExecuter
# Author: Ahmed Akram
def self.execute(sol, input)
@solution = sol
- class_path = Solution::CLASS_PATH
- file_name = @solution.file_name
+ file_name = @solution.class_name
+ folder_name = @solution.folder_name
validity = check_input_validity(input, @solution.problem.id)
if validity[:status]
- @execute_res = %x[#{'java -cp ' + class_path + ' ' + file_name + ' ' + input + ' 2>&1'}]
+ Dir.chdir(Solution::SOLUTION_PATH + folder_name) {
+ @execute_res = %x[#{'java ' + file_name + ' ' + input + ' 2>&1'}]
+ }
if @execute_res.include?("Exception")
return {executer_feedback: false, executer_output: get_runtime_error()}
else
diff --git a/tutor/app/models/lecturer.rb b/tutor/app/models/lecturer.rb
index 6dbe6b7f..bc25ac1c 100644
--- a/tutor/app/models/lecturer.rb
+++ b/tutor/app/models/lecturer.rb
@@ -53,6 +53,8 @@ class Lecturer < ActiveRecord::Base
has_many :grades, as: :editor
has_many :resources, as: :owner
+ has_many :notes, as: :owner
+ has_many :notifications, as: :receiver
#Methods
# [Advanced Search - Story 1.23]
diff --git a/tutor/app/models/note.rb b/tutor/app/models/note.rb
new file mode 100644
index 00000000..9610e2a6
--- /dev/null
+++ b/tutor/app/models/note.rb
@@ -0,0 +1,13 @@
+class Note < ActiveRecord::Base
+
+ #Validations
+ validates_presence_of :content
+ validates_presence_of :solution_id
+ validates_presence_of :line
+ #Relations
+ belongs_to :owner, polymorphic: true
+ belongs_to :problems
+
+ #Methods
+
+end
diff --git a/tutor/app/models/notification.rb b/tutor/app/models/notification.rb
new file mode 100644
index 00000000..348f58bd
--- /dev/null
+++ b/tutor/app/models/notification.rb
@@ -0,0 +1,12 @@
+class Notification < ActiveRecord::Base
+
+ #Validations
+
+ #Relations
+ belongs_to :receiver, polymorphic: true
+
+ #Scoops
+
+ #Methods
+
+end
diff --git a/tutor/app/models/solution.rb b/tutor/app/models/solution.rb
index 77b56eb3..275a1ff7 100644
--- a/tutor/app/models/solution.rb
+++ b/tutor/app/models/solution.rb
@@ -9,6 +9,8 @@ class Solution < ActiveRecord::Base
belongs_to :assignment_problem, class_name:"AssignmentProblem", polymorphic: true
belongs_to :contest_problem, class_name:"Cproblem", polymorphic: true
+ has_many :notes, dependent: :destroy
+
#Methods
# [Compiler: Validate - Story 3.5]
# Checks the validity of a submitted solution
@@ -21,41 +23,36 @@ class Solution < ActiveRecord::Base
# Author: MOHAMEDSAEED
def self.validate(solution, test_cases)
response = []
- compiler_status = JavaCompiler.compiler_feedback(solution)
- if compiler_status[:success]
- test_cases.each do |testcase|
- input = testcase.input
- expected_output = testcase.output
- runtime_check = JavaExecuter.execute(solution, input)
- if(runtime_check[:executer_feedback])
- output = runtime_check[:executer_output][:message]
- if (output != expected_output)
- if(output.to_s.empty?)
- output = "Empty"
- end
- response << {success: false, test_case: input,
- response: "Logic error: Expected output: " +
- expected_output.to_s.strip + ", but your output was: " + output}
- unless solution.status == 4
- solution.status = 5
- end
- else
- response << {success: true, test_case: input,
- response: "Passed!"}
- unless solution.status == 4 | 5
- solution.status = 1
- end
+ test_cases.each do |testcase|
+ input = testcase.input
+ expected_output = testcase.output
+ runtime_check = JavaExecuter.execute(solution, input)
+ if(runtime_check[:executer_feedback])
+ output = runtime_check[:executer_output][:message]
+ if (output != expected_output)
+ if(output.to_s.empty?)
+ output = "Empty"
end
- else
- runtime_error = runtime_check[:executer_output]
- explanation = get_response(runtime_error)
- solution.status = 4
response << {success: false, test_case: input,
- response: "Runtime error: " + explanation}
+ response: "Logic error: Expected output: " +
+ expected_output.to_s.strip + ", but your output was: " + output}
+ unless solution.status == 4
+ solution.status = 5
+ end
+ else
+ response << {success: true, test_case: input,
+ response: "Passed!"}
+ unless solution.status == 4 | 5
+ solution.status = 1
+ end
end
+ else
+ runtime_error = runtime_check[:executer_output]
+ explanation = get_response(runtime_error)
+ solution.status = 4
+ response << {success: false, test_case: input,
+ response: "Runtime error: " + explanation}
end
- else
- return {compiler_error: true, compiler_output: compiler_status}
end
solution.save
return response
@@ -154,6 +151,50 @@ def class_file_name(prepend_path = false, append_extension = false)
return jfile_name
end
+ # [Compiler: Compile - Story 3.4]
+ # Returns the folder's name, which the solution's files will be placed into.
+ # Parameters: none
+ # Returns:
+ # The folder's name.
+ # Author: Ahmed Moataz
+ def folder_name
+ return 'st' + student_id.to_s + 'pr' + problem_id.to_s + 'so' + id.to_s + '/'
+ end
+
+ # [Compiler: Compile - Story 3.4]
+ # Returns the file path of the solution's files.
+ # Parameters:
+ # append_extension: A boolean value indicating if the file extension
+ # should be appended or not.
+ # Returns:
+ # path: The file path.
+ # Author: Ahmed Moataz
+ def file_path(append_extension = true)
+ path = SOLUTION_PATH + folder_name + class_name
+ path += '.java' if append_extension
+ return path
+ end
+
+ # [Compiler: Compile - Story 3.4]
+ # Checks if the class_name contains special characters, line breaks, or white spaces.
+ # Parameters: none
+ # Returns:
+ # A String containing feedback on what it found.
+ # Author: Ahmed Moataz
+ def check_class_name
+ special = "?<>',?[]}{=-)(*&|^%$#`~{}/\\:;"
+ regex = /[#{special.gsub(/./){|char| "\\#{char}"}}]/
+ if class_name =~ regex
+ return "The file name cannot contain special characters"
+ elsif class_name.include?("\n")
+ return "The file name cannot contain line breaks"
+ elsif class_name.include?(" ") || class_name.include?("\t")
+ return "The file name cannot contain white spaces"
+ else
+ return ""
+ end
+ end
+
#Constants
STATUS_SUBMITTED = 0
STATUS_ACCEPTED = 1
@@ -163,5 +204,6 @@ def class_file_name(prepend_path = false, append_extension = false)
STATUS_EXECUTED_WITH_LOGIC_ERRORS = 5
JAVA_PATH = 'students_solutions/Java/'
CLASS_PATH = 'students_solutions/Class/'
+ SOLUTION_PATH = 'students_solutions/'
end
\ No newline at end of file
diff --git a/tutor/app/models/solutions_layer.rb b/tutor/app/models/solutions_layer.rb
index 5afb0691..c67ffe0e 100644
--- a/tutor/app/models/solutions_layer.rb
+++ b/tutor/app/models/solutions_layer.rb
@@ -11,13 +11,13 @@ class SolutionsLayer
# Returns:
# A hash with the status of the execution
# Author: Mussab ElDash
- def self.execute lang, code, student_id, problem_id, cases
+ def self.execute lang, code, student_id, problem_id, problem_type, class_name, cases
executer = get_executer lang
unless executer
return false
end
compiler = get_compiler lang
- solution = get_solution code, student_id, problem_id
+ solution = get_solution code, student_id, problem_id, problem_type, class_name
compile_status = {}
if compiler
compile_status = compiler.compiler_feedback(solution)
@@ -38,9 +38,9 @@ def self.execute lang, code, student_id, problem_id, cases
# Returns:
# The compile status
# Author: Mussab ElDash
- def self.compile lang, code, student_id, problem_id
+ def self.compile lang, code, student_id, problem_id, problem_type, class_name
compiler = get_compiler lang
- solution = get_solution code, student_id, problem_id
+ solution = get_solution code, student_id, problem_id, problem_type, class_name
if compiler
feed_back = compiler.compiler_feedback solution
if feed_back[:success]
@@ -65,8 +65,8 @@ def self.compile lang, code, student_id, problem_id
# Returns:
# A hash with the validation status
# Author: Mussab ElDash
- def self.validate lang, code, student_id, problem_id, time
- solution = get_solution code, student_id, problem_id
+ def self.validate lang, code, student_id, problem_id, problem_type, class_name, time
+ solution = get_solution code, student_id, problem_id, problem_type, class_name
solution.time = time
test_cases = solution.problem.test_cases
compiler = get_compiler lang
@@ -90,8 +90,8 @@ def self.validate lang, code, student_id, problem_id, time
# Returns:
# A hash with the debugging results
# Author: Mussab ElDash
- def self.debug lang, code, student_id, problem_id, cases
- solution = get_solution code, student_id, problem_id
+ def self.debug lang, code, student_id, problem_id, problem_type, class_name, cases
+ solution = get_solution code, student_id, problem_id, problem_type, class_name
debugger = get_debugger lang
compiler = get_compiler lang
if debugger
@@ -116,9 +116,10 @@ def self.debug lang, code, student_id, problem_id, cases
# Returns:
# A new Solution
# Author: Mussab ElDash
- def self.get_solution code, student_id, problem_id
+ def self.get_solution code, student_id, problem_id, type = "Problem", class_name
+ type = type.capitalize
solution = Solution.create({code: code, student_id: student_id,
- problem_id: problem_id})
+ problem_id: problem_id, problem_type: type, class_name: class_name})
return solution
end
diff --git a/tutor/app/models/student.rb b/tutor/app/models/student.rb
index 2e24dcbb..e49ab39d 100644
--- a/tutor/app/models/student.rb
+++ b/tutor/app/models/student.rb
@@ -47,6 +47,7 @@ class Student < ActiveRecord::Base
has_and_belongs_to_many :contests, class_name:"Contest", join_table: "contests_students"
has_many :grades
+ has_many :notifications, as: :receiver
#Methods
diff --git a/tutor/app/models/teaching_assistant.rb b/tutor/app/models/teaching_assistant.rb
index 0a39a462..05609df5 100644
--- a/tutor/app/models/teaching_assistant.rb
+++ b/tutor/app/models/teaching_assistant.rb
@@ -55,6 +55,8 @@ class TeachingAssistant < ActiveRecord::Base
has_many :grades, as: :editor
has_many :resources, as: :owner
+ has_many :notes, as: :owner
+ has_many :notifications, as: :receiver
#Methods
# [Advanced Search - Story 1.23]
diff --git a/tutor/app/models/time_limit.rb b/tutor/app/models/time_limit.rb
new file mode 100644
index 00000000..a765c15e
--- /dev/null
+++ b/tutor/app/models/time_limit.rb
@@ -0,0 +1,48 @@
+require 'timeout'
+require 'open3'
+class TimeLimit
+
+ # [Terminate - Story X.9]
+ # Run the system call given and terminate it or Terminate the given block
+ # after the time given is reached while running
+ # Parameters:
+ # time: The time for the program not to exceed
+ # args: The system call to be executed
+ # Returns:
+ # In case there is an args passes it will return the output and error streams lines
+ # else none
+ # Author: Mussab ElDash
+ def self.start time, args = ""
+ unless args.empty?
+ stdin, stdout, stderr, wait_thr = Open3.popen3 args
+ pid = wait_thr.pid
+ terminated = false
+ begin
+ status = Timeout::timeout(time) {
+ Process.wait(pid)
+ }
+ rescue => e
+ end
+ stdin.close
+ begin
+ Process.kill("TERM", pid)
+ rescue => e
+ terminated = true
+ end
+ result_out = stdout.read
+ result_err = stderr.read
+ return terminated, result_out, result_err
+ else
+ begin
+ Timeout::timeout(time) {
+ yield
+ }
+ rescue => e
+ unless e.message === 'Exited'
+ return "The debugging session timed out."
+ end
+ end
+ return "The debugging session was successful."
+ end
+ end
+end
\ No newline at end of file
diff --git a/tutor/app/models/topic.rb b/tutor/app/models/topic.rb
index b232b441..2db3f746 100644
--- a/tutor/app/models/topic.rb
+++ b/tutor/app/models/topic.rb
@@ -42,4 +42,20 @@ def self.search(params)
end
end
end
+
+ # [Course Sign-Up - Story 2.6]
+ # Update the the progress of all students for a topic in a
+ # specfic course
+ # Parameters:
+ # Topic: the topic in which the progress of students should be updated
+ # Returns: none
+ # Author: Mohamed Saeed
+ def self.update_track_progression topic
+ course = Course.find_by_id(topic.course_id)
+ course.students.each do |student|
+ progress = TrackProgression.create(level: 0, topic_id: topic.id)
+ student.progressions << progress
+ end
+ end
+
end
diff --git a/tutor/app/models/track.rb b/tutor/app/models/track.rb
index 40badeaf..419f0d15 100644
--- a/tutor/app/models/track.rb
+++ b/tutor/app/models/track.rb
@@ -13,4 +13,49 @@ class Track < ActiveRecord::Base
#Scoops
#Methods
+
+ # [Create Track - Story 4.1]
+ # Get a text difficulty of this track
+ # Parameters: none
+ # Returns:
+ # A string according to the current difficulty of this track
+ # Author: Mussab ElDash
+ def get_difficulty_name
+ if difficulty == 0
+ return "Very Easy"
+ elsif difficulty == 1
+ return "Easy"
+ elsif difficulty == 2
+ return "Medium"
+ elsif difficulty == 3
+ return "Hard"
+ elsif difficulty == 4
+ return "Very Hard"
+ else
+ return "Not Supported"
+ end
+ end
+
+ # [Create Track - Story 4.1]
+ # Get a text difficulty of the number given
+ # Parameters:
+ # difficulty: The difficulty that be converted to string and by default its -1
+ # Returns:
+ # A string according to the number given
+ # Author: Mussab ElDash
+ def self.get_difficulty_name difficulty = -1
+ if difficulty == 0
+ return "Very Easy"
+ elsif difficulty == 1
+ return "Easy"
+ elsif difficulty == 2
+ return "Medium"
+ elsif difficulty == 3
+ return "Hard"
+ elsif difficulty == 4
+ return "Very Hard"
+ else
+ return "Not Supported"
+ end
+ end
end
diff --git a/tutor/app/views/assignment_problems/edit.html.erb b/tutor/app/views/assignment_problems/edit.html.erb
new file mode 100644
index 00000000..6b7f10e8
--- /dev/null
+++ b/tutor/app/views/assignment_problems/edit.html.erb
@@ -0,0 +1 @@
+
Edit assignment problem Page
\ No newline at end of file
diff --git a/tutor/app/views/assignment_problems/show.html.erb b/tutor/app/views/assignment_problems/show.html.erb
new file mode 100644
index 00000000..04ab13a0
--- /dev/null
+++ b/tutor/app/views/assignment_problems/show.html.erb
@@ -0,0 +1,25 @@
+\
+\
+
+
+ <% end %>
+ <% if !@can_edit %>
+ <%= render partial: "solutions/new" %>
+ <% end %>
+
+
+
+
\ No newline at end of file
diff --git a/tutor/app/views/assignments/edit.html.erb b/tutor/app/views/assignments/edit.html.erb
new file mode 100644
index 00000000..07405e76
--- /dev/null
+++ b/tutor/app/views/assignments/edit.html.erb
@@ -0,0 +1 @@
+
Edit Assignment Page
\ No newline at end of file
diff --git a/tutor/app/views/assignments/show.html.erb b/tutor/app/views/assignments/show.html.erb
new file mode 100644
index 00000000..a48ff9bf
--- /dev/null
+++ b/tutor/app/views/assignments/show.html.erb
@@ -0,0 +1,46 @@
+
-<%end%>
+<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/utilities/_simple_search.html.erb b/tutor/app/views/utilities/_simple_search.html.erb
index e5b03226..e9ba6a66 100644
--- a/tutor/app/views/utilities/_simple_search.html.erb
+++ b/tutor/app/views/utilities/_simple_search.html.erb
@@ -7,32 +7,30 @@
\ No newline at end of file
diff --git a/tutor/app/views/utilities/advanced_search.html.erb b/tutor/app/views/utilities/advanced_search.html.erb
index 2d6af627..b945e037 100644
--- a/tutor/app/views/utilities/advanced_search.html.erb
+++ b/tutor/app/views/utilities/advanced_search.html.erb
@@ -1,6 +1,12 @@
-
+
+
+
+ Advanced Search Form
+
+
+
+
<%= render "advanced_search" %>
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tutor/app/views/utilities/auto_complete.json_builder b/tutor/app/views/utilities/auto_complete.json_builder
deleted file mode 100644
index ff619bcb..00000000
--- a/tutor/app/views/utilities/auto_complete.json_builder
+++ /dev/null
@@ -1,4 +0,0 @@
-array @objects.each do |object|
- id object
- name object.name
-end
\ No newline at end of file
diff --git a/tutor/config/environment.rb b/tutor/config/environment.rb
index e0489645..00e60e20 100644
--- a/tutor/config/environment.rb
+++ b/tutor/config/environment.rb
@@ -3,3 +3,6 @@
# Initialize the Rails application.
Tutor::Application.initialize!
+
+#paginate
+require 'will_paginate'
\ No newline at end of file
diff --git a/tutor/config/initializers/active_admin.rb b/tutor/config/initializers/active_admin.rb
new file mode 100644
index 00000000..33d1d2a9
--- /dev/null
+++ b/tutor/config/initializers/active_admin.rb
@@ -0,0 +1,243 @@
+ActiveAdmin.setup do |config|
+
+ # == Site Title
+ #
+ # Set the title that is displayed on the main layout
+ # for each of the active admin pages.
+ #
+ config.site_title = "Tutor"
+
+ # Set the link url for the title. For example, to take
+ # users to your main site. Defaults to no link.
+ #
+ # config.site_title_link = "/"
+
+ # Set an optional image to be displayed for the header
+ # instead of a string (overrides :site_title)
+ #
+ # Note: Aim for an image that's 21px high so it fits in the header.
+ #
+ # config.site_title_image = "logo.png"
+
+ # == Default Namespace
+ #
+ # Set the default namespace each administration resource
+ # will be added to.
+ #
+ # eg:
+ # config.default_namespace = :hello_world
+ #
+ # This will create resources in the HelloWorld module and
+ # will namespace routes to /hello_world/*
+ #
+ # To set no namespace by default, use:
+ # config.default_namespace = false
+ #
+ # Default:
+ # config.default_namespace = :admin
+ #
+ # You can customize the settings for each namespace by using
+ # a namespace block. For example, to change the site title
+ # within a namespace:
+ #
+ # config.namespace :admin do |admin|
+ # admin.site_title = "Custom Admin Title"
+ # end
+ #
+ # This will ONLY change the title for the admin section. Other
+ # namespaces will continue to use the main "site_title" configuration.
+
+ # == User Authentication
+ #
+ # Active Admin will automatically call an authentication
+ # method in a before filter of all controller actions to
+ # ensure that there is a currently logged in admin user.
+ #
+ # This setting changes the method which Active Admin calls
+ # within the application controller.
+ config.authentication_method = :authenticate_admin_user!
+
+ # == User Authorization
+ #
+ # Active Admin will automatically call an authorization
+ # method in a before filter of all controller actions to
+ # ensure that there is a user with proper rights. You can use
+ # CanCanAdapter or make your own. Please refer to documentation.
+ # config.authorization_adapter = ActiveAdmin::CanCanAdapter
+
+ # You can customize your CanCan Ability class name here.
+ # config.cancan_ability_class = "Ability"
+
+ # You can specify a method to be called on unauthorized access.
+ # This is necessary in order to prevent a redirect loop which happens
+ # because, by default, user gets redirected to Dashboard. If user
+ # doesn't have access to Dashboard, he'll end up in a redirect loop.
+ # Method provided here should be defined in application_controller.rb.
+ # config.on_unauthorized_access = :access_denied
+
+ # == Current User
+ #
+ # Active Admin will associate actions with the current
+ # user performing them.
+ #
+ # This setting changes the method which Active Admin calls
+ # (within the application controller) to return the currently logged in user.
+ config.current_user_method = :current_admin_user
+
+
+ # == Logging Out
+ #
+ # Active Admin displays a logout link on each screen. These
+ # settings configure the location and method used for the link.
+ #
+ # This setting changes the path where the link points to. If it's
+ # a string, the strings is used as the path. If it's a Symbol, we
+ # will call the method to return the path.
+ #
+ # Default:
+ config.logout_link_path = :destroy_admin_user_session_path
+
+ # This setting changes the http method used when rendering the
+ # link. For example :get, :delete, :put, etc..
+ #
+ # Default:
+ # config.logout_link_method = :get
+
+
+ # == Root
+ #
+ # Set the action to call for the root path. You can set different
+ # roots for each namespace.
+ #
+ # Default:
+ # config.root_to = 'dashboard#index'
+
+
+ # == Admin Comments
+ #
+ # This allows your users to comment on any resource registered with Active Admin.
+ #
+ # You can completely disable comments:
+ # config.allow_comments = false
+ #
+ # You can disable the menu item for the comments index page:
+ # config.show_comments_in_menu = false
+ #
+ # You can change the name under which comments are registered:
+ # config.comments_registration_name = 'AdminComment'
+
+
+ # == Batch Actions
+ #
+ # Enable and disable Batch Actions
+ #
+ config.batch_actions = true
+
+
+ # == Controller Filters
+ #
+ # You can add before, after and around filters to all of your
+ # Active Admin resources and pages from here.
+ #
+ # config.before_filter :do_something_awesome
+
+
+ # == Setting a Favicon
+ #
+ # config.favicon = '/assets/favicon.ico'
+
+
+ # == Removing Breadcrumbs
+ #
+ # Breadcrumbs are enabled by default. You can customize them for individual
+ # resources or you can disable them globally from here.
+ #
+ # config.breadcrumb = false
+
+
+ # == Register Stylesheets & Javascripts
+ #
+ # We recommend using the built in Active Admin layout and loading
+ # up your own stylesheets / javascripts to customize the look
+ # and feel.
+ #
+ # To load a stylesheet:
+ # config.register_stylesheet 'my_stylesheet.css'
+ #
+ # You can provide an options hash for more control, which is passed along to stylesheet_link_tag():
+ # config.register_stylesheet 'my_print_stylesheet.css', :media => :print
+ #
+ # To load a javascript file:
+ # config.register_javascript 'my_javascript.js'
+
+
+ # == CSV options
+ #
+ # Set the CSV builder separator
+ # config.csv_options = { :col_sep => ';' }
+ #
+ # Force the use of quotes
+ # config.csv_options = { :force_quotes => true }
+
+
+ # == Menu System
+ #
+ # You can add a navigation menu to be used in your application, or configure a provided menu
+ #
+ # To change the default utility navigation to show a link to your website & a logout btn
+ #
+ # config.namespace :admin do |admin|
+ # admin.build_menu :utility_navigation do |menu|
+ # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank }
+ # admin.add_logout_button_to_menu menu
+ # end
+ # end
+ #
+ # If you wanted to add a static menu item to the default menu provided:
+ #
+ # config.namespace :admin do |admin|
+ # admin.build_menu :default do |menu|
+ # menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank }
+ # end
+ # end
+
+
+ # == Download Links
+ #
+ # You can disable download links on resource listing pages,
+ # or customize the formats shown per namespace/globally
+ #
+ # To disable/customize for the :admin namespace:
+ #
+ # config.namespace :admin do |admin|
+ #
+ # # Disable the links entirely
+ # admin.download_links = false
+ #
+ # # Only show XML & PDF options
+ # admin.download_links = [:xml, :pdf]
+ #
+ # # Enable/disable the links based on block
+ # # (for example, with cancan)
+ # admin.download_links = proc { can?(:view_download_links) }
+ #
+ # end
+
+
+ # == Pagination
+ #
+ # Pagination is enabled by default for all resources.
+ # You can control the default per page count for all resources here.
+ #
+ # config.default_per_page = 30
+
+
+ # == Filters
+ #
+ # By default the index screen includes a “Filters” sidebar on the right
+ # hand side with a filter for each attribute of the registered model.
+ # You can enable or disable them for all resources here.
+ #
+ # config.filters = true
+
+end
diff --git a/tutor/config/routes.rb b/tutor/config/routes.rb
index 9fd7ce20..d95df07b 100644
--- a/tutor/config/routes.rb
+++ b/tutor/config/routes.rb
@@ -1,5 +1,7 @@
Tutor::Application.routes.draw do
-
+
+ devise_for :admin_users, ActiveAdmin::Devise.config
+ ActiveAdmin.routes(self)
devise_for :teaching_assistants
devise_for :students
devise_for :lecturers
@@ -15,6 +17,8 @@
get "utilities/auto_complete"
get 'courses/sign_up'
get 'tracks/show_classmates/:id' => 'tracks#show_classmates'
+ get 'solutions/mark_solution'
+ get 'solutions/view_submissions'
post 'solutions/compile_solution' => 'solutions#compile_solution'
post 'courses/new' => 'courses#new'
post 'courses/share' => 'courses#share'
@@ -27,6 +31,8 @@
post 'students/solved_problems' => 'students#solved_problems'
post 'students/failed_problems' => 'students#failed_problems'
post 'students/incomplete_problems' => 'students#incomplete_problems'
+ post 'students/solved_contest_problems' => 'students#solved_contest_problems'
+ post 'students/failed_contest_problems' => 'students#failed_contest_problems'
get 'students/view_registrants' => 'students#view_registrants'
get 'problems/edit'
@@ -36,8 +42,9 @@
get "tips/show"
get "tips/index"
get "tips/edit"
- get "tips/destroy"
+ get "tips/destroy"
post "tips/:id/edit" => 'tips#update'
+ get "notes/destroy"
# You can have the root of your site routed with "root"
root 'site#index'
@@ -94,6 +101,9 @@
resources :posts
resources :facebook
resources :tips
+ resources :assignments
+ resources :assignment_problems
+ resources :notes
# Example resource route with options:
# resources :products do
diff --git a/tutor/db/schema.rb b/tutor/db/schema.rb
index b6f5f788..268584aa 100644
--- a/tutor/db/schema.rb
+++ b/tutor/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20140509205739) do
+ActiveRecord::Schema.define(version: 20140515223322) do
create_table "acknowledgements", force: true do |t|
t.string "message"
@@ -22,15 +22,39 @@
t.datetime "updated_at"
end
- create_table "admins", force: true do |t|
- t.string "name"
- t.date "dob"
- t.string "profile_image"
- t.boolean "gender"
+ create_table "active_admin_comments", force: true do |t|
+ t.string "namespace"
+ t.text "body"
+ t.string "resource_id", null: false
+ t.string "resource_type", null: false
+ t.integer "author_id"
+ t.string "author_type"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "active_admin_comments", ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id"
+ add_index "active_admin_comments", ["namespace"], name: "index_active_admin_comments_on_namespace"
+ add_index "active_admin_comments", ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id"
+
+ create_table "admin_users", force: true do |t|
+ t.string "email", default: "", null: false
+ t.string "encrypted_password", default: "", null: false
+ t.string "reset_password_token"
+ t.datetime "reset_password_sent_at"
+ t.datetime "remember_created_at"
+ t.integer "sign_in_count", default: 0, null: false
+ t.datetime "current_sign_in_at"
+ t.datetime "last_sign_in_at"
+ t.string "current_sign_in_ip"
+ t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
end
+ add_index "admin_users", ["email"], name: "index_admin_users_on_email", unique: true
+ add_index "admin_users", ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true
+
create_table "assignment_problems", force: true do |t|
t.string "title"
t.text "description"
@@ -82,6 +106,7 @@
t.integer "contest_id"
t.integer "student_id"
t.integer "cproblem_id"
+ t.integer "trials"
t.boolean "status"
t.datetime "created_at"
t.datetime "updated_at"
@@ -93,10 +118,8 @@
t.string "title"
t.text "description"
t.boolean "incomplete"
- t.time "start_time"
- t.time "end_time"
- t.date "start_date"
- t.date "end_date"
+ t.datetime "start_time"
+ t.datetime "end_time"
t.integer "course_id"
t.integer "owner_id"
t.string "owner_type"
@@ -270,6 +293,16 @@
t.datetime "updated_at"
end
+ create_table "notes", force: true do |t|
+ t.string "content"
+ t.integer "line"
+ t.integer "solution_id"
+ t.integer "owner_id"
+ t.string "owner_type"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "notification_mails", force: true do |t|
t.string "subject"
t.string "email"
@@ -278,6 +311,15 @@
t.datetime "updated_at"
end
+ create_table "notifications", force: true do |t|
+ t.string "message", null: false
+ t.boolean "seen", default: false
+ t.integer "receiver_id"
+ t.string "receiver_type"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "posts", force: true do |t|
t.string "title"
t.string "img"
@@ -303,13 +345,14 @@
create_table "problems", force: true do |t|
t.string "title"
t.text "description"
- t.boolean "incomplete"
- t.integer "views_count"
- t.integer "time_limit"
+ t.text "snippet"
+ t.integer "views_count", default: 0
+ t.integer "time_limit", default: 0
t.integer "track_id"
- t.string "snippet"
- t.boolean "fill_gaps"
- t.boolean "public"
+ t.boolean "fill_gaps", default: false
+ t.boolean "incomplete", default: true
+ t.boolean "seen", default: false
+ t.boolean "duplicated", default: false
t.integer "owner_id"
t.string "owner_type"
t.datetime "created_at"
@@ -352,9 +395,9 @@
create_table "solutions", force: true do |t|
t.text "code"
- t.integer "length"
+ t.integer "length", default: 0
t.integer "status"
- t.integer "time"
+ t.integer "time", default: 0
t.text "class_name"
t.integer "student_id"
t.integer "problem_id"
@@ -372,16 +415,16 @@
t.string "faculty"
t.string "major"
t.integer "semester"
- t.boolean "advising"
- t.boolean "probation"
+ t.boolean "advising", default: false
+ t.boolean "probation", default: false
t.datetime "created_at"
t.datetime "updated_at"
- t.string "email", default: "", null: false
- t.string "encrypted_password", default: "", null: false
+ t.string "email", default: "", null: false
+ t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
- t.integer "sign_in_count", default: 0, null: false
+ t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
@@ -443,6 +486,8 @@
t.string "output"
t.integer "model_answer_id"
t.integer "problem_id"
+ t.integer "cproblem_id"
+ t.integer "assignment_problem_id"
t.integer "owner_id"
t.string "owner_type"
t.datetime "created_at"
@@ -472,7 +517,7 @@
create_table "tracks", force: true do |t|
t.string "title"
t.integer "difficulty"
- t.integer "views_count"
+ t.integer "views_count", default: 0
t.integer "topic_id"
t.integer "owner_id"
t.string "owner_type"
diff --git a/tutor/db/seeds.rb b/tutor/db/seeds.rb
index 33a5e768..3534b730 100644
--- a/tutor/db/seeds.rb
+++ b/tutor/db/seeds.rb
@@ -10,7 +10,6 @@
puts("**************************************************************")
puts("# ----------------------- Admins ----------------------- ")
- Admin.create(name: "Admin")
puts("# ----------------------- Lecturers ----------------------- ")
l = Lecturer.new(email: '1@lecturer.com', password: '123456789',
@@ -67,19 +66,19 @@
puts("# ----------------------- Courses ----------------------- ")
Course.create(name:"Data Structures and Alogrithms",
- description:"This is a very easy course", code:"CSEN1", year:2014, semester:1)
+ description:"This is a very easy course", code:"CSEN1", year:2014, semester:1, link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
Course.create(name:"Computer Programming Lab",
- description:"This course's evaluation system is the bad", code:2, year:2014, semester:1)
+ description:"This course's evaluation system is the bad", code:2, year:2014, semester:1, link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
Course.create(name:"Course3",
- description:"This is course three", code:3, year:2014, semester:1)
+ description:"This is course three", code:3, year:2014, semester:1, link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
Course.create(name: "CS 2",
- description:"This is course four", code: "cs2", year: 2014, semester: 2, university: "GUC")
+ description:"This is course four", code: "cs2", year: 2014, semester: 2, university: "GUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
Course.create(name: "CS 3",
- description:"This is course five", code: "cs3", year: 2014, semester: 3, university: "GUC")
+ description:"This is course five", code: "cs3", year: 2014, semester: 3, university: "GUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
Course.create(name: "CS 4",
- description:"This is course six", code: "cs4", year: 2014, semester: 4, university: "AUC")
+ description:"This is course six", code: "cs4", year: 2014, semester: 4, university: "AUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
Course.create(name: "CS 5",
- description:"This is course seven", code: "cs5", year: 2014, semester: 5, university: "AUC")
+ description:"This is course seven", code: "cs5", year: 2014, semester: 5, university: "AUC", link: "http://met.guc.edu.eg/Courses/CourseEdition.aspx?crsEdId=487")
puts("# ----------------------- Course_Student ----------------------- ")
CourseStudent.create(share: true)
@@ -183,16 +182,27 @@
Cproblem.create(title: "ContestProblem 5", description: "This will be very easy Problem")
Cproblem.create(title: "ContestProblem 6", description: "This is very easy Problem")
## Exercise
+
Problem.create(title: "Problem 1", description: "Given two numbers a and b, output a/b",
- incomplete: false)
+ incomplete: false,
+ snippet: "public class CoolSoft {\n\tpublic static void main(String [] args)
+ {\n\t\t\n\t}\n}")
Problem.create(title: "Problem 2", description: "This is very hard Problem",
- incomplete: false)
+ incomplete: false,
+ snippet: "public class CoolSoft {\n\tpublic static void main(String [] args)
+ {\n\t\t\n\t}\n}")
Problem.create(title: "Problem 3", description: "This wont be a hard Problem",
- incomplete: false)
+ incomplete: false,
+ snippet: "public class CoolSoft {\n\tpublic static void main(String [] args)
+ {\n\t\t\n\t}\n}")
Problem.create(title: "Problem 4", description: "This will be very easy Problem",
- incomplete: true)
+ incomplete: true,
+ snippet: "public class CoolSoft {\n\tpublic static void main(String [] args)
+ {\n\t\t\n\t}\n}")
Problem.create(title: "Problem 5", description: "This is very easy Problem",
- incomplete: true)
+ incomplete: true,
+ snippet: "public class CoolSoft {\n\tpublic static void main(String [] args)
+ {\n\t\t\n\t}\n}")
puts("# ----------------------- Tracks ----------------------- ")
Track.create(title: "Track 1", difficulty: 0)
@@ -201,9 +211,10 @@
Track.create(title: "Track 4", difficulty: 3)
puts("# -----------------------Solutions---------------------------")
- Solution.create(code:"println(My first solution)", length:5, status:1)
+ Solution.create(code: "println(My first submitted solution);", length: 5, status: 1)
Solution.create(code:"println(My second solution)", length:5, status:0)
Solution.create(code:"println(My third solution)", length:5, status:3)
+ Solution.create(code: "println(My first solution) \n int x =10;\n x++;\nDouble y\n y = x/3;", length: 5, status: 0)
puts("# ----------------------- TrackProgression ----------------------- ")
TrackProgression.create(level: 1, topic_id: 1)
@@ -247,9 +258,11 @@
ContestProgress.create!(status:false)
puts("# ----------------------- Assignments ----------------------- ")
- Assignment.create(title:"DSD Assignment_1", publish: true)
- Assignment.create(title:"DMENT Assignment_2", publish: true)
- Assignment.create(title:"DSD Assignment_3", publish: true)
+ Assignment.create(title:"DSD Assignment_1", publish: true, due_date: Date.new(2009,6,13),
+ description:"This is your first DSD assignment. It contains exersices on basics of logic design.")
+ Assignment.create(title:"DMENT Assignment_2", publish: true, due_date: Date.new(2015,1,1))
+ Assignment.create(title:"DSD Assignment_3", publish: true, due_date: DateTime.now.to_date)
+ Assignment.create(title:"CA Assignment", publish: false, description:"Allows practice on instruction set formats.", due_date: DateTime.now.to_date)
puts("# ----------------------- Grades ----------------------- ")
Grade.create(grade: 100)
@@ -314,6 +327,7 @@
Lecturer.first.assignments << Assignment.first
Lecturer.first.assignments << Assignment.find_by_id(2)
Lecturer.first.assignments << Assignment.find_by_id(3)
+ Lecturer.first.assignments << Assignment.find_by_id(4)
## Grades
Lecturer.first.grades << Grade.first
Lecturer.find_by_id(2).grades << Grade.find_by_id(2)
@@ -323,6 +337,7 @@
Student.first.course_students << CourseStudent.first
## Solutions
Student.first.solutions << Solution.first
+ Student.first.solutions << Solution.find_by_id(4)
Student.first.solutions << Solution.find_by_id(2)
Student.first.solutions << Solution.find_by_id(3)
## Attempts
@@ -405,7 +420,8 @@
Problem.find_by_id(3).model_answers << ModelAnswer.find_by_id(5)
Problem.find_by_id(3).model_answers << ModelAnswer.find_by_id(6)
## Solutions
- Problem.first.solutions << Solution.first
+ AssignmentProblem.first.solutions << Solution.first
+ AssignmentProblem.first.solutions << Solution.find_by_id(4)
Problem.find_by_id(2).solutions << Solution.find_by_id(2)
Problem.find_by_id(3).solutions << Solution.find_by_id(3)
## Attempts
@@ -430,6 +446,7 @@
Cproblem.find_by_id(4).contests_progresses << ContestProgress.find_by_id(4)
## Hints
Problem.first.model_answers.first.hints << Hint.first
+ Problem.first.model_answers.first.hints << Hint.all.second
puts("# ----------------------- Tracks ----------------------- ")
## Problems
@@ -464,6 +481,7 @@
## Assignments
Course.first.assignments << Assignment.first
Course.first.assignments << Assignment.find_by_id(2)
+ Course.first.assignments << Assignment.find_by_id(4)
Course.find_by_id(2).assignments << Assignment.find_by_id(3)
diff --git a/tutor/spec/controllers/topics_controller_spec.rb b/tutor/spec/controllers/topics_controller_spec.rb
new file mode 100644
index 00000000..08c30985
--- /dev/null
+++ b/tutor/spec/controllers/topics_controller_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+include Devise::TestHelpers
+
+describe TopicsController do
+
+ context "Create Topic and deletes it" do
+ it "destroys the requested topic" do
+ lecturer = Lecturer.new(email: '1@lecturer.com', password: '123456789',
+ password_confirmation: '123456789', name: 'LecturerI',
+ confirmed_at: Time.now, dob: DateTime.now.to_date, gender: true,
+ degree: "PhD", university: "GUC", department: "MET")
+ lecturer.save!
+ course = Course.create(name:"Data Structures and Alogrithms",
+ description:"This is a very easy course", code:"CSEN1", year:2014, semester:1)
+ topic = Topic.create(title: "Topic1", description: "This is Topic1 description",
+ lecturer_id: lecturer.id, course_id: course.id)
+ TopicsController.skip_before_filter :authenticate!
+ expect {
+ delete :destroy, :id => topic.id
+ }.to change(Topic, :count).by(-1)
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/tutor/spec/models/admin_user_spec.rb b/tutor/spec/models/admin_user_spec.rb
new file mode 100644
index 00000000..3d153022
--- /dev/null
+++ b/tutor/spec/models/admin_user_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe AdminUser do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/tutor/spec/models/assignment_problem_spec.rb b/tutor/spec/models/assignment_problem_spec.rb
new file mode 100644
index 00000000..a4f2ffed
--- /dev/null
+++ b/tutor/spec/models/assignment_problem_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe AssignmentProblem do
+ it 'is valid with a title and description' do
+ assignment = AssignmentProblem.new(
+ title: 'rspec assignemt problem',
+ description: 'This is an assignment problem test')
+ expect(assignment).to be_valid
+ end
+
+ it 'is invalid without title' do
+ assignment = AssignmentProblem.new(
+ description: 'This is a test assignemnt'
+ )
+ expect(assignment).not_to be_valid
+ end
+
+ it ' is added successfully to an asssignmet ' do
+ aproblem1 = AssignmentProblem.new(
+ title: 'P1',
+ description: 'P2',
+ )
+ aproblem2 = AssignmentProblem.new(
+ title: 'P1',
+ description: 'P2',
+ )
+
+ assignment = Assignment.new(
+ title: 'rspec assignemt',
+ description: 'This is a test assignemnt',
+ due_date: Date.new(2015,1,1) ,
+ problems: [aproblem1,aproblem2])
+
+ expect(assignment.problems).to include(aproblem1)
+ end
+end
\ No newline at end of file
diff --git a/tutor/spec/models/assignment_spec.rb b/tutor/spec/models/assignment_spec.rb
new file mode 100644
index 00000000..c8cb59c4
--- /dev/null
+++ b/tutor/spec/models/assignment_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Assignment do
+ it 'is valid with a title and description' do
+ assignment = Assignment.new(
+ title: 'rspec assignemt',
+ description: 'This is a test assignemnt',
+ due_date: Date.new(2015,1,1))
+ expect(assignment).to be_valid
+ end
+
+ it 'is invalid without title' do
+ assignment = Assignment.new(
+ description: 'This is a test assignemnt',
+ due_date: Date.new(2015,1,1)
+ )
+ expect(assignment).not_to be_valid
+ end
+
+ it 'is valid if it includes problems' do
+
+ aproblem1 = AssignmentProblem.new(
+ title: 'P1',
+ description: 'P2')
+ aproblem2 = AssignmentProblem.new(
+ title: 'P1',
+ description: 'p2')
+
+ assignment = Assignment.new(
+ title: 'rspec assignemt',
+ description: 'This is a test assignemnt',
+ due_date: Date.new(2015,1,1), problems:[aproblem1 , aproblem2])
+
+ expect(assignment.problems[0]).to eq(aproblem1)
+ expect(assignment.problems[1]).to eq(aproblem2)
+ end
+
+ it 'Changes the number of assignment' do
+ assignment = Assignment.new(
+ title: 'rspec assignemt',
+ description: 'This is a test assignemnt',
+ due_date: Date.new(2015,1,1))
+
+ expect {assignment.save}.to change {Assignment.count}.by(1)
+ end
+end
\ No newline at end of file
diff --git a/tutor/spec/models/note_spec.rb b/tutor/spec/models/note_spec.rb
new file mode 100644
index 00000000..c13ca5d2
--- /dev/null
+++ b/tutor/spec/models/note_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+
+describe Note do
+ it 'is valid with a content, solution_id and line' do
+ solution = Solution.new(
+ code: "println(My first solution)\n
+ int x =10;\n x++;\n Double y\n y = x/3;",
+ length: 5,
+ status: 1)
+ solution.save
+ note = Note.new(
+ content: 'This is a comment on a line of code',
+ line: 1,
+ solution_id: solution.id)
+ expect(note).to be_valid
+ end
+
+ it 'is invalid without content' do
+ solution = Solution.new(
+ code: "println(My first solution)\n
+ int x =10;\n x++;\n Double y\n y = x/3;",
+ length: 5,
+ status: 1)
+ note = Note.new(
+ line: 1,
+ solution_id: solution.id)
+ expect(note).not_to be_valid
+ end
+
+ it 'is invalid without line' do
+ solution = Solution.new(
+ code: "println(My first solution)\n
+ int x =10;\n x++;\n Double y\n y = x/3;",
+ length: 5,
+ status: 1)
+ note = Note.new(
+ content: "This is a comment on a line of code",
+ solution_id: solution.id)
+ expect(note).not_to be_valid
+ end
+
+ it 'is invalid without solution_id' do
+ note = Note.new(
+ content: "This is a comment on a line of code",
+ line: 1)
+ expect(note).not_to be_valid
+ end
+
+ it 'Changes the number of notes' do
+ solution = Solution.new(
+ code: "println(My first solution)\n
+ int x =10;\n x++;\n Double y\n y = x/3;",
+ length: 5,
+ status: 1)
+ solution.save
+ note = Note.new(
+ content: "This is a comment on a line of code",
+ line: 1,
+ solution_id: solution.id)
+ expect {note.save}.to change {Note.count}.by(1)
+ end
+end
\ No newline at end of file
diff --git a/tutor/spec/models/notification_spec.rb b/tutor/spec/models/notification_spec.rb
new file mode 100644
index 00000000..74df8c73
--- /dev/null
+++ b/tutor/spec/models/notification_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe Notification do
+ pending "add some examples to (or delete) #{__FILE__}"
+end
diff --git a/tutor/spec/models/solutions_layer_spec.rb b/tutor/spec/models/solutions_layer_spec.rb
index efff8001..6d40a5ed 100644
--- a/tutor/spec/models/solutions_layer_spec.rb
+++ b/tutor/spec/models/solutions_layer_spec.rb
@@ -7,18 +7,18 @@
expect(SolutionsLayer.get_compiler "java").to eq JavaCompiler
end
- it "no such compiler named blabla" do
- expect(SolutionsLayer.get_compiler "blabla").to eq false
+ it "no such compiler named foo" do
+ expect(SolutionsLayer.get_compiler "foo").to eq false
end
end
context "get_executer tests" do
it "get java executer" do
- expect(SolutionsLayer.get_executer "java").to eq JavaExecuter
+ expect(SolutionsLayer.get_executer foo"java").to eq JavaExecuter
end
- it "raise error when trying to get a non valid blabla executer" do
- expect{SolutionsLayer.get_executer "blabla"}.to raise_error(NameError)
+ it "no such executer named foo" do
+ expect(SolutionsLayer.get_executer "foo").to eq false
end
end
@@ -27,8 +27,8 @@
expect(SolutionsLayer.get_debugger "java").to eq JavaDebugger
end
- it "no such debugger named blabla" do
- expect(SolutionsLayer.get_debugger "blabla").to eq false
+ it "no such debugger named foo" do
+ expect(SolutionsLayer.get_debugger "foo").to eq false
end
end
@@ -43,41 +43,44 @@
description: "Given two numbers a and b, output a/b", incomplete: false)
test_cases = TestCase.create(output: "5\n", input:"10 2")
problem.test_cases << test_cases
- @code = "public static void main(String [] args){}}"
+ @code = "public class Coolsoft{public static void main(String [] args){}}"
@student_id = 1
@problem_id = problem.id
@lang = "java"
@cases1 = "1 12 123"
@cases2 = "1 12"
+ @problem_type = "Problem"
+ @class_name = "Coolsoft"
@solution = Solution.create(code: @code, student_id: @student_id,
- problem_id: @problem_id)
+ problem_id: @problem_id, problem_type: @problem_type, class_name: @class_name)
end
context "compiler" do
it "a solution is created" do
expect(Solution).to receive(:create).and_return(@solution)
- SolutionsLayer.compile @lang, @code, @student_id, @problem_id
+ SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name
end
it "java JavaCompiler.compiler_feedback was called" do
expect(JavaCompiler).to receive(:compiler_feedback){
{success: false, errors: ""}
}
- SolutionsLayer.compile @lang, @code, @student_id, @problem_id
+ SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name
end
it "get_compiler was called and JavaCompiler was returned" do
expect(SolutionsLayer).to receive(:get_compiler).with("java"){JavaCompiler}
- SolutionsLayer.compile @lang, @code, @student_id, @problem_id
+ SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name
end
it "get_solution was called and new Solution was returned" do
expect(SolutionsLayer).to receive(:get_solution).and_return(@solution)
- SolutionsLayer.compile @lang, @code, @student_id, @problem_id
+ SolutionsLayer.compile @lang, @code, @student_id, @problem_id, @problem_type, @class_name
end
it "Compiled successfuly" do
- feed_back = SolutionsLayer.compile @lang, @code, @student_id, @problem_id
+ feed_back = SolutionsLayer.compile(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name)
expected_hash = {success: true, errors: nil}
expect(feed_back).to eq expected_hash
end
@@ -86,17 +89,20 @@
context "executer" do
it "get_compiler was called and JavaCompiler was returned" do
expect(SolutionsLayer).to receive(:get_compiler).with("java"){JavaCompiler}
- SolutionsLayer.execute @lang, @code, @student_id, @problem_id, @cases1
+ SolutionsLayer.execute(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
end
it "Compiled successfuly but wrong input" do
- feed_back = SolutionsLayer.execute @lang, @code, @student_id, @problem_id, @cases1
+ feed_back = SolutionsLayer.execute(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
expected_hash = {executer_feedback: false, executer_output: "Enter only 2 numbers"}
expect(feed_back).to eq expected_hash
end
it "Compiled successfuly and run successfuly with no output" do
- feed_back = SolutionsLayer.execute @lang, @code, @student_id, @problem_id, @cases2
+ feed_back = SolutionsLayer.execute(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases2)
expected_hash = {executer_feedback: true,
executer_output: {success: true, message: ""}
}
@@ -104,21 +110,24 @@
end
it "Compilation error" do
- code = "public static void main(String [] args){System.out.println(5)}}"
- feed_back = SolutionsLayer.execute @lang, code, @student_id, @problem_id, @cases2
- expected_hash = {compiler_error: true,
- compiler_output: {success: false,
- errors: "CoolSoft.java:2: error: ';' expected\npublic " +
- "static void main(String [] args){System.out.println(5)}}" +
- "\n " +
- " ^\n1 error\n"}
+ code = "public class Coolsoft{public static void main" +
+ "(String [] args){System.out.println(5)}}"
+ feed_back = SolutionsLayer.execute(@lang, code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases2)
+ expected_hash = {compiler_error: true, compiler_output: {success: false,
+ errors: "Coolsoft.java:1: error: ';' expected\npublic " +
+ "class Coolsoft{public static void main(String [] args)" +
+ "{System.out.println(5)}}\n " +
+ " ^\n1 error\n"}
}
expect(feed_back).to eq expected_hash
end
it "Compiled successfuly and run successfuly with output" do
- code = "public static void main(String [] args){System.out.println(5);}}"
- feed_back = SolutionsLayer.execute @lang, code, @student_id, @problem_id, @cases2
+ code = "public class Coolsoft{public static void main" +
+ "(String [] args){System.out.println(5);}}"
+ feed_back = SolutionsLayer.execute(@lang, code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases2)
expected_hash = {executer_feedback: true,
executer_output: {success: true, message: "5\n"}
}
@@ -129,33 +138,41 @@
context "debugger" do
it "get_solution called" do
expect(SolutionsLayer).to receive(:get_solution).and_return(@solution)
- SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1
+ SolutionsLayer.debug(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
end
it "get_compiler called" do
expect(SolutionsLayer).to receive(:get_compiler).and_return(JavaCompiler)
- SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1
+ SolutionsLayer.debug(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
end
it "get_debugger called" do
expect(SolutionsLayer).to receive(:get_debugger).and_return(JavaDebugger)
- SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1
+ SolutionsLayer.debug(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
end
it "Compilation error" do
- code = "public static void main(String [] args){System.out.println(5)}}"
- feed_back = SolutionsLayer.debug @lang, code, @student_id, @problem_id, @cases1
- expected_hash = {data: {success: false, errors: "CoolSoft.java:2: error: ';'" +
- " expected\npublic static void main(String [] args){" +
- "System.out.println(5)}}\n " +
- " ^\n1 error\n"}, success: false}
+ code = "public class Coolsoft{public static void main" +
+ "(String [] args){System.out.println(5)}}"
+ feed_back = SolutionsLayer.debug(@lang, code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
+ expected_hash = {success: false, data: {success: false,
+ errors: "Coolsoft.java:1: error: ';'" +
+ " expected\npublic class Coolsoft{public static void main(String [] args){" +
+ "System.out.println(5)}}\n " +
+ " ^\n1 error\n"}}
expect(feed_back).to eq expected_hash
end
it "Debugged successfuly", focus: true do
- feed_back = SolutionsLayer.debug @lang, @code, @student_id, @problem_id, @cases1
- expected_hash = {data: [{status: true, line: 2, stream: "",
- locals: [" args = {\n\"1\", \"12\", \"123\"\n}\n"]}], success: true}
+ feed_back = SolutionsLayer.debug(@lang, @code, @student_id,
+ @problem_id, @problem_type, @class_name, @cases1)
+ expected_hash = {data: [{status: true, line: 1, stream: "",
+ locals: [" args = {\n\"1\", \"12\", \"123\"\n}\n"]}], success: true,
+ status: "The debugging session was successful."}
expect(feed_back).to eq expected_hash
end
end
diff --git a/tutor/spec/models/time_limit_spec.rb b/tutor/spec/models/time_limit_spec.rb
new file mode 100644
index 00000000..c03d880e
--- /dev/null
+++ b/tutor/spec/models/time_limit_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe TimeLimit do
+ context "TimeLimit Execeded" do
+ it "sleeping" do
+ expect(TimeLimit.start 5, "sleep 20").not_to raise_error
+ end
+
+ it "infinite loop" do
+ expect(TimeLimit.start(5){
+ while true
+ end
+ }).not_to raise_error
+ end
+ end
+
+ context "TimeLimit wasnot Execeded" do
+ it "echo foo" do
+ expect(TimeLimit.start 1, "echo foo").to eq [true, "foo\n", ""]
+ end
+ end
+end
diff --git a/tutor/test/controllers/assignment_problems_controller_test.rb b/tutor/test/controllers/assignment_problems_controller_test.rb
new file mode 100644
index 00000000..2069f2a4
--- /dev/null
+++ b/tutor/test/controllers/assignment_problems_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class AssignmentProblemsControllerTest < ActionController::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/test/controllers/assignments_controller_test.rb b/tutor/test/controllers/assignments_controller_test.rb
new file mode 100644
index 00000000..8018cb5e
--- /dev/null
+++ b/tutor/test/controllers/assignments_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class AssignmentsControllerTest < ActionController::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/test/fixtures/notes.yml b/tutor/test/fixtures/notes.yml
new file mode 100644
index 00000000..937a0c00
--- /dev/null
+++ b/tutor/test/fixtures/notes.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+# This model initially had no columns defined. If you add columns to the
+# model remove the '{}' from the fixture names and add the columns immediately
+# below each fixture, per the syntax in the comments below
+#
+one: {}
+# column: value
+#
+two: {}
+# column: value
diff --git a/tutor/test/helpers/assignment_problems_helper_test.rb b/tutor/test/helpers/assignment_problems_helper_test.rb
new file mode 100644
index 00000000..8953bc37
--- /dev/null
+++ b/tutor/test/helpers/assignment_problems_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class AssignmentProblemsHelperTest < ActionView::TestCase
+end
diff --git a/tutor/test/helpers/assignments_helper_test.rb b/tutor/test/helpers/assignments_helper_test.rb
new file mode 100644
index 00000000..f077f005
--- /dev/null
+++ b/tutor/test/helpers/assignments_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class AssignmentsHelperTest < ActionView::TestCase
+end
diff --git a/tutor/test/models/note_test.rb b/tutor/test/models/note_test.rb
new file mode 100644
index 00000000..7bbab537
--- /dev/null
+++ b/tutor/test/models/note_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class NoteTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/vendor/assets/javascripts/active_admin.js.coffee b/tutor/vendor/assets/javascripts/active_admin.js.coffee
new file mode 100644
index 00000000..3752dcef
--- /dev/null
+++ b/tutor/vendor/assets/javascripts/active_admin.js.coffee
@@ -0,0 +1 @@
+#= require active_admin/base
diff --git a/tutor/vendor/assets/javascripts/bootstrap.js b/tutor/vendor/assets/javascripts/bootstrap.js
index c5f6cf0b..a2cde25d 100644
--- a/tutor/vendor/assets/javascripts/bootstrap.js
+++ b/tutor/vendor/assets/javascripts/bootstrap.js
@@ -1952,3 +1952,967 @@ if (typeof jQuery === 'undefined') { throw new Error('Bootstrap\'s JavaScript re
})
}(jQuery);
+!function($) {
+
+ 'use strict';
+
+ $.expr[':'].icontains = function(obj, index, meta) {
+ return $(obj).text().toUpperCase().indexOf(meta[3].toUpperCase()) >= 0;
+ };
+
+ var Selectpicker = function(element, options, e) {
+ if (e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ this.$element = $(element);
+ this.$newElement = null;
+ this.$button = null;
+ this.$menu = null;
+ this.$lis = null;
+
+ //Merge defaults, options and data-attributes to make our options
+ this.options = $.extend({}, $.fn.selectpicker.defaults, this.$element.data(), typeof options == 'object' && options);
+
+ //If we have no title yet, check the attribute 'title' (this is missed by jq as its not a data-attribute
+ if (this.options.title === null) {
+ this.options.title = this.$element.attr('title');
+ }
+
+ //Expose public methods
+ this.val = Selectpicker.prototype.val;
+ this.render = Selectpicker.prototype.render;
+ this.refresh = Selectpicker.prototype.refresh;
+ this.setStyle = Selectpicker.prototype.setStyle;
+ this.selectAll = Selectpicker.prototype.selectAll;
+ this.deselectAll = Selectpicker.prototype.deselectAll;
+ this.init();
+ };
+
+ Selectpicker.prototype = {
+
+ constructor: Selectpicker,
+
+ init: function() {
+ var that = this,
+ id = this.$element.attr('id');
+
+ this.$element.hide();
+ this.multiple = this.$element.prop('multiple');
+ this.autofocus = this.$element.prop('autofocus');
+ this.$newElement = this.createView();
+ this.$element.after(this.$newElement);
+ this.$menu = this.$newElement.find('> .dropdown-menu');
+ this.$button = this.$newElement.find('> button');
+ this.$searchbox = this.$newElement.find('input');
+
+ if (id !== undefined) {
+ this.$button.attr('data-id', id);
+ $('label[for="' + id + '"]').click(function(e) {
+ e.preventDefault();
+ that.$button.focus();
+ });
+ }
+
+ this.checkDisabled();
+ this.clickListener();
+ if (this.options.liveSearch) this.liveSearchListener();
+ this.render();
+ this.liHeight();
+ this.setStyle();
+ this.setWidth();
+ if (this.options.container) this.selectPosition();
+ this.$menu.data('this', this);
+ this.$newElement.data('this', this);
+ },
+
+ createDropdown: function() {
+ //If we are multiple, then add the show-tick class by default
+ var multiple = this.multiple ? ' show-tick' : '';
+ var inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '';
+ var autofocus = this.autofocus ? ' autofocus' : '';
+ var header = this.options.header ? '
' + this.options.header + '
' : '';
+ var searchbox = this.options.liveSearch ? '' : '';
+ var actionsbox = this.options.actionsBox ? '
' +
+ '
' +
+ '' +
+ '' +
+ '
' +
+ '
' : '';
+ var drop =
+ '
' +
+ '' +
+ '
' +
+ header +
+ searchbox +
+ actionsbox +
+ '
' +
+ '
' +
+ '
' +
+ '
';
+
+ return $(drop);
+ },
+
+ createView: function() {
+ var $drop = this.createDropdown();
+ var $li = this.createLi();
+ $drop.find('ul').append($li);
+ return $drop;
+ },
+
+ reloadLi: function() {
+ //Remove all children.
+ this.destroyLi();
+ //Re build
+ var $li = this.createLi();
+ this.$menu.find('ul').append( $li );
+ },
+
+ destroyLi: function() {
+ this.$menu.find('li').remove();
+ },
+
+ createLi: function() {
+ var that = this,
+ _liA = [],
+ _liHtml = '';
+
+ this.$element.find('option').each(function() {
+ var $this = $(this);
+
+ //Get the class and text for the option
+ var optionClass = $this.attr('class') || '';
+ var inline = $this.attr('style') || '';
+ var text = $this.data('content') ? $this.data('content') : $this.html();
+ var subtext = $this.data('subtext') !== undefined ? '' + $this.data('subtext') + '' : '';
+ var icon = $this.data('icon') !== undefined ? ' ' : '';
+ if (icon !== '' && ($this.is(':disabled') || $this.parent().is(':disabled'))) {
+ icon = ''+icon+'';
+ }
+
+ if (!$this.data('content')) {
+ //Prepend any icon and append any subtext to the main text.
+ text = icon + '' + text + subtext + '';
+ }
+
+ if (that.options.hideDisabled && ($this.is(':disabled') || $this.parent().is(':disabled'))) {
+ _liA.push('');
+ } else if ($this.parent().is('optgroup') && $this.data('divider') !== true) {
+ if ($this.index() === 0) {
+ //Get the opt group label
+ var label = $this.parent().attr('label');
+ var labelSubtext = $this.parent().data('subtext') !== undefined ? ''+$this.parent().data('subtext')+'' : '';
+ var labelIcon = $this.parent().data('icon') ? ' ' : '';
+ label = labelIcon + '' + label + labelSubtext + '';
+
+ if ($this[0].index !== 0) {
+ _liA.push(
+ '