Bug fixes and adding admin functionality
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 2 Sep 2019 14:41:00 +0000 (09:41 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 2 Sep 2019 14:41:00 +0000 (09:41 -0500)
32 files changed:
codebox/app/app.moon
codebox/controllers/account/login.moon
codebox/controllers/account/register.moon
codebox/controllers/admin/Tupfile [new file with mode: 0644]
codebox/controllers/admin/problem.moon [new file with mode: 0644]
codebox/controllers/admin/problem/Tupfile [new file with mode: 0644]
codebox/controllers/admin/problem/edit.moon [new file with mode: 0644]
codebox/controllers/admin/problem/new.moon [new file with mode: 0644]
codebox/controllers/controller.moon
codebox/middleware/admin_required.moon [new file with mode: 0644]
codebox/migrations.moon
codebox/services/crypto.moon
codebox/static/css/core.css
codebox/static/css/core.css.map
codebox/static/scss/_utils.scss
codebox/static/scss/core.scss
codebox/views/account/login.moon
codebox/views/account/register.moon
codebox/views/admin/Tupfile [new file with mode: 0644]
codebox/views/admin/problem.moon [new file with mode: 0644]
codebox/views/admin/problem/Tupfile [new file with mode: 0644]
codebox/views/admin/problem/edit.moon [new file with mode: 0644]
codebox/views/admin/problem/new.moon [new file with mode: 0644]
codebox/views/partials/admin_layout.moon [new file with mode: 0644]
codebox/views/partials/admin_navbar.moon [new file with mode: 0644]
codebox/views/partials/error_list.moon [new file with mode: 0644]
codebox/views/partials/layout.moon
docker/executer/Dockerfile
docs/todo [new file with mode: 0644]
executer/app/executer.coffee
executer/app/executers/c_executer.coffee
executer/app/routes.coffee

index 25200f774170fbc32c41be3adbaa46c1c2c13558..adc70c974f8ee8a9ca7fa48714a789745f50bbbd 100644 (file)
@@ -4,8 +4,6 @@ console = require "lapis.console"
 import Users from require "models"
 bind = require "utils.binding"
 
-bind\bind 'test', -> 'Test called!!!'
-
 bind\bind_static 'executer', require 'facades.executer'
 bind\bind_static 'crypto', -> require 'services.crypto'
 
@@ -18,9 +16,10 @@ class extends lapis.Application
 
        @before_filter =>
                @navbar = {}
-               @navbar.selected = 1
+               @navbar.selected = -1
 
        ['index':                "/"]:             require "controllers.index"
+
        ['account.login':    "/login"]:    require "controllers.account.login"
        ['account.logout':   "/logout"]:   require "controllers.account.logout"
        ['account.register': "/register"]: require "controllers.account.register"
@@ -28,7 +27,13 @@ class extends lapis.Application
        ['executer.status_update': "/executer/status_update"]: require "controllers.executer.status_update"
        ['executer.request': '/executer/request']: require "controllers.executer.request"
 
-       [test: "/test"]: =>
-               "Your value is over 10"
+       ['admin.user': "/admin/user"]: => "NOT IMPLEMENTED"
+
+       ['admin.problem': "/admin/problem"]: require "controllers.admin.problem"
+       ['admin.problem.new': "/admin/problem/new"]: require "controllers.admin.problem.new"
+       ['admin.problem.edit': "/admin/problem/edit/:problem_name"]: require "controllers.admin.problem.edit"
+
+       ['admin.submission': "/admin/submission"]: => "NOT IMPLEMENTED"
+       ['admin.competition': "/admin/competition"]: => "NOT IMPLEMENTED"
 
        "/console": console.make!
index 99fdcaf1bf1a9718b8b9a1026440453a75e4608a..a44204dd943c90c904749baabc50acb979b59ad5 100644 (file)
@@ -1,6 +1,7 @@
 import make_controller from require "controllers.controller"
 import Users from require 'models'
 import assert_valid from require "lapis.validate"
+import capture_errors, yield_error from require "lapis.application"
 
 utils = require "lapis.util"
 
@@ -14,7 +15,7 @@ make_controller
                @flow 'csrf_setup'
                render: 'account.login'
 
-       post: =>
+       post: capture_errors =>
                @flow 'csrf_validate'
 
                assert_valid @params, {
@@ -22,11 +23,15 @@ make_controller
                        { "password", exists: true, min_length: 2 }
                }
 
-               users = Users\find username: @params.username
-               if #users > 0
-                       if @crypto.verify @params.password, users[1].password_hash
-                               @session.user_id = users[1].id
+               user = Users\find username: @params.username
+               if user != nil
+                       if @crypto.verify @params.password, user.password_hash
+                               @session.user_id = user.id
                                return redirect_to: @url_for 'index'
+                       else
+                               yield_error "Password incorrect"
+               else
+                       yield_error "User not found"
 
                render: 'account.login'
 
index 9bb399b25c4d2102efd0d102cc130bc249825041..c16d4b4a0e86d9ed22c13053b8f8bfda8d5de51a 100644 (file)
@@ -1,7 +1,7 @@
 import make_controller from require "controllers.controller"
 import Users from require 'models'
 import assert_valid from require "lapis.validate"
-import capture_errors from require 'lapis.application'
+import capture_errors, yield_error from require 'lapis.application'
 
 make_controller
        inject:
@@ -27,8 +27,7 @@ make_controller
                users = Users\find username: @params.username
                if users
                        -- Account already exists
-                       @errors or= {}
-                       table.insert @errors, 'Username already taken'
+                       yield_error 'Username already taken'
                        return render: 'account.register'
 
                user_id = Users\create {
diff --git a/codebox/controllers/admin/Tupfile b/codebox/controllers/admin/Tupfile
new file mode 100644 (file)
index 0000000..f0fe651
--- /dev/null
@@ -0,0 +1 @@
+include_rules
diff --git a/codebox/controllers/admin/problem.moon b/codebox/controllers/admin/problem.moon
new file mode 100644 (file)
index 0000000..a12f810
--- /dev/null
@@ -0,0 +1,18 @@
+import make_controller from require "controllers.controller"
+import assert_valid from require "lapis.validate"
+import capture_errors from require 'lapis.application'
+import Problems from require 'models'
+
+make_controller
+       layout: require 'views.partials.admin_layout'
+
+       middleware: { 'logged_in', 'admin_required' }
+
+       get: =>
+               @navbar.selected = 1
+
+               @problems = Problems\select!
+
+               render: 'admin.problem'
+
+
diff --git a/codebox/controllers/admin/problem/Tupfile b/codebox/controllers/admin/problem/Tupfile
new file mode 100644 (file)
index 0000000..f0fe651
--- /dev/null
@@ -0,0 +1 @@
+include_rules
diff --git a/codebox/controllers/admin/problem/edit.moon b/codebox/controllers/admin/problem/edit.moon
new file mode 100644 (file)
index 0000000..7b14622
--- /dev/null
@@ -0,0 +1,54 @@
+import make_controller from require "controllers.controller"
+import assert_valid from require "lapis.validate"
+import capture_errors, capture_errors_json, yield_error from require 'lapis.application'
+import Problems from require 'models'
+
+make_controller
+       layout: require 'views.partials.admin_layout'
+
+       middleware: { 'logged_in', 'admin_required' }
+
+       get: capture_errors_json =>
+               @flow 'csrf_setup'
+
+               @navbar.selected = 1
+
+               assert_valid @params, {
+                       { "problem_name", exists: true }
+               }
+
+               @problem = Problems\find short_name: @params.problem_name
+               unless @problem
+                       yield_error "Problem not found"
+
+               render: true
+
+       post: capture_errors =>
+               @flow 'csrf_validate'
+               @navbar.selected = 1
+
+               assert_valid @params, {
+                       { "problem_name", exists: true }
+                       { "problem_id", exists: true }
+                       { "name", exists: true }
+                       { "short_name", exists: true }
+                       { "description", exists: true }
+                       { "time_limit", exists: true, is_integer: true }
+               }
+
+               problem = Problems\find @params.problem_id
+               unless problem
+                       yield_error "Problem with id '#{@params.problem_id}' not found"
+                       return
+
+               problem\update {
+                       name: @params.name
+                       short_name: @params.short_name
+                       description: @params.description
+                       time_limit: @params.time_limit
+               }
+
+               redirect_to: (@url_for "admin.problem.edit", problem_name: @params.short_name)
+
+       delete: capture_errors_json =>
+               return json: @params
diff --git a/codebox/controllers/admin/problem/new.moon b/codebox/controllers/admin/problem/new.moon
new file mode 100644 (file)
index 0000000..75d7f34
--- /dev/null
@@ -0,0 +1,48 @@
+import make_controller from require "controllers.controller"
+import assert_valid from require "lapis.validate"
+import capture_errors, capture_errors_json, yield_error from require 'lapis.application'
+import Problems from require 'models'
+
+make_controller
+       layout: require 'views.partials.admin_layout'
+
+       middleware: { 'logged_in', 'admin_required' }
+
+       get: =>
+               @flow 'csrf_setup'
+
+               @navbar.selected = 1
+
+               @problems = Problems\select!
+
+               return {
+                       layout: require 'views.partials.admin_layout'
+                       render: 'admin.problem.new'
+               }
+
+       post: capture_errors =>
+               @flow 'csrf_validate'
+
+               assert_valid @params, {
+                       { 'short_name', exists: true }
+                       { 'name', exists: true }
+                       { 'description', exists: true }
+                       { 'time_limit', exists: true, is_integer: true }
+               }
+
+               if Problems\find short_name: @params.short_name
+                       yield_error "Problem with short name, #{@params.short_name}, already exists"
+                       return
+
+               problem = Problems\create {
+                       short_name: @params.short_name
+                       name: @params.name
+                       kind: Problems.kinds\for_db 'code'
+                       description: @params.description
+                       time_limit: @params.time_limit
+               }
+
+               if problem then
+                       yield_error success: true, msg: "Successfully created problem #{@params.name}"
+               else
+                       yield_error "There was an error creating the problem"
index a9519e17e8132f5411014ce5b2a2d6a8222394a0..34e77d0b24f298eecdf2cd6c1218535acc3760f0 100644 (file)
@@ -14,7 +14,17 @@ bind = require 'utils.binding'
                                for middleware in *routes.middleware
                                        require("#{@app.middleware_prefix}.#{middleware}") @
 
-                       GET: => routes.get(@)
-                       POST: => routes.post(@)
+                       GET: =>
+                               r = routes.get(@)
+                               r.layout or= routes.layout
+                               return r
+                       POST: =>
+                               r = routes.post(@)
+                               r.layout or= routes.layout
+                               return r
+                       DELETE: =>
+                               r = routes.delete(@)
+                               r.layout or= routes.layout
+                               return r
                }
 }
diff --git a/codebox/middleware/admin_required.moon b/codebox/middleware/admin_required.moon
new file mode 100644 (file)
index 0000000..ce4356b
--- /dev/null
@@ -0,0 +1,4 @@
+=>
+       unless @user.username == "admin"
+               @write redirect_to: @url_for 'index'
+
index eb2917137314cd71e18f6d6c914ee175a2fa28f7..36ed5c008ae580a8bee22ac0fbd2c5f082f71e19 100644 (file)
@@ -1,4 +1,5 @@
 import create_table, add_column, types from require "lapis.db.schema"
+import insert from require "lapis.db"
 
 {
        [1]: =>
@@ -30,6 +31,7 @@ import create_table, add_column, types from require "lapis.db.schema"
        [3]: =>
                create_table "problems", {
                        { "id", types.serial },
+                       { "short_name", types.varchar unique: true },
                        { "name", types.varchar },
                        { "kind", types.enum },
                        { "description", types.text null: true },
@@ -47,4 +49,12 @@ import create_table, add_column, types from require "lapis.db.schema"
 
                        "PRIMARY KEY (id)"
                }
+
+       [5]: =>
+               insert "users", {
+                       username: "admin"
+                       password_hash: "$2b$10$uZasTmdngnbGO4ogkvY9b.S7bn.YxLJseCc3MufyX7S0wr5UpgNxy"
+                       nickname: "admin"
+                       email: "admin@admin.org"
+               }
 }
index 1ebedaa089321453be0f66881b8c914cf16ad491..e492a8701030b9e82595ce11f1923ac3a8aa46b8 100644 (file)
@@ -1,7 +1,5 @@
 bcrypt = require "bcrypt"
 
-import Users from require "models"
-
 ROUNDS = 10
 
 {
index 5b2439b85dd5924338fcb8c4e3282e15e8039ac1..27db62a225274614a635e36a84253004d1799352 100644 (file)
   color: white;
 }
 
+.content, .error-list {
+  width: 100%;
+  padding: 10px 10%;
+}
+
+.right {
+  float: right;
+}
+
+.left {
+  float: left;
+}
+
+.hidden {
+  visibility: hidden;
+}
+
+a {
+  text-decoration: none;
+  color: inherit;
+}
+
 .sidebar-problem-list {
   z-index: 50;
   box-shadow: 0 10px 0 0 transparent, 0 10px 0 0 transparent, 10px 0 0 0 transparent, 0 8px 8px 2px rgba(0, 0, 0, 0.5);
   font-size: 16px;
 }
 
+h1 {
+  width: 100%;
+  margin-top: 12px;
+  text-align: center;
+}
+
 body {
   font-family: sans-serif;
   background: url(/static/imgs/grain_background.png);
@@ -102,4 +130,54 @@ body {
   color: white;
 }
 
+button {
+  box-shadow: 0px 2px 6px 0.5px rgba(0, 0, 0, 0.7);
+  border: 1px solid #0077c2;
+  background-color: #0077c2;
+  color: white;
+  font-size: 1rem;
+  padding: 0.75rem;
+  margin-bottom: 12px;
+  display: inline-block;
+}
+button::placeholder {
+  color: #999;
+}
+
+form input, form button, form textarea {
+  box-shadow: 0px 2px 6px 0.5px rgba(0, 0, 0, 0.7);
+  border: 1px solid #0077c2;
+  background-color: #0077c2;
+  color: white;
+  font-size: 1rem;
+  padding: 0.75rem;
+  margin-bottom: 12px;
+  display: inline-block;
+  width: 100%;
+}
+form input::placeholder, form button::placeholder, form textarea::placeholder {
+  color: #999;
+}
+form textarea {
+  background-color: #111;
+  height: 50vh;
+}
+form label {
+  display: inline-block;
+  padding-left: 12px;
+  margin-top: 16px;
+  margin-bottom: 8px;
+}
+
+.error-list ul li {
+  box-shadow: 0px 2px 6px 0.5px rgba(0, 0, 0, 0.5);
+  list-style: none;
+  background-color: #998100;
+  padding: 0.5rem;
+  margin-bottom: 0.5rem;
+}
+.error-list ul li.success {
+  background-color: #0077c2;
+}
+
 /*# sourceMappingURL=core.css.map */
index ce0098160d22c096fce4cbf978c93b322b81fbc1..60084543b4790ed9b60f62c7fa0f8a33cb6a8527 100644 (file)
@@ -1 +1 @@
-{"version":3,"sourceRoot":"","sources":["../scss/_navbar.scss","../scss/_vars.scss","../scss/problem/_sidebar.scss","../scss/core.scss"],"names":[],"mappings":"AAAA;EACC;EACA;EACA,YCIc;EDHd,QCgBe;EDff;EAEA;EAEA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;EACA;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA,kBClCY;EDoCZ;;AAEA;EACC;EACA;;;AE/CJ;EACC;EACA;EACA;EACA;EACA;;AAEA;EACC;EAEA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AC9CF;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EAEA;EACA;EACA;EACA","file":"core.css"}
\ No newline at end of file
+{"version":3,"sourceRoot":"","sources":["../scss/_navbar.scss","../scss/_vars.scss","../scss/_utils.scss","../scss/problem/_sidebar.scss","../scss/core.scss","../scss/_elevate.scss"],"names":[],"mappings":"AAAA;EACC;EACA;EACA,YCIc;EDHd,QCgBe;EDff;EAEA;EAEA;EACA;;AAEA;EACC;EACA;;AAEA;EACC;EACA;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EAEA;EACA,kBClCY;EDoCZ;;AAEA;EACC;EACA;;;AE/CJ;EACC;EACA;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;EACA;;;ACnBD;EACC;EACA;EACA;EACA;EACA;;AAEA;EACC;EAEA;EACA;EAEA;EACA;EAEA;EACA;EACA;EAEA;;AAEA;EACC;;AAGD;EACC;;AAGD;EACC;;AAIF;EACC;EACA;EACA;EACA;EACA;;AAGD;EACC;EACA;EAEA;EACA;EACA;EAEA;EACA;;;AC7CF;EACC;EACA;EACA;;;AAGD;EACC;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;EAEA;EACA;EACA;EACA;;;AAGD;EC/BI;EDiCH;EACA,kBH5Bc;EG6Bd;EAEA;EACA;EACA;EACA;;AAEA;EACC;;;AAKD;EChDG;EDkDF;EACA,kBH7Ca;EG8Cb;EAEA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;;AAGD;EACC;EACA;EACA;EACA;;;AAOD;ECjFG;EDoFF;EACA,kBH3Ee;EG4Ef;EACA;;AAEA;EACC,kBHpFY","file":"core.css"}
\ No newline at end of file
index 3d48d4be9111b196ea49a645c55bf4117e502ce2..78fe86401ecb08389e2408c1e8467d85f32d8f36 100644 (file)
@@ -1,6 +1,6 @@
-.content {
+%content, .content {
        width: 100%;
-       padding: 10px 5%;
+       padding: 10px 10%;
 }
 
 .right {
index d76fe8ca0b0587b1244a9a004cf8151ed72b6405..7b44fce48eeb7a8e11de975b56852be4cff2346d 100644 (file)
@@ -1,6 +1,7 @@
 @import '_vars';
 @import '_elevate';
 @import '_navbar';
+@import '_utils';
 @import 'problem/_list.scss';
 @import 'problem/_sidebar.scss';
 
        font-size: 16px;
 }
 
+h1 {
+       width: 100%;
+       margin-top: 12px;
+       text-align: center;
+}
+
 body {
        font-family: sans-serif;
 
@@ -22,3 +29,67 @@ body {
        background-repeat: repeat;
        color: white;
 }
+
+button {
+       @include elevate(2px, rgba(0, 0, 0, 0.7));
+       border: 1px solid $primary-dark;
+       background-color: $primary-dark;
+       color: white;
+
+       font-size: 1rem;
+       padding: .75rem;
+       margin-bottom: 12px;
+       display: inline-block;
+
+       &::placeholder {
+               color: #999;
+       }
+}
+
+form {
+       input, button, textarea {
+               @include elevate(2px, rgba(0, 0, 0, 0.7));
+               border: 1px solid $primary-dark;
+               background-color: $primary-dark;
+               color: white;
+
+               font-size: 1rem;
+               padding: .75rem;
+               margin-bottom: 12px;
+               display: inline-block;
+               width: 100%;
+
+               &::placeholder {
+                       color: #999;
+               }
+       }
+
+       textarea {
+               background-color: #111;
+               height: 50vh;
+       }
+
+       label {
+               display: inline-block;
+               padding-left: 12px;
+               margin-top: 16px;
+               margin-bottom: 8px;
+       }
+}
+
+.error-list {
+       @extend .content;
+
+       ul li {
+               @include elevate(2px, rgba(0, 0, 0, 0.5));
+
+               list-style: none;
+               background-color: $secondary-dark;
+               padding: .5rem;
+               margin-bottom: .5rem;
+
+               &.success {
+                       background-color: $primary-dark;
+               }
+       }
+}
index 0dc321308c4e5d7baeb20d1df2296fddd027c608..7cb119534ca684d2964b95be1ee1224ccc64fb17 100644 (file)
@@ -1,11 +1,13 @@
 html = require "lapis.html"
+import to_json from require 'lapis.util'
 
 class Login extends html.Widget
        content: =>
-               h1 style: "background-color:red", "Login!"
+               h1 "Login"
 
-               form method: 'POST', ->
-                       input type: 'hidden', name: 'csrf_token', value: @csrf_token
-                       input type: 'text', placeholder: 'Username', name: 'username', ''
-                       input type: 'password', placeholder: 'Password', name: 'password', ''
-                       input type: 'submit', value: 'Submit', ''
+               div class: 'content', ->
+                       form method: 'POST', ->
+                               input type: 'hidden', name: 'csrf_token', value: @csrf_token
+                               input type: 'text', placeholder: 'Username', name: 'username', ''
+                               input type: 'password', placeholder: 'Password', name: 'password', ''
+                               input type: 'submit', value: 'Submit', ''
index c0cc4cecba4e8c30731585aabae6407a7db39f11..c6123e3f26ac647917ccbdb29913ac6e3b28b251 100644 (file)
@@ -3,16 +3,25 @@ import to_json from require 'lapis.util'
 
 class Register extends html.Widget
        content: =>
-               h1 style: "background-color:red", "Register"
+               h1 "Register"
 
-               p ->
-                       text (to_json @errors)
+               div class: 'content', ->
+                       form method: 'POST', ->
+                               input type: 'hidden', name: 'csrf_token', value: @csrf_token
+                               
+                               label for: 'username', 'Username'
+                               p -> input type: 'text', placeholder: 'Username', name: 'username', required: true, ''
 
-               form method: 'POST', ->
-                       p -> input type: 'hidden', name: 'csrf_token', value: @csrf_token
-                       p -> input type: 'text', placeholder: 'Username', name: 'username', required: true, ''
-                       p -> input type: 'password', placeholder: 'Password', name: 'password', required: true, ''
-                       p -> input type: 'password', placeholder: 'Confirm Password', name: 'password_confirmation', required: true, ''
-                       p -> input type: 'text', placeholder: 'Email', name: 'email', required: true, ''
-                       p -> input type: 'text', placeholder: 'Display Name', name: 'nickname', required: true, ''
-                       input type: 'submit', value: 'Submit', ''
+                               label for: 'password', 'Password'
+                               p -> input type: 'password', placeholder: 'Password', name: 'password', required: true, ''
+
+                               label for: 'password_confirmation', 'Confirm Password'
+                               p -> input type: 'password', placeholder: 'Confirm Password', name: 'password_confirmation', required: true, ''
+
+                               label for: 'email', 'Email'
+                               p -> input type: 'text', placeholder: 'Email', name: 'email', required: true, ''
+
+                               label for: 'nickname', 'Display name'
+                               p -> input type: 'text', placeholder: 'Display Name', name: 'nickname', required: true, ''
+
+                               input type: 'submit', value: 'Submit', ''
diff --git a/codebox/views/admin/Tupfile b/codebox/views/admin/Tupfile
new file mode 100644 (file)
index 0000000..f0fe651
--- /dev/null
@@ -0,0 +1 @@
+include_rules
diff --git a/codebox/views/admin/problem.moon b/codebox/views/admin/problem.moon
new file mode 100644 (file)
index 0000000..e71cbb3
--- /dev/null
@@ -0,0 +1,10 @@
+html = require "lapis.html"
+
+class AdminProblems extends html.Widget
+       content: =>
+               ul ->
+                       for problem in *@problems
+                               li "#{problem.name} - #{problem.kind} - #{problem.time_limit}"
+
+               a href: (@url_for 'admin.problem.new'),
+                       -> text 'Create a problem'
diff --git a/codebox/views/admin/problem/Tupfile b/codebox/views/admin/problem/Tupfile
new file mode 100644 (file)
index 0000000..f0fe651
--- /dev/null
@@ -0,0 +1 @@
+include_rules
diff --git a/codebox/views/admin/problem/edit.moon b/codebox/views/admin/problem/edit.moon
new file mode 100644 (file)
index 0000000..7046cad
--- /dev/null
@@ -0,0 +1,24 @@
+html = require "lapis.html"
+
+class AdminProblemEdit extends html.Widget
+       content: =>
+               h1 "Editing '#{@problem.name}'"
+
+               div class: 'content', ->
+                       form method: 'POST', ->
+                               input type: 'hidden', name: 'csrf_token', value: @csrf_token
+                               input type: 'hidden', name: 'problem_id', value: @problem.id
+
+                               label for: 'name', 'Problem name'
+                               input type: 'text', name: 'name', placeholder: 'Problem name', value: @problem.name, ""
+
+                               label for: 'name', 'Short name'
+                               input type: 'text', name: 'short_name', placeholder: 'Short URL name', value: @problem.short_name, ""
+
+                               label for: 'name', 'Problem description'
+                               textarea name: 'description', placeholder: 'Problem description', @problem.description
+
+                               label for: 'name', 'Time limit'
+                               input type: 'number', value: 500, name: 'time_limit', value: @problem.time_limit, ""
+
+                               input type: 'submit', value: 'Update problem'
diff --git a/codebox/views/admin/problem/new.moon b/codebox/views/admin/problem/new.moon
new file mode 100644 (file)
index 0000000..f1d8f05
--- /dev/null
@@ -0,0 +1,24 @@
+html = require "lapis.html"
+
+class AdminProblems extends html.Widget
+       content: =>
+               h1 "Create a problem"
+
+               div class: 'content', ->
+                       form method: 'POST', ->
+                               input type: 'hidden', name: 'csrf_token', value: @csrf_token
+
+                               label for: 'name', 'Problem name'
+                               input type: 'text', name: 'name', placeholder: 'Problem name', ""
+
+                               label for: 'name', 'Short name'
+                               input type: 'text', name: 'short_name', placeholder: 'Short URL name', ""
+
+                               label for: 'name', 'Problem description'
+                               textarea name: 'description', placeholder: 'Problem description', ""
+
+                               label for: 'name', 'Time limit'
+                               input type: 'number', value: 500, name: 'time_limit', ""
+
+                               input type: 'submit', value: 'Create problem'
+
diff --git a/codebox/views/partials/admin_layout.moon b/codebox/views/partials/admin_layout.moon
new file mode 100644 (file)
index 0000000..da11766
--- /dev/null
@@ -0,0 +1,16 @@
+html = require "lapis.html"
+AdminNavbar = require 'views.partials.admin_navbar'
+ErrorList = require 'views.partials.error_list'
+
+class DefaultLayout extends html.Widget
+       content: =>
+               html_5 ->
+                       head ->
+                               link rel: "stylesheet", href: "/static/css/core.css"
+
+                               script type: "text/javascript", src: "/static/js/main.js"
+
+                       body ->
+                               widget AdminNavbar
+                               widget ErrorList
+                               @content_for "inner"
diff --git a/codebox/views/partials/admin_navbar.moon b/codebox/views/partials/admin_navbar.moon
new file mode 100644 (file)
index 0000000..6ea9487
--- /dev/null
@@ -0,0 +1,13 @@
+html = require 'lapis.html'
+
+class AdminNavigation extends html.Widget
+       content: =>
+               div class: 'navbar', ->
+                       div class: 'navbar-logo', ->
+                               img src: '/static/imgs/logo.png'
+
+                       ul ->
+                               a (href: @url_for 'admin.user'), -> li class: { 'selected': @navbar.selected == 0 }, 'Users'
+                               a (href: @url_for 'admin.problem'), -> li class: { 'selected': @navbar.selected == 1 }, 'Problems'
+                               a (href: @url_for 'admin.submission'), -> li class: { 'selected': @navbar.selected == 2 }, 'Submissions'
+                               a (href: @url_for 'admin.competition'), -> li class: { 'selected': @navbar.selected == 3 }, 'Competitions'
diff --git a/codebox/views/partials/error_list.moon b/codebox/views/partials/error_list.moon
new file mode 100644 (file)
index 0000000..8cad3eb
--- /dev/null
@@ -0,0 +1,13 @@
+html = require 'lapis.html'
+
+class ErrorList extends html.Widget
+       content: =>
+               return unless @errors
+
+               div class: 'error-list', ->
+                       ul ->
+                               for err in *@errors
+                                       if err.success then
+                                               li class: 'success', -> text "Success: #{err.msg}"
+                                       else
+                                               li -> text "Error: #{err}"
index 79d5a0886df1d0a4497d369c24a10cede8e09626..38cf9e07a215abc533852175e21b9a661259bdee 100644 (file)
@@ -1,14 +1,16 @@
 html = require "lapis.html"
 Navbar = require 'views.partials.navbar'
+ErrorList = require 'views.partials.error_list'
 
 class DefaultLayout extends html.Widget
        content: =>
                html_5 ->
                        head ->
-                               link rel: "stylesheet", href: "static/css/core.css"
+                               link rel: "stylesheet", href: "/static/css/core.css"
 
-                               script type: "text/javascript", src: "static/js/main.js"
+                               script type: "text/javascript", src: "/static/js/main.js"
 
                        body ->
                                widget Navbar
+                               widget ErrorList
                                @content_for "inner"
index efd1e54f8db9e6f45a8bf9791901ee80411e4fd0..f4d0b9f085e05824dbb644536468504c017fcd02 100644 (file)
@@ -8,8 +8,6 @@ COPY ./executer/package.json /app/package.json
 WORKDIR /app
 RUN yarn
 
-COPY ./executer/Tupfile /app/Tupfile
-COPY ./executer/Tuprules.tup /app/Tuprules.tup
 COPY ./executer/main.js /app/main.js
 COPY ./executer/app /app/app
 
diff --git a/docs/todo b/docs/todo
new file mode 100644 (file)
index 0000000..736b240
--- /dev/null
+++ b/docs/todo
@@ -0,0 +1,5 @@
+[ ] Be able to add test cases to problems
+[ ] Be able to delete problems
+[ ] Be able to delete test cases
+[ ] Be able to order test cases
+[ ] 
index 7c57cb7eda1d1bea551149549f8f6e7d499a7499..ada99d53b19ee91902bb7f453be59a2cfc46f6b7 100644 (file)
@@ -65,19 +65,21 @@ class Executer
 
                        switch (res.status)
                                when 'SUCCESS'
+                                       console.log test_case.output, res.output
                                        output = clean_output res.output
                                        expected = create_matchers (clean_output test_case.output)
 
                                        worked = true
                                        i = 0
                                        for matcher in expected
+                                               console.log matcher.line, output[i]
                                                unless matcher.test output[i]
                                                        worked = false
                                                        break
 
                                                i++
 
-                                       unless i == output.length
+                                       if worked && i != output.length
                                                worked = false
 
                                        run_times[completed] = res.run_time
index 73b063696ec9d92f753aa51117f9ab88a7e460e1..0b58493ae56dae44c57ea59999bb8f46085abf82 100644 (file)
@@ -24,7 +24,7 @@ class CExecuter extends BaseExecuter
                        return { status: 'SUCCESS', output: output, run_time: run_time }
                else if res_code == 124 or res_code == 137
                        bash_shell.kill()
-                       return { status: 'TIME_LIMIT_EXECEED' }
+                       return { status: 'TIME_LIMIT_EXCEEDED' }
                else
                        bash_shell.kill()
                        return { status: 'BAD_EXECUTION', err: err_output }
index 2825dbfdd07451d7a0d7f4dc3a76b569f3e959b3..2ce05f2adc8ddae9856cc34b6b520c37e3b177ac 100644 (file)
@@ -36,21 +36,6 @@ module.exports = (app) ->
                        test: 'This is test data'
                }
 
-       app.post '/submit', (req, res) ->
-               console.log req.body.lang, req.body.code
-
-               request.post 'http://192.168.0.3:8888/executer/status_update',
-                       { json: true, form: { request_token: process.env.REQ_SECRET } },
-                       (err, res, body) ->
-                               if err
-                                       return console.log err
-
-                               console.log body.status
-
-               res.json {
-                       id: 'test'
-               }
-
        app.post '/request', (req, res) ->
                cases = JSON.parse req.body.test_cases
                job_id = uuid()