Skip to content

Commit 1bee1d1

Browse files
committed
Add testdata upload progress bar
1 parent e2cae48 commit 1bee1d1

File tree

6 files changed

+146
-23
lines changed

6 files changed

+146
-23
lines changed

Gemfile

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ gem 'sass-rails'
1515
gem 'bootstrap-sass', '~> 3'
1616
gem 'popper_js'
1717
gem 'sprockets', '~> 4'
18-
# user
18+
19+
# User
1920
gem 'devise'
2021
gem 'omniauth'
2122
gem 'omniauth-facebook'
23+
2224
# Use Redcarpet to render Markdown
2325
gem 'redcarpet'
2426

@@ -28,10 +30,10 @@ gem 'uglifier'
2830
# Use CoffeeScript for .js.coffee assets and views
2931
gem 'coffee-rails'
3032

31-
# use Kaminari to paginate
33+
# Pagination
3234
gem 'kaminari'
3335

34-
# Use jquery as the JavaScript library
36+
# JavaScript library
3537
gem 'jquery-rails'
3638

3739
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
@@ -41,17 +43,22 @@ gem 'jquery-rails'
4143
gem 'json'
4244
gem 'jbuilder'
4345

44-
# nested form: https://github.com/ryanb/nested_form
46+
# Nested form: https://github.com/ryanb/nested_form
4547
gem 'nested_form'
4648

49+
# Upload
4750
# carrierwave, upload handler: https://github.com/carrierwaveuploader/carrierwave
4851
gem 'carrierwave'
4952
gem 'mini_magick'
53+
# compression
54+
gem 'zstd-ruby'
55+
# progress bar
56+
gem 'jquery-fileupload-rails'
5057

5158
# Mathjax, can render latex equation: https://github.com/pmq20/mathjax-rails
5259
gem 'mathjax-rails-3'
5360

54-
# tagging feature: https://github.com/mbleigh/acts-as-taggable-on
61+
# Tagging feature: https://github.com/mbleigh/acts-as-taggable-on
5562
gem 'acts-as-taggable-on'
5663

5764
# Active Admin, db admin tool: https://github.com/gregbell/active_admin
@@ -90,9 +97,6 @@ gem 'net-http'
9097
# Redis for Action Cable
9198
gem 'redis', '~> 4'
9299

93-
# Upload compression
94-
gem 'zstd-ruby'
95-
96100
# Sentry for monitoring
97101
gem 'sentry-ruby'
98102
gem 'sentry-rails'

Gemfile.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ GEM
170170
jbuilder (2.11.5)
171171
actionview (>= 5.0.0)
172172
activesupport (>= 5.0.0)
173+
jquery-fileupload-rails (1.0.0)
174+
actionpack (>= 3.1)
175+
railties (>= 3.1)
176+
sassc
173177
jquery-rails (4.5.1)
174178
rails-dom-testing (>= 1, < 3)
175179
railties (>= 4.2.0)
@@ -405,6 +409,7 @@ DEPENDENCIES
405409
devise
406410
friendly_id
407411
jbuilder
412+
jquery-fileupload-rails
408413
jquery-rails
409414
jquery-tablesorter
410415
json

app/assets/javascripts/application.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@
3232
//= require jquery-tablesorter
3333
//= require jquery-tablesorter/addons/pager/jquery.tablesorter.pager
3434
//= require jquery-tablesorter/beta-testing/pager-custom-controls
35+
36+
//= require jquery-fileupload/basic
37+
//= require jquery-fileupload/vendor/tmpl
38+
3539
//= require_self

app/assets/javascripts/init_code_copy_script.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
divs.forEach((div, idx, _) => initialize(div))
77

88
function initialize(div) {
9-
console.log(div)
10-
119
let btn = div.getElementsByClassName("copy-group-btn")[0]
1210
let code = div.getElementsByClassName("copy-group-code")[0]
1311

app/assets/stylesheets/application.css

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
*= require bootstrap_import
1212
*= require_tree .
1313
*= require_self
14-
*= require custom_styles
1514
*
16-
* Somehow the ".default" in theme.default is stripped by sprocket 4 asset finding logic.
17-
* We add another .default to as an dirty hack to make it find the correct asset.
15+
*= require custom_styles
1816
*
1917
*= require jquery-tablesorter/theme.blue
2018
*= require jquery-tablesorter/addons/pager/jquery.tablesorter.pager
19+
*
2120
*= require animate
21+
*
22+
*= require jquery.fileupload
23+
*= require jquery.fileupload-ui
2224
*/

app/views/testdata/_form.html.erb

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
1-
<%= form_for [@problem, @testdatum], :html => {:multipart => true} do |f| %>
1+
<%= form_for [@problem, @testdatum], :html => {multipart: true, id: 'testdata-form'}, remote: true, authenticity_token: true do |f| %>
22
<%= render partial: "shared/errors", locals: {errors: @testdatum.errors, item: 'testdatum'} %>
33

44
<div class="form-group">
5-
<%= f.label :test_input, "input testdata" %><br>
6-
<%= @testdatum.test_input.to_s.split("/").last(3).join("/") if @testdatum.test_input %><br>
5+
<%= f.label :test_input, "Input testdata" %>
6+
<% if @testdatum.test_input %>
7+
<br><%= @testdatum.test_input.to_s.split("/").last(3).join("/") %>
8+
<% end %>
79
<%= f.file_field :test_input, :class => 'form-control flat' %>
810
</div>
11+
912
<div class="form-group">
10-
<%= f.label :test_output, "output testdata" %><br>
11-
<%= @testdatum.test_output.to_s.split("/").last(3).join("/") if @testdatum.test_output %><br>
13+
<%= f.label :test_output, "Output testdata" %>
14+
<% if @testdatum.test_output %>
15+
<br><%= @testdatum.test_output.to_s.split("/").last(3).join("/") %>
16+
<% end %>
1217
<%= f.file_field :test_output, :class => 'form-control flat' %>
1318
</div>
14-
<br>
1519
<div class="form-group">
1620
<%= f.label :time_limit, "Time Limit" %>
1721
<%= f.number_field :time_limit, :class => 'form-control flat' %>
@@ -33,10 +37,116 @@
3337
<%= f.hidden_field :problem_id %>
3438
</div>
3539

36-
<hr>
37-
38-
<div class="actions">
39-
<%= f.submit :class => 'btn btn-success btn-lg' %>
40+
<div class="form-group flex" style="display: flex; margin-bottom: 0.5em;">
41+
<div class="actions" style="margin-right: 1em; flex: 0 0 0%;">
42+
<%= f.submit :class => 'btn btn-success btn-lg' %>
43+
</div>
44+
<div class="fade" id="progress-fade" style="flex: 1 0 0%;">
45+
<div class="progress progress-striped active" role="progressbar" style="margin-bottom: 0.25em; border-radius: 4px; height: 20px; background: #f0faf0;">
46+
<div id="progress-inner-bar" class="progress-bar progress-bar-success" style="width: 0%;"></div>
47+
</div>
48+
<div id="progress-text" class="progress-extended small"></div>
49+
</div>
4050
</div>
4151

4252
<% end %>
53+
54+
55+
<script>
56+
$(function () {
57+
$('#testdata-form').submit(function (event) {
58+
event.preventDefault();
59+
var _formatFileSize = function (bytes) {
60+
if (typeof bytes !== 'number') {
61+
return '';
62+
}
63+
if (bytes >= (1024*1024*1024)) {
64+
return (bytes / (1024*1024*1024)).toFixed(2) + ' GiB';
65+
}
66+
if (bytes >= (1024*1024)) {
67+
return (bytes / (1024*1024)).toFixed(2) + ' MiB';
68+
}
69+
if (bytes >= 1024) {
70+
return (bytes / 1024).toFixed(2) + ' KiB';
71+
}
72+
return (bytes * 1.0).toFixed(2) + ' byte';
73+
};
74+
var _formatTime = function (seconds) {
75+
var date = new Date(seconds * 1000), days = Math.floor(seconds / 86400);
76+
days = days ? days + 'd ' : '';
77+
if (date.getUTCHours() > 1) {
78+
days = days + ('0' + date.getUTCHours()).slice(-2) + ':';
79+
}
80+
return (
81+
days +
82+
('0' + date.getUTCMinutes()).slice(-2) +
83+
':' +
84+
('0' + date.getUTCSeconds()).slice(-2)
85+
);
86+
};
87+
var _formatPercentage = function (floatValue) {
88+
return (floatValue * 100).toFixed(2) + ' %';
89+
};
90+
var _renderProgress = function (data) {
91+
return (
92+
_formatPercentage(data.loaded / data.total) +
93+
' | ' +
94+
_formatFileSize(data.loaded) +
95+
' / ' +
96+
_formatFileSize(data.total) +
97+
' | ETA ' +
98+
_formatTime((data.total - data.loaded) / (data.byterate)) +
99+
' | ' +
100+
_formatFileSize(data.byterate) +
101+
'/s'
102+
);
103+
};
104+
var formData = new FormData(this);
105+
var lastUpdate = (new Date().getTime()) - 500;
106+
var byterate = 0.0;
107+
var prevLoaded = 0;
108+
$.ajax({
109+
type: 'POST',
110+
url: $(this).attr('action'),
111+
data: formData,
112+
processData: false,
113+
contentType: false,
114+
xhr: function() {
115+
var xhr = new window.XMLHttpRequest();
116+
xhr.upload.onprogress = function(evt) {
117+
if (evt.lengthComputable) {
118+
var now = new Date().getTime();
119+
$('#progress-fade').addClass('in');
120+
if (evt.loaded == evt.total) {
121+
$('#progress-inner-bar').width('100%');
122+
$('#progress-text').text('Processing...');
123+
} else if (now - lastUpdate >= 500) {
124+
var curByterate = ((evt.loaded - prevLoaded) / (now - lastUpdate)) * 1000;
125+
byterate = byterate * 0.7 + curByterate * 0.3;
126+
evt.byterate = curByterate;
127+
lastUpdate = now;
128+
prevLoaded = evt.loaded;
129+
$('#progress-text').text(_renderProgress(evt));
130+
$('#progress-inner-bar').width((evt.loaded / evt.total * 100) + '%');
131+
}
132+
}
133+
};
134+
return xhr;
135+
},
136+
success: function (data) {
137+
document.open();
138+
document.write(data);
139+
document.close();
140+
},
141+
error: function (xhr, status, error) {
142+
$('#progress-fade').removeClass('in');
143+
$('#progress-inner-bar').width('0%');
144+
},
145+
abort: function() {
146+
$('#progress-fade').removeClass('in');
147+
$('#progress-inner-bar').width('0%');
148+
},
149+
});
150+
});
151+
});
152+
</script>

0 commit comments

Comments
 (0)