diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..3fd9f6fd1 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +web: cd client && npm start +api: bundle exec rails s -p 3001 diff --git a/app/assets/javascripts/admins/ajaxform.js b/app/assets/javascripts/admins/ajaxform.js new file mode 100644 index 000000000..20a88247a --- /dev/null +++ b/app/assets/javascripts/admins/ajaxform.js @@ -0,0 +1,1312 @@ +/*! + * jQuery Form Plugin + * version: 3.51.0-2014.06.20 + * Requires jQuery v1.5 or later + * Copyright (c) 2014 M. Alsup + * Examples and documentation at: http://malsup.com/jquery/form/ + * Project repository: https://github.com/malsup/form + * Dual licensed under the MIT and GPL licenses. + * https://github.com/malsup/form#copyright-and-license + */ +/*global ActiveXObject */ + +// AMD support +(function(factory) { + "use strict"; + if (typeof define === 'function' && define.amd) { + // using AMD; register as anon module + define(['jquery'], factory); + } else { + // no AMD; invoke directly + factory((typeof(jQuery) != 'undefined') ? jQuery : window.Zepto); + } + } + + (function($) { + "use strict"; + + /* + Usage Note: + ----------- + Do not use both ajaxSubmit and ajaxForm on the same form. These + functions are mutually exclusive. Use ajaxSubmit if you want + to bind your own submit handler to the form. For example, + + $(document).ready(function() { + $('#myForm').on('submit', function(e) { + e.preventDefault(); // <-- important + $(this).ajaxSubmit({ + target: '#output' + }); + }); + }); + + Use ajaxForm when you want the plugin to manage all the event binding + for you. For example, + + $(document).ready(function() { + $('#myForm').ajaxForm({ + target: '#output' + }); + }); + + You can also use ajaxForm with delegation (requires jQuery v1.7+), so the + form does not have to exist when you invoke ajaxForm: + + $('#myForm').ajaxForm({ + delegation: true, + target: '#output' + }); + + When using ajaxForm, the ajaxSubmit function will be invoked for you + at the appropriate time. + */ + + /** + * Feature detection + */ + var feature = {}; + feature.fileapi = $("").get(0).files !== undefined; + feature.formdata = window.FormData !== undefined; + + var hasProp = !!$.fn.prop; + + // attr2 uses prop when it can but checks the return type for + // an expected string. this accounts for the case where a form + // contains inputs with names like "action" or "method"; in those + // cases "prop" returns the element + $.fn.attr2 = function() { + if (!hasProp) { + return this.attr.apply(this, arguments); + } + var val = this.prop.apply(this, arguments); + if ((val && val.jquery) || typeof val === 'string') { + return val; + } + return this.attr.apply(this, arguments); + }; + + /** + * ajaxSubmit() provides a mechanism for immediately submitting + * an HTML form using AJAX. + */ + $.fn.ajaxSubmit = function(options) { + /*jshint scripturl:true */ + + // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) + if (!this.length) { + log('ajaxSubmit: skipping submit process - no element selected'); + return this; + } + + var method, action, url, $form = this; + + if (typeof options == 'function') { + options = { + success: options + }; + } else if (options === undefined) { + options = {}; + } + + method = options.type || this.attr2('method'); + action = options.url || this.attr2('action'); + + url = (typeof action === 'string') ? $.trim(action) : ''; + url = url || window.location.href || ''; + if (url) { + // clean url (don't include hash vaue) + url = (url.match(/^([^#]+)/) || [])[1]; + } + + options = $.extend(true, { + url: url, + success: $.ajaxSettings.success, + type: method || $.ajaxSettings.type, + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' + }, options); + + // hook for manipulating the form data before it is extracted; + // convenient for use with rich editors like tinyMCE or FCKEditor + var veto = {}; + this.trigger('form-pre-serialize', [this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); + return this; + } + + // provide opportunity to alter form data before it is serialized + if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSerialize callback'); + return this; + } + + var traditional = options.traditional; + if (traditional === undefined) { + traditional = $.ajaxSettings.traditional; + } + + var elements = []; + var qx, a = this.formToArray(options.semantic, elements); + if (options.data) { + options.extraData = options.data; + qx = $.param(options.data, traditional); + } + + // give pre-submit callback an opportunity to abort the submit + if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { + log('ajaxSubmit: submit aborted via beforeSubmit callback'); + return this; + } + + // fire vetoable 'validate' event + this.trigger('form-submit-validate', [a, this, options, veto]); + if (veto.veto) { + log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); + return this; + } + + var q = $.param(a, traditional); + if (qx) { + q = (q ? (q + '&' + qx) : qx); + } + if (options.type.toUpperCase() == 'GET') { + options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; + options.data = null; // data is null for 'get' + } else { + options.data = q; // data is the query string for 'post' + } + + var callbacks = []; + if (options.resetForm) { + callbacks.push(function() { + $form.resetForm(); + }); + } + if (options.clearForm) { + callbacks.push(function() { + $form.clearForm(options.includeHidden); + }); + } + + // perform a load on the target only if dataType is not provided + if (!options.dataType && options.target) { + var oldSuccess = options.success || function() {}; + callbacks.push(function(data) { + var fn = options.replaceTarget ? 'replaceWith' : 'html'; + $(options.target)[fn](data).each(oldSuccess, arguments); + }); + } else if (options.success) { + callbacks.push(options.success); + } + + options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg + var context = options.context || this; // jQuery 1.4+ supports scope context + for (var i = 0, max = callbacks.length; i < max; i++) { + callbacks[i].apply(context, [data, status, xhr || $form, $form]); + } + }; + + if (options.error) { + var oldError = options.error; + options.error = function(xhr, status, error) { + var context = options.context || this; + oldError.apply(context, [xhr, status, error, $form]); + }; + } + + if (options.complete) { + var oldComplete = options.complete; + options.complete = function(xhr, status) { + var context = options.context || this; + oldComplete.apply(context, [xhr, status, $form]); + }; + } + + // are there files to upload? + + // [value] (issue #113), also see comment: + // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 + var fileInputs = $('input[type=file]:enabled', this).filter(function() { + return $(this).val() !== ''; + }); + + var hasFileInputs = fileInputs.length > 0; + var mp = 'multipart/form-data'; + var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); + + var fileAPI = feature.fileapi && feature.formdata; + log("fileAPI :" + fileAPI); + var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; + + var jqxhr; + + // options.iframe allows user to force iframe mode + // 06-NOV-09: now defaulting to iframe mode if file input is detected + if (options.iframe !== false && (options.iframe || shouldUseFrame)) { + // hack to fix Safari hang (thanks to Tim Molendijk for this) + // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d + if (options.closeKeepAlive) { + $.get(options.closeKeepAlive, function() { + jqxhr = fileUploadIframe(a); + }); + } else { + jqxhr = fileUploadIframe(a); + } + } else if ((hasFileInputs || multipart) && fileAPI) { + jqxhr = fileUploadXhr(a); + } else { + jqxhr = $.ajax(options); + } + + $form.removeData('jqxhr').data('jqxhr', jqxhr); + + // clear element array + for (var k = 0; k < elements.length; k++) { + elements[k] = null; + } + + // fire 'notify' event + this.trigger('form-submit-notify', [this, options]); + return this; + + // utility fn for deep serialization + function deepSerialize(extraData) { + var serialized = $.param(extraData, options.traditional).split('&'); + var len = serialized.length; + var result = []; + var i, part; + for (i = 0; i < len; i++) { + // #252; undo param space replacement + serialized[i] = serialized[i].replace(/\+/g, ' '); + part = serialized[i].split('='); + // #278; use array instead of object storage, favoring array serializations + result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); + } + return result; + } + + // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) + function fileUploadXhr(a) { + var formdata = new FormData(); + + for (var i = 0; i < a.length; i++) { + formdata.append(a[i].name, a[i].value); + } + + if (options.extraData) { + var serializedData = deepSerialize(options.extraData); + for (i = 0; i < serializedData.length; i++) { + if (serializedData[i]) { + formdata.append(serializedData[i][0], serializedData[i][1]); + } + } + } + + options.data = null; + + var s = $.extend(true, {}, $.ajaxSettings, options, { + contentType: false, + processData: false, + cache: false, + type: method || 'POST' + }); + + if (options.uploadProgress) { + // workaround because jqXHR does not expose upload property + s.xhr = function() { + var xhr = $.ajaxSettings.xhr(); + if (xhr.upload) { + xhr.upload.addEventListener('progress', function(event) { + var percent = 0; + var position = event.loaded || event.position; /*event.position is deprecated*/ + var total = event.total; + if (event.lengthComputable) { + percent = Math.ceil(position / total * 100); + } + options.uploadProgress(event, position, total, percent); + }, false); + } + return xhr; + }; + } + + s.data = null; + var beforeSend = s.beforeSend; + s.beforeSend = function(xhr, o) { + //Send FormData() provided by user + if (options.formData) { + o.data = options.formData; + } else { + o.data = formdata; + } + if (beforeSend) { + beforeSend.call(this, xhr, o); + } + }; + return $.ajax(s); + } + + // private function for handling file uploads (hat tip to YAHOO!) + function fileUploadIframe(a) { + var form = $form[0], + el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; + var deferred = $.Deferred(); + + // #341 + deferred.abort = function(status) { + xhr.abort(status); + }; + + if (a) { + // ensure that every serialized input is still enabled + for (i = 0; i < elements.length; i++) { + el = $(elements[i]); + if (hasProp) { + el.prop('disabled', false); + } else { + el.removeAttr('disabled'); + } + } + } + + s = $.extend(true, {}, $.ajaxSettings, options); + s.context = s.context || s; + id = 'jqFormIO' + (new Date().getTime()); + if (s.iframeTarget) { + $io = $(s.iframeTarget); + n = $io.attr2('name'); + if (!n) { + $io.attr2('name', id); + } else { + id = n; + } + } else { + $io = $(''); + $io.css({ + position: 'absolute', + top: '-1000px', + left: '-1000px' + }); + } + io = $io[0]; + + + xhr = { // mock object + aborted: 0, + responseText: null, + responseXML: null, + status: 0, + statusText: 'n/a', + getAllResponseHeaders: function() {}, + getResponseHeader: function() {}, + setRequestHeader: function() {}, + abort: function(status) { + var e = (status === 'timeout' ? 'timeout' : 'aborted'); + log('aborting upload... ' + e); + this.aborted = 1; + + try { // #214, #257 + if (io.contentWindow.document.execCommand) { + io.contentWindow.document.execCommand('Stop'); + } + } catch (ignore) {} + + $io.attr('src', s.iframeSrc); // abort op in progress + xhr.error = e; + if (s.error) { + s.error.call(s.context, xhr, e, status); + } + if (g) { + $.event.trigger("ajaxError", [xhr, s, e]); + } + if (s.complete) { + s.complete.call(s.context, xhr, e); + } + } + }; + + g = s.global; + // trigger ajax global events so that activity/block indicators work like normal + if (g && 0 === $.active++) { + $.event.trigger("ajaxStart"); + } + if (g) { + $.event.trigger("ajaxSend", [xhr, s]); + } + + if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { + if (s.global) { + $.active--; + } + deferred.reject(); + return deferred; + } + if (xhr.aborted) { + deferred.reject(); + return deferred; + } + + // add submitting element to data if we know it + sub = form.clk; + if (sub) { + n = sub.name; + if (n && !sub.disabled) { + s.extraData = s.extraData || {}; + s.extraData[n] = sub.value; + if (sub.type == "image") { + s.extraData[n + '.x'] = form.clk_x; + s.extraData[n + '.y'] = form.clk_y; + } + } + } + + var CLIENT_TIMEOUT_ABORT = 1; + var SERVER_ABORT = 2; + + function getDoc(frame) { + /* it looks like contentWindow or contentDocument do not + * carry the protocol property in ie8, when running under ssl + * frame.document is the only valid response document, since + * the protocol is know but not on the other two objects. strange? + * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy + */ + + var doc = null; + + // IE8 cascading access check + try { + if (frame.contentWindow) { + doc = frame.contentWindow.document; + } + } catch (err) { + // IE8 access denied under ssl & missing protocol + log('cannot get iframe.contentWindow document: ' + err); + } + + if (doc) { // successful getting content + return doc; + } + + try { // simply checking may throw in ie8 under ssl or mismatched protocol + doc = frame.contentDocument ? frame.contentDocument : frame.document; + } catch (err) { + // last attempt + log('cannot get iframe.contentDocument: ' + err); + doc = frame.document; + } + return doc; + } + + // Rails CSRF hack (thanks to Yvan Barthelemy) + var csrf_token = $('meta[name=csrf-token]').attr('content'); + var csrf_param = $('meta[name=csrf-param]').attr('content'); + if (csrf_param && csrf_token) { + s.extraData = s.extraData || {}; + s.extraData[csrf_param] = csrf_token; + } + + // take a breath so that pending repaints get some cpu time before the upload starts + function doSubmit() { + // make sure form attrs are set + var t = $form.attr2('target'), + a = $form.attr2('action'), + mp = 'multipart/form-data', + et = $form.attr('enctype') || $form.attr('encoding') || mp; + + // update form attrs in IE friendly way + form.setAttribute('target', id); + if (!method || /post/i.test(method)) { + form.setAttribute('method', 'POST'); + } + if (a != s.url) { + form.setAttribute('action', s.url); + } + + // ie borks in some cases when setting encoding + if (!s.skipEncodingOverride && (!method || /post/i.test(method))) { + $form.attr({ + encoding: 'multipart/form-data', + enctype: 'multipart/form-data' + }); + } + + // support timout + if (s.timeout) { + timeoutHandle = setTimeout(function() { + timedOut = true; + cb(CLIENT_TIMEOUT_ABORT); + }, s.timeout); + } + + // look for server aborts + function checkState() { + try { + var state = getDoc(io).readyState; + log('state = ' + state); + if (state && state.toLowerCase() == 'uninitialized') { + setTimeout(checkState, 50); + } + } catch (e) { + log('Server abort: ', e, ' (', e.name, ')'); + cb(SERVER_ABORT); + if (timeoutHandle) { + clearTimeout(timeoutHandle); + } + timeoutHandle = undefined; + } + } + + // add "extra" data to form if provided in options + var extraInputs = []; + try { + if (s.extraData) { + for (var n in s.extraData) { + if (s.extraData.hasOwnProperty(n)) { + // if using the $.param format that allows for multiple values with the same name + if ($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { + extraInputs.push( + $('').val(s.extraData[n].value) + .appendTo(form)[0]); + } else { + extraInputs.push( + $('').val(s.extraData[n]) + .appendTo(form)[0]); + } + } + } + } + + if (!s.iframeTarget) { + // add iframe to doc and submit the form + $io.appendTo('body'); + } + if (io.attachEvent) { + io.attachEvent('onload', cb); + } else { + io.addEventListener('load', cb, false); + } + setTimeout(checkState, 15); + + try { + form.submit(); + } catch (err) { + // just in case form has element with name/id of 'submit' + var submitFn = document.createElement('form').submit; + submitFn.apply(form); + } + } finally { + // reset attrs and remove "extra" input elements + form.setAttribute('action', a); + form.setAttribute('enctype', et); // #380 + if (t) { + form.setAttribute('target', t); + } else { + $form.removeAttr('target'); + } + $(extraInputs).remove(); + } + } + + if (s.forceSync) { + doSubmit(); + } else { + setTimeout(doSubmit, 10); // this lets dom updates render + } + + var data, doc, domCheckCount = 50, + callbackProcessed; + + function cb(e) { + if (xhr.aborted || callbackProcessed) { + return; + } + + doc = getDoc(io); + if (!doc) { + log('cannot access response document'); + e = SERVER_ABORT; + } + if (e === CLIENT_TIMEOUT_ABORT && xhr) { + xhr.abort('timeout'); + deferred.reject(xhr, 'timeout'); + return; + } else if (e == SERVER_ABORT && xhr) { + xhr.abort('server abort'); + deferred.reject(xhr, 'error', 'server abort'); + return; + } + + if (!doc || doc.location.href == s.iframeSrc) { + // response not received yet + if (!timedOut) { + return; + } + } + if (io.detachEvent) { + io.detachEvent('onload', cb); + } else { + io.removeEventListener('load', cb, false); + } + + var status = 'success', + errMsg; + try { + if (timedOut) { + throw 'timeout'; + } + + var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); + log('isXml=' + isXml); + if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { + if (--domCheckCount) { + // in some browsers (Opera) the iframe DOM is not always traversable when + // the onload callback fires, so we loop a bit to accommodate + log('requeing onLoad callback, DOM not available'); + setTimeout(cb, 250); + return; + } + // let this fall through because server response could be an empty document + //log('Could not access iframe DOM after mutiple tries.'); + //throw 'DOMException: not available'; + } + + //log('response detected'); + var docRoot = doc.body ? doc.body : doc.documentElement; + xhr.responseText = docRoot ? docRoot.innerHTML : null; + xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; + if (isXml) { + s.dataType = 'xml'; + } + xhr.getResponseHeader = function(header) { + var headers = { + 'content-type': s.dataType + }; + return headers[header.toLowerCase()]; + }; + // support for XHR 'status' & 'statusText' emulation : + if (docRoot) { + xhr.status = Number(docRoot.getAttribute('status')) || xhr.status; + xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; + } + + var dt = (s.dataType || '').toLowerCase(); + var scr = /(json|script|text)/.test(dt); + if (scr || s.textarea) { + // see if user embedded response in textarea + var ta = doc.getElementsByTagName('textarea')[0]; + if (ta) { + xhr.responseText = ta.value; + // support for XHR 'status' & 'statusText' emulation : + xhr.status = Number(ta.getAttribute('status')) || xhr.status; + xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; + } else if (scr) { + // account for browsers injecting pre around json response + var pre = doc.getElementsByTagName('pre')[0]; + var b = doc.getElementsByTagName('body')[0]; + if (pre) { + xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; + } else if (b) { + xhr.responseText = b.textContent ? b.textContent : b.innerText; + } + } + } else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { + xhr.responseXML = toXml(xhr.responseText); + } + + try { + data = httpData(xhr, dt, s); + } catch (err) { + status = 'parsererror'; + xhr.error = errMsg = (err || status); + } + } catch (err) { + log('error caught: ', err); + status = 'error'; + xhr.error = errMsg = (err || status); + } + + if (xhr.aborted) { + log('upload aborted'); + status = null; + } + + if (xhr.status) { // we've set xhr.status + status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; + } + + // ordering of these callbacks/triggers is odd, but that's how $.ajax does it + if (status === 'success') { + if (s.success) { + s.success.call(s.context, data, 'success', xhr); + } + deferred.resolve(xhr.responseText, 'success', xhr); + if (g) { + $.event.trigger("ajaxSuccess", [xhr, s]); + } + } else if (status) { + if (errMsg === undefined) { + errMsg = xhr.statusText; + } + if (s.error) { + s.error.call(s.context, xhr, status, errMsg); + } + deferred.reject(xhr, 'error', errMsg); + if (g) { + $.event.trigger("ajaxError", [xhr, s, errMsg]); + } + } + + if (g) { + $.event.trigger("ajaxComplete", [xhr, s]); + } + + if (g && !--$.active) { + $.event.trigger("ajaxStop"); + } + + if (s.complete) { + s.complete.call(s.context, xhr, status); + } + + callbackProcessed = true; + if (s.timeout) { + clearTimeout(timeoutHandle); + } + + // clean up + setTimeout(function() { + if (!s.iframeTarget) { + $io.remove(); + } else { //adding else to clean up existing iframe response. + $io.attr('src', s.iframeSrc); + } + xhr.responseXML = null; + }, 100); + } + + var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) + if (window.ActiveXObject) { + doc = new ActiveXObject('Microsoft.XMLDOM'); + doc.async = 'false'; + doc.loadXML(s); + } else { + doc = (new DOMParser()).parseFromString(s, 'text/xml'); + } + return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; + }; + var parseJSON = $.parseJSON || function(s) { + /*jslint evil:true */ + return window['eval']('(' + s + ')'); + }; + + var httpData = function(xhr, type, s) { // mostly lifted from jq1.4.4 + + var ct = xhr.getResponseHeader('content-type') || '', + xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, + data = xml ? xhr.responseXML : xhr.responseText; + + if (xml && data.documentElement.nodeName === 'parsererror') { + if ($.error) { + $.error('parsererror'); + } + } + if (s && s.dataFilter) { + data = s.dataFilter(data, type); + } + if (typeof data === 'string') { + if (type === 'json' || !type && ct.indexOf('json') >= 0) { + data = parseJSON(data); + } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { + $.globalEval(data); + } + } + return data; + }; + + return deferred; + } + }; + + /** + * ajaxForm() provides a mechanism for fully automating form submission. + * + * The advantages of using this method instead of ajaxSubmit() are: + * + * 1: This method will include coordinates for elements (if the element + * is used to submit the form). + * 2. This method will include the submit element's name/value data (for the element that was + * used to submit the form). + * 3. This method binds the submit() method to the form for you. + * + * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely + * passes the options argument along after properly binding events for submit elements and + * the form itself. + */ + $.fn.ajaxForm = function(options) { + options = options || {}; + options.delegation = options.delegation && $.isFunction($.fn.on); + + // in jQuery 1.3+ we can fix mistakes with the ready state + if (!options.delegation && this.length === 0) { + var o = { + s: this.selector, + c: this.context + }; + if (!$.isReady && o.s) { + log('DOM not ready, queuing ajaxForm'); + $(function() { + $(o.s, o.c).ajaxForm(options); + }); + return this; + } + // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() + log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); + return this; + } + + if (options.delegation) { + $(document) + .off('submit.form-plugin', this.selector, doAjaxSubmit) + .off('click.form-plugin', this.selector, captureSubmittingElement) + .on('submit.form-plugin', this.selector, options, doAjaxSubmit) + .on('click.form-plugin', this.selector, options, captureSubmittingElement); + return this; + } + + return this.ajaxFormUnbind() + .bind('submit.form-plugin', options, doAjaxSubmit) + .bind('click.form-plugin', options, captureSubmittingElement); + }; + + // private event handlers + function doAjaxSubmit(e) { + /*jshint validthis:true */ + var options = e.data; + if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed + e.preventDefault(); + $(e.target).ajaxSubmit(options); // #365 + } + } + + function captureSubmittingElement(e) { + /*jshint validthis:true */ + var target = e.target; + var $el = $(target); + if (!($el.is("[type=submit],[type=image]"))) { + // is this a child element of the submit el? (ex: a span within a button) + var t = $el.closest('[type=submit]'); + if (t.length === 0) { + return; + } + target = t[0]; + } + var form = this; + form.clk = target; + if (target.type == 'image') { + if (e.offsetX !== undefined) { + form.clk_x = e.offsetX; + form.clk_y = e.offsetY; + } else if (typeof $.fn.offset == 'function') { + var offset = $el.offset(); + form.clk_x = e.pageX - offset.left; + form.clk_y = e.pageY - offset.top; + } else { + form.clk_x = e.pageX - target.offsetLeft; + form.clk_y = e.pageY - target.offsetTop; + } + } + // clear form vars + setTimeout(function() { + form.clk = form.clk_x = form.clk_y = null; + }, 100); + } + + + // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm + $.fn.ajaxFormUnbind = function() { + return this.unbind('submit.form-plugin click.form-plugin'); + }; + + /** + * formToArray() gathers form element data into an array of objects that can + * be passed to any of the following ajax functions: $.get, $.post, or load. + * Each object in the array has both a 'name' and 'value' property. An example of + * an array for a simple login form might be: + * + * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] + * + * It is this array that is passed to pre-submit callback functions provided to the + * ajaxSubmit() and ajaxForm() methods. + */ + $.fn.formToArray = function(semantic, elements) { + var a = []; + if (this.length === 0) { + return a; + } + + var form = this[0]; + var formId = this.attr('id'); + var els = semantic ? form.getElementsByTagName('*') : form.elements; + var els2; + + if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390 + els = $(els).get(); // convert to standard array + } + + // #386; account for inputs outside the form which use the 'form' attribute + if (formId) { + els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet + if (els2.length) { + els = (els || []).concat(els2); + } + } + + if (!els || !els.length) { + return a; + } + + var i, j, n, v, el, max, jmax; + for (i = 0, max = els.length; i < max; i++) { + el = els[i]; + n = el.name; + if (!n || el.disabled) { + continue; + } + + if (semantic && form.clk && el.type == "image") { + // handle image inputs on the fly when semantic == true + if (form.clk == el) { + a.push({ + name: n, + value: $(el).val(), + type: el.type + }); + a.push({ + name: n + '.x', + value: form.clk_x + }, { + name: n + '.y', + value: form.clk_y + }); + } + continue; + } + + v = $.fieldValue(el, true); + if (v && v.constructor == Array) { + if (elements) { + elements.push(el); + } + for (j = 0, jmax = v.length; j < jmax; j++) { + a.push({ + name: n, + value: v[j] + }); + } + } else if (feature.fileapi && el.type == 'file') { + if (elements) { + elements.push(el); + } + var files = el.files; + if (files.length) { + for (j = 0; j < files.length; j++) { + a.push({ + name: n, + value: files[j], + type: el.type + }); + } + } else { + // #180 + a.push({ + name: n, + value: '', + type: el.type + }); + } + } else if (v !== null && typeof v != 'undefined') { + if (elements) { + elements.push(el); + } + a.push({ + name: n, + value: v, + type: el.type, + required: el.required + }); + } + } + + if (!semantic && form.clk) { + // input type=='image' are not found in elements array! handle it here + var $input = $(form.clk), + input = $input[0]; + n = input.name; + if (n && !input.disabled && input.type == 'image') { + a.push({ + name: n, + value: $input.val() + }); + a.push({ + name: n + '.x', + value: form.clk_x + }, { + name: n + '.y', + value: form.clk_y + }); + } + } + return a; + }; + + /** + * Serializes form data into a 'submittable' string. This method will return a string + * in the format: name1=value1&name2=value2 + */ + $.fn.formSerialize = function(semantic) { + //hand off to jQuery.param for proper encoding + return $.param(this.formToArray(semantic)); + }; + + /** + * Serializes all field elements in the jQuery object into a query string. + * This method will return a string in the format: name1=value1&name2=value2 + */ + $.fn.fieldSerialize = function(successful) { + var a = []; + this.each(function() { + var n = this.name; + if (!n) { + return; + } + var v = $.fieldValue(this, successful); + if (v && v.constructor == Array) { + for (var i = 0, max = v.length; i < max; i++) { + a.push({ + name: n, + value: v[i] + }); + } + } else if (v !== null && typeof v != 'undefined') { + a.push({ + name: this.name, + value: v + }); + } + }); + //hand off to jQuery.param for proper encoding + return $.param(a); + }; + + /** + * Returns the value(s) of the element in the matched set. For example, consider the following form: + * + *
+ * + * var v = $('input[type=text]').fieldValue(); + * // if no values are entered into the text inputs + * v == ['',''] + * // if values entered into the text inputs are 'foo' and 'bar' + * v == ['foo','bar'] + * + * var v = $('input[type=checkbox]').fieldValue(); + * // if neither checkbox is checked + * v === undefined + * // if both checkboxes are checked + * v == ['B1', 'B2'] + * + * var v = $('input[type=radio]').fieldValue(); + * // if neither radio is checked + * v === undefined + * // if first radio is checked + * v == ['C1'] + * + * The successful argument controls whether or not the field element must be 'successful' + * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). + * The default value of the successful argument is true. If this value is false the value(s) + * for each element is returned. + * + * Note: This method *always* returns an array. If no valid value can be determined the + * array will be empty, otherwise it will contain one or more values. + */ + $.fn.fieldValue = function(successful) { + for (var val = [], i = 0, max = this.length; i < max; i++) { + var el = this[i]; + var v = $.fieldValue(el, successful); + if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { + continue; + } + if (v.constructor == Array) { + $.merge(val, v); + } else { + val.push(v); + } + } + return val; + }; + + /** + * Returns the value of the field element. + */ + $.fieldValue = function(el, successful) { + var n = el.name, + t = el.type, + tag = el.tagName.toLowerCase(); + if (successful === undefined) { + successful = true; + } + + if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || + (t == 'checkbox' || t == 'radio') && !el.checked || + (t == 'submit' || t == 'image') && el.form && el.form.clk != el || + tag == 'select' && el.selectedIndex == -1)) { + return null; + } + + if (tag == 'select') { + var index = el.selectedIndex; + if (index < 0) { + return null; + } + var a = [], + ops = el.options; + var one = (t == 'select-one'); + var max = (one ? index + 1 : ops.length); + for (var i = (one ? index : 0); i < max; i++) { + var op = ops[i]; + if (op.selected) { + var v = op.value; + if (!v) { // extra pain for IE... + v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; + } + if (one) { + return v; + } + a.push(v); + } + } + return a; + } + return $(el).val(); + }; + + /** + * Clears the form data. Takes the following actions on the form's input fields: + * - input text fields will have their 'value' property set to the empty string + * - select elements will have their 'selectedIndex' property set to -1 + * - checkbox and radio inputs will have their 'checked' property set to false + * - inputs of type submit, button, reset, and hidden will *not* be effected + * - button elements will *not* be effected + */ + $.fn.clearForm = function(includeHidden) { + return this.each(function() { + $('input,select,textarea', this).clearFields(includeHidden); + }); + }; + + /** + * Clears the selected form elements. + */ + $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { + var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list + return this.each(function() { + var t = this.type, + tag = this.tagName.toLowerCase(); + if (re.test(t) || tag == 'textarea') { + this.value = ''; + } else if (t == 'checkbox' || t == 'radio') { + this.checked = false; + } else if (tag == 'select') { + this.selectedIndex = -1; + } else if (t == "file") { + if (/MSIE/.test(navigator.userAgent)) { + $(this).replaceWith($(this).clone(true)); + } else { + $(this).val(''); + } + } else if (includeHidden) { + // includeHidden can be the value true, or it can be a selector string + // indicating a special test; for example: + // $('#myForm').clearForm('.special:hidden') + // the above would clean hidden inputs that have the class of 'special' + if ((includeHidden === true && /hidden/.test(t)) || + (typeof includeHidden == 'string' && $(this).is(includeHidden))) { + this.value = ''; + } + } + }); + }; + + /** + * Resets the form data. Causes all form elements to be reset to their original value. + */ + $.fn.resetForm = function() { + return this.each(function() { + // guard against an input with the name of 'reset' + // note that IE reports the reset function as an 'object' + if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { + this.reset(); + } + }); + }; + + /** + * Enables or disables any matching elements. + */ + $.fn.enable = function(b) { + if (b === undefined) { + b = true; + } + return this.each(function() { + this.disabled = !b; + }); + }; + + /** + * Checks/unchecks any matching checkboxes or radio buttons and + * selects/deselects and matching option elements. + */ + $.fn.selected = function(select) { + if (select === undefined) { + select = true; + } + return this.each(function() { + var t = this.type; + if (t == 'checkbox' || t == 'radio') { + this.checked = select; + } else if (this.tagName.toLowerCase() == 'option') { + var $sel = $(this).parent('select'); + if (select && $sel[0] && $sel[0].type == 'select-one') { + // deselect all other options + $sel.find('option').selected(false); + } + this.selected = select; + } + }); + }; + + // expose debug var + $.fn.ajaxSubmit.debug = false; + + // helper fn for console logging + function log() { + if (!$.fn.ajaxSubmit.debug) { + return; + } + var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, ''); + if (window.console && window.console.log) { + window.console.log(msg); + } else if (window.opera && window.opera.postError) { + window.opera.postError(msg); + } + } + + })); \ No newline at end of file diff --git a/app/assets/javascripts/admins/courses/index.js b/app/assets/javascripts/admins/courses/index.js index 0e3473dff..58e962b8c 100644 --- a/app/assets/javascripts/admins/courses/index.js +++ b/app/assets/javascripts/admins/courses/index.js @@ -19,7 +19,7 @@ $(document).on('turbolinks:load', function() { var json = {}; json[s_name] = s_value; $.ajax({ - url: "/admins/courses/" + s_id, + url: "/admins/classrooms/" + s_id, type: "PUT", dataType:'script', data: json diff --git a/app/assets/javascripts/admins/user_schools_statistics/index.js b/app/assets/javascripts/admins/user_schools_statistics/index.js new file mode 100644 index 000000000..1d084ff25 --- /dev/null +++ b/app/assets/javascripts/admins/user_schools_statistics/index.js @@ -0,0 +1,81 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-user-schools-statistics-index-page').length > 0) { + var $form = $('.user-schools-statistic-list-form'); + + // ************** 学校选择 ************* + var matcherFunc = function(params, data){ + if ($.trim(params.term) === '') { + return data; + } + if (typeof data.text === 'undefined') { + return null; + } + + if (data.name && data.name.indexOf(params.term) > -1) { + var modifiedData = $.extend({}, data, true); + return modifiedData; + } + + // Return `null` if the term should not be displayed + return null; + } + + + var defineSchoolSelect = function (schools) { + $form.find('.school-select').select2({ + theme: 'bootstrap4', + placeholder: '选择学校/单位', + minimumInputLength: 1, + data: schools, + templateResult: function (item) { + if(!item.id || item.id === '') return item.text; + return item.name; + }, + templateSelection: function(item){ + if (item.id) { + $form.find('#school_id').val(item.id); + } + return item.name || item.text; + }, + matcher: matcherFunc + }); + }; + + + // 初始化学校选择器 + $.ajax({ + url: '/api/schools/for_option.json', + dataType: 'json', + type: 'GET', + success: function(data) { + defineSchoolSelect(data.schools); + } + }); + + // 清空 + $form.on('click', '.clear-btn', function(){ + $form.find('select[name="date"]').val(''); + $form.find('select[name="province"]').val(''); + $form.find('.school-select').val('').trigger('change'); + $form.find('input[type="submit"]').trigger('click'); + }) + + + // 导出 + $('.export-action').on('click', function(){ + var form = $(".user-schools-statistic-list-form") + var exportLink = $(this); + var date = form.find("select[name='date']").val(); + var schoolId = form.find('input[name="school_id"]').val(); + var province = form.find('input[name="province"]').val(); + console.log(province) + if(province == "" || province == null){ + alert("只能按省份导出"); + return; + } + + var url = exportLink.data("url").split('?')[0] + "?date=" + date + "&school_id=" + schoolId + "&province=" + province; + window.open(url); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/webapp_banners/index.js b/app/assets/javascripts/admins/webapp_banners/index.js new file mode 100644 index 000000000..c1ecf6693 --- /dev/null +++ b/app/assets/javascripts/admins/webapp_banners/index.js @@ -0,0 +1,25 @@ +$(document).on('turbolinks:load', function() { + if ($('.weapp-banner-setting-container').length > 0) { + var $form = $('#course_form'); + + $('.course.banner-item-bottom').on("change", 'input[type="file"]', function() { + var $fileInput = $(this); + var file = this.files[0]; + var imageType = /image.*/; + if (file && file.type.match(imageType)) { + $form.ajaxSubmit() + } + }); + + var $shixunform = $('#shixun_form'); + + $('.shixun.banner-item-bottom').on("change", 'input[type="file"]', function() { + var $fileInput = $(this); + var file = this.files[0]; + var imageType = /image.*/; + if (file && file.type.match(imageType)) { + $shixunform.ajaxSubmit() + } + }); + } +}) \ No newline at end of file diff --git a/app/assets/stylesheets/admins/laboratories.scss b/app/assets/stylesheets/admins/laboratories.scss index 9dc219f6a..28e616c94 100644 --- a/app/assets/stylesheets/admins/laboratories.scss +++ b/app/assets/stylesheets/admins/laboratories.scss @@ -1,171 +1,154 @@ .admins-laboratories-index-page { - .laboratory-list-table { - .member-container { - .laboratory-user { - display: flex; - justify-content: center; - flex-wrap: wrap; - - .laboratory-user-item { - display: flex; - align-items: center; - height: 22px; - line-height: 22px; - padding: 2px 5px; - margin: 2px 2px; - border: 1px solid #91D5FF; - background-color: #E6F7FF; - color: #91D5FF; - border-radius: 4px; + .laboratory-list-table { + .member-container { + .laboratory-user { + display: flex; + justify-content: center; + flex-wrap: wrap; + .laboratory-user-item { + display: flex; + align-items: center; + height: 22px; + line-height: 22px; + padding: 2px 5px; + margin: 2px 2px; + border: 1px solid #91D5FF; + background-color: #E6F7FF; + color: #91D5FF; + border-radius: 4px; + } + } } - } } - } } -.admins-laboratory-settings-show-page, .admins-laboratory-settings-update-page { - .edit-laboratory-setting-container { - .logo-item { - display: flex; - - &-img { - display: block; - width: 80px; - height: 80px; - background: #f0f0f0; - } - - &-upload { - cursor: pointer; - position: absolute; - top: 0; - width: 80px; - height: 80px; - background: #F5F5F5; - border: 1px solid #E5E5E5; - - &::before { - content: ''; - position: absolute; - top: 27px; - left: 39px; - width: 2px; - height: 26px; - background: #E5E5E5; - } - - &::after { - content: ''; - position: absolute; - top: 39px; - left: 27px; - width: 26px; - height: 2px; - background: #E5E5E5; - } - } - - &-left { - position: relative; - width: 80px; - height: 80px; - &.has-img { - .logo-item-upload { - display: none; - } - - &:hover { - .logo-item-upload { - display: block; - background: rgba(145, 145, 145, 0.8); +.admins-laboratory-settings-show-page, +.admins-laboratory-settings-update-page, +.weapp-banner-setting-container { + .edit-laboratory-setting-container { + .logo-item { + display: flex; + &-img { + display: block; + width: 80px; + height: 80px; + background: #f0f0f0; + } + &-upload { + cursor: pointer; + position: absolute; + top: 0; + width: 80px; + height: 80px; + background: #F5F5F5; + border: 1px solid #E5E5E5; + &::before { + content: ''; + position: absolute; + top: 27px; + left: 39px; + width: 2px; + height: 26px; + background: #E5E5E5; + } + &::after { + content: ''; + position: absolute; + top: 39px; + left: 27px; + width: 26px; + height: 2px; + background: #E5E5E5; + } + } + &-left { + position: relative; + width: 80px; + height: 80px; + &.has-img { + .logo-item-upload { + display: none; + } + &:hover { + .logo-item-upload { + display: block; + background: rgba(145, 145, 145, 0.8); + } + } + } + } + &-right { + display: flex; + flex-direction: column; + justify-content: space-between; + color: #777777; + font-size: 12px; + } + &-title { + color: #23272B; + font-size: 14px; } - } - } - } - - &-right { - display: flex; - flex-direction: column; - justify-content: space-between; - color: #777777; - font-size: 12px; - } - - &-title { - color: #23272B; - font-size: 14px; - } - } - - .banner-item { - margin-bottom: 15px; - display: flex; - flex-direction: column; - - &-img { - display: block; - width: 300px; - height: 80px; - background: #f0f0f0; - } - - &-upload { - cursor: pointer; - position: absolute; - top: 0; - width: 300px; - height: 80px; - background: #F5F5F5; - border: 1px solid #E5E5E5; - - &::before { - content: ''; - position: absolute; - top: 27px; - left: 149px; - width: 2px; - height: 26px; - background: #E5E5E5; - } - - &::after { - content: ''; - position: absolute; - top: 39px; - left: 137px; - width: 26px; - height: 2px; - background: #E5E5E5; } - } - - &-top { - margin-bottom: 10px; - } - - &-bottom { - position: relative; - width: 300px; - height: 80px; - - &.has-img { - .banner-item-upload { - display: none; - } - - &:hover { - .banner-item-upload { - display: block; - background: rgba(145, 145, 145, 0.8); + .banner-item { + margin-bottom: 15px; + display: flex; + flex-direction: column; + &-img { + display: block; + width: 300px; + height: 80px; + background: #f0f0f0; + } + &-upload { + cursor: pointer; + position: absolute; + top: 0; + width: 300px; + height: 80px; + background: #F5F5F5; + border: 1px solid #E5E5E5; + &::before { + content: ''; + position: absolute; + top: 27px; + left: 149px; + width: 2px; + height: 26px; + background: #E5E5E5; + } + &::after { + content: ''; + position: absolute; + top: 39px; + left: 137px; + width: 26px; + height: 2px; + background: #E5E5E5; + } + } + &-top { + margin-bottom: 10px; + } + &-bottom { + position: relative; + width: 300px; + height: 80px; + &.has-img { + .banner-item-upload { + display: none; + } + &:hover { + .banner-item-upload { + display: block; + background: rgba(145, 145, 145, 0.8); + } + } + } + } + &-title { + color: #23272B; + font-size: 14px; } - } } - } - - &-title { - color: #23272B; - font-size: 14px; - } } - } } \ No newline at end of file diff --git a/app/controllers/admins/dashboards_controller.rb b/app/controllers/admins/dashboards_controller.rb index 3971971ff..5a85d5a0e 100644 --- a/app/controllers/admins/dashboards_controller.rb +++ b/app/controllers/admins/dashboards_controller.rb @@ -5,6 +5,16 @@ class Admins::DashboardsController < Admins::BaseController @month_active_user_count = User.where(last_login_on: current_month).count @new_user_count = User.where(created_on: current_month).count + + unless Rails.env.development? + shixun_tomcat = edu_setting('cloud_bridge') + + uri = "#{shixun_tomcat}/bridge/monitor/getPodsInfo" + res = interface_get uri, 502, "数据接口延迟" + if res['code'] == 0 + @pod_num = res['sum'] || 0 + end + end end def month_active_user diff --git a/app/controllers/admins/disciplines_controller.rb b/app/controllers/admins/disciplines_controller.rb index 598ab7386..b7b3152fb 100644 --- a/app/controllers/admins/disciplines_controller.rb +++ b/app/controllers/admins/disciplines_controller.rb @@ -7,7 +7,7 @@ class Admins::DisciplinesController < Admins::BaseController def create name = params[:name].to_s.strip return render_error('名称重复') if Discipline.where(name: name).exists? - Discipline.create!(name: name, position: Discipline.all.pluck(:position).max + 1) + Discipline.create!(name: name, position: Discipline.all.pluck(:position).max.to_i + 1) render_ok end @@ -46,7 +46,7 @@ class Admins::DisciplinesController < Admins::BaseController end def adjust_position - max_position = Discipline.all.pluck(:position).max + max_position = Discipline.all.pluck(:position).max.to_i opr = params[:opr] || "down" if (params[:opr] == "up" && current_discipline.position == 1) || (params[:opr] == "down" && current_discipline.position == max_position) @message = "超出范围" diff --git a/app/controllers/admins/laboratory_settings_controller.rb b/app/controllers/admins/laboratory_settings_controller.rb index 283afc175..b22122831 100644 --- a/app/controllers/admins/laboratory_settings_controller.rb +++ b/app/controllers/admins/laboratory_settings_controller.rb @@ -16,7 +16,7 @@ class Admins::LaboratorySettingsController < Admins::BaseController def form_params params.permit(:identifier, :name, - :nav_logo, :login_logo, :tab_logo, :oj_banner, + :nav_logo, :login_logo, :tab_logo, :oj_banner, :shixun_banner, :subject_banner, :course_banner, :competition_banner, :moop_cases_banner, :footer, navbar: %i[name link hidden]) end diff --git a/app/controllers/admins/sub_disciplines_controller.rb b/app/controllers/admins/sub_disciplines_controller.rb index a60f497e5..61f644a91 100644 --- a/app/controllers/admins/sub_disciplines_controller.rb +++ b/app/controllers/admins/sub_disciplines_controller.rb @@ -9,7 +9,7 @@ class Admins::SubDisciplinesController < Admins::BaseController name = params[:name].to_s.strip return render_error('名称不能为空') if name.blank? return render_error('名称重复') if current_discipline.sub_disciplines.where(name: name).exists? - SubDiscipline.create!(name: name, discipline_id: current_discipline.id, position: current_discipline.sub_disciplines.pluck(:position).max + 1) + SubDiscipline.create!(name: name, discipline_id: current_discipline.id, position: current_discipline.sub_disciplines.pluck(:position).max.to_i + 1) render_ok end @@ -47,7 +47,7 @@ class Admins::SubDisciplinesController < Admins::BaseController def adjust_position discipline = current_sub_discipline.discipline - max_position = discipline.sub_disciplines.pluck(:position).max + max_position = discipline.sub_disciplines.pluck(:position).max.to_i opr = params[:opr] || "down" if (params[:opr] == "up" && current_sub_discipline.position == 1) || (params[:opr] == "down" && current_sub_discipline.position == max_position) @message = "超出范围" diff --git a/app/controllers/admins/tag_disciplines_controller.rb b/app/controllers/admins/tag_disciplines_controller.rb index c10f63884..c8e14dbd8 100644 --- a/app/controllers/admins/tag_disciplines_controller.rb +++ b/app/controllers/admins/tag_disciplines_controller.rb @@ -9,7 +9,7 @@ class Admins::TagDisciplinesController < Admins::BaseController name = params[:name].to_s.strip return render_error('名称重复') if current_sub_discipline.tag_disciplines.where(name: name).exists? TagDiscipline.create!(name: name, sub_discipline_id: current_sub_discipline.id, user_id: current_user.id, - position: current_sub_discipline.tag_disciplines.pluck(:position).max + 1) + position: current_sub_discipline.tag_disciplines.pluck(:position).max.to_i + 1) render_ok end @@ -42,7 +42,7 @@ class Admins::TagDisciplinesController < Admins::BaseController def adjust_position sub_discipline = current_tag_discipline.sub_discipline - max_position = sub_discipline.tag_disciplines.pluck(:position).max + max_position = sub_discipline.tag_disciplines.pluck(:position).max.to_i opr = params[:opr] || "down" if (params[:opr] == "up" && current_tag_discipline.position == 1) || (params[:opr] == "down" && current_tag_discipline.position == max_position) @message = "超出范围" diff --git a/app/controllers/admins/user_schools_statistics_controller.rb b/app/controllers/admins/user_schools_statistics_controller.rb new file mode 100644 index 000000000..a4680d5a2 --- /dev/null +++ b/app/controllers/admins/user_schools_statistics_controller.rb @@ -0,0 +1,18 @@ +class Admins::UserSchoolsStatisticsController < Admins::BaseController + + def export + params[:per_page] = 500 + _count, @schools = Admins::UserSchoolsStatisticQuery.call(params) + + filename = ['用户运营统计', Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx' + render xlsx: 'export', filename: filename + end + + def index + default_sort('cnt', 'desc') + total_count, schools = Admins::UserSchoolsStatisticQuery.call(params) + + @schools = paginate schools, total_count: total_count + + end +end \ No newline at end of file diff --git a/app/controllers/admins/weapp_banners_controller.rb b/app/controllers/admins/weapp_banners_controller.rb new file mode 100644 index 000000000..d8e5aedf9 --- /dev/null +++ b/app/controllers/admins/weapp_banners_controller.rb @@ -0,0 +1,52 @@ +class Admins::WeappBannersController < Admins::BaseController + + def index + @shixun = WeappSettings::ShixunBanner.first + @course = WeappSettings::CourseBanner.first + end + + def create + ActiveRecord::Base.transaction do + old_carouse = WeappSettings::CourseBanner.first + + if old_carouse.present? + old_carouse.destroy! + file_path = Util::FileManage.source_disk_filename(old_carouse) + File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 + end + + @course = WeappSettings::CourseBanner.create! + save_image_file(params[:course_banner], @course) + end + end + + + def shixun_banner + ActiveRecord::Base.transaction do + old_shixun = WeappSettings::ShixunBanner.first + + if old_shixun.present? + old_shixun.destroy! + file_path = Util::FileManage.source_disk_filename(old_shixun) + File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 + end + + @shixun = WeappSettings::ShixunBanner.create! + save_image_file(params[:shixun_banner], @shixun) + end + end + + + + + private + + def save_image_file(file, model) + return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile) + + file_path = Util::FileManage.source_disk_filename(model) + File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 + Util.write_file(file, file_path) + end + +end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 15f918c01..15e795bdb 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -72,6 +72,15 @@ class ApplicationController < ActionController::Base if !current_user.shixun_permission(@shixun) tip_exception(403, "..") end + # if !current_user.shixun_permission(@shixun) + # if @shixun.user_scope == 1 + # school_id = @shixun.shixun_schools.first&.school_id + # name = School.find_by(id: school_id)&.name + # tip_exception(-5, "当前实训只对#{name}等单位开放") + # else + # tip_exception(403, "..") + # end + # end end def admin_or_business? @@ -86,13 +95,13 @@ class ApplicationController < ActionController::Base check_account tip_exception(@course.excellent ? 410 : 409, "您没有权限进入") end - if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id - # 实名认证和职业认证的身份判断 - tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication && - @course.professional_certification && (!current_user.authentication && !current_user.professional_certification) - tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication - tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification - end + # if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id + # # 实名认证和职业认证的身份判断 + # tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication && + # @course.professional_certification && (!current_user.authentication && !current_user.professional_certification) + # tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication + # tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification + # end uid_logger("###############user_course_identity:#{@user_course_identity}") end @@ -448,6 +457,25 @@ class ApplicationController < ActionController::Base end end + # 无参类型处理 + def interface_get(uri, status, message) + begin + uid_logger_dubug("--uri_exec: url is #{uri}") + uri = URI.parse(URI.encode(uri.strip)) + res = Net::HTTP.get(uri) + uid_logger_dubug("--uri_exec: .....res is #{res}") + res = JSON.parse(res) + if (res && res['code'] != 0) + tip_exception(status, message) + else + res + end + rescue Exception => e + uid_logger("--uri_exec: exception #{e.message}") + raise Educoder::TipException.new(message) + end + end + # json格式请求 def interface_json_post(uri, params, status, message) begin diff --git a/app/controllers/challenges_controller.rb b/app/controllers/challenges_controller.rb index c7fcb4423..e73243b94 100644 --- a/app/controllers/challenges_controller.rb +++ b/app/controllers/challenges_controller.rb @@ -155,9 +155,16 @@ class ChallengesController < ApplicationController def index uid_logger("identifier: #{params}") + base_columns = "challenges.id, challenges.subject, challenges.st, challenges.score, challenges.position, + challenges.shixun_id, games.identifier, games.status" + join_sql = "LEFT JOIN games ON games.challenge_id = challenges.id AND games.user_id = #{current_user.id}" + # 下面2个参数是为了解决列表获取通关人数与正在游玩人数的问题 + #@pass_games_map = @shixun.challenges.joins(:games).where(games: {status:2}).group(:challenge_id).reorder(nil).count + #@play_games_map = @shixun.challenges.joins(:games).where(games: {status:[0,1]}).group(:challenge_id).reorder(nil).count - @challenges = @shixun.challenges.fields_for_list + @challenges = @shixun.challenges.joins(join_sql).select(base_columns).uniq + #@challenges = @shixun.challenges.fields_for_list @editable = @shixun.status == 0 # before_action:有判断权限,如果没发布,则肯定是管理人员 @user = current_user @shixun.increment!(:visits) @@ -230,7 +237,7 @@ class ChallengesController < ApplicationController logger.info("############shixun_publiced:#{@shixun.public == 0}") if @shixun.public == 0 script = modify_shixun_script @shixun, @shixun.evaluate_script - @shixun.shixun_info.update_column(:evaluate_script, script) + @shixun.shixun_info.update_column(:evaluate_script, script) if script.present? end # TODO: # if path != params[:challenge][:path] diff --git a/app/controllers/concerns/git_helper.rb b/app/controllers/concerns/git_helper.rb index db99f9da5..f97a137bb 100644 --- a/app/controllers/concerns/git_helper.rb +++ b/app/controllers/concerns/git_helper.rb @@ -25,7 +25,7 @@ module GitHelper decode_content = if cd["encoding"] == 'GB18030' && cd['confidence'] > 0.8 content.encode('UTF-8', 'GBK', {:invalid => :replace, :undef => :replace, :replace => ' '}) - elsif cd['encoding'].blank? + elsif cd['encoding'].blank? && !content.blank? raise("ERROR_UTF8") else content.force_encoding('UTF-8') @@ -36,7 +36,7 @@ module GitHelper rescue Exception => e Rails.logger.error(e.message) - error_msg = e.message == "ERROR_UTF8" ? "文件无法预览" : "文档内容获取异常" + error_msg = e.message == "ERROR_UTF8" ? "文件编码异常,请将文件编码设置为UTF-8" : "文档内容获取异常" error_status = e.message == "ERROR_UTF8" ? -2 : -1 raise Educoder::TipException.new(error_status, error_msg) end diff --git a/app/controllers/concerns/login_helper.rb b/app/controllers/concerns/login_helper.rb index b85b17a22..e3ef0480c 100644 --- a/app/controllers/concerns/login_helper.rb +++ b/app/controllers/concerns/login_helper.rb @@ -35,6 +35,7 @@ module LoginHelper UserAction.create(action_id: user&.id, action_type: 'Login', user_id: user&.id, ip: request.remote_ip) user.update_column(:last_login_on, Time.now) + # 注册完成后有一天的试用申请(先去掉) # UserDayCertification.create(user_id: user.id, status: 1) end @@ -44,6 +45,8 @@ module LoginHelper if autologin = cookies.delete(autologin_cookie_name) User.current.delete_autologin_token(autologin) end + + UserOnline.logout(User.current.id) User.current.delete_session_token(session[:tk]) self.logged_user = nil end @@ -52,6 +55,7 @@ module LoginHelper default_yun_session = "#{laboratory.try(:identifier).split('.').first}_user_id" # end session[:"#{default_yun_session}"] = nil + session[:request_user_id] = nil end # Sets the logged in user @@ -78,6 +82,8 @@ module LoginHelper # # end # session[:user_id] = user.id + UserOnline.login(user.id) + session[:request_user_id] = user.id session[:"#{default_yun_session}"] = user.id session[:ctime] = Time.now.utc.to_i session[:atime] = Time.now.utc.to_i diff --git a/app/controllers/course_modules_controller.rb b/app/controllers/course_modules_controller.rb index 0bef519fd..bea248b52 100644 --- a/app/controllers/course_modules_controller.rb +++ b/app/controllers/course_modules_controller.rb @@ -2,9 +2,13 @@ class CourseModulesController < ApplicationController before_action :require_login, :check_auth before_action :set_module, except: [:unhidden_modules] before_action :find_course, only: [:unhidden_modules] - before_action :teacher_or_admin_allowed, except: [:add_second_category] + before_action :teacher_or_admin_allowed, except: [:show, :add_second_category] before_action :teacher_allowed, only: [:add_second_category] + def show + + end + # 模块置顶 def sticky_module # position为1则不做处理,否则该模块的position置为1,position小于当前模块的position加1 diff --git a/app/controllers/course_second_categories_controller.rb b/app/controllers/course_second_categories_controller.rb index b295c0f9f..62cbf83d9 100644 --- a/app/controllers/course_second_categories_controller.rb +++ b/app/controllers/course_second_categories_controller.rb @@ -36,10 +36,13 @@ class CourseSecondCategoriesController < ApplicationController # 更新相应对象的子目录id if @course_module.module_type == "shixun_homework" @category.homework_commons.update_all(course_second_category_id: 0) - @right_url = "/courses/#{@course.id}/shixun_homeworks/#{@course_module.id}" + @right_url = "/classrooms/#{@course.id}/shixun_homeworks/#{@course_module.id}" elsif @course_module.module_type == "attachment" Attachment.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0) - @right_url = "/courses/#{@course.id}/files/#{@course_module.id}" + @right_url = "/classrooms/#{@course.id}/files/#{@course_module.id}" + elsif @course_module.module_type == "video" + @course.course_videos.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0) + @right_url = "/classrooms/#{@course.id}/course_videos" end @category.destroy diff --git a/app/controllers/course_videos_controller.rb b/app/controllers/course_videos_controller.rb new file mode 100644 index 000000000..090c7e29a --- /dev/null +++ b/app/controllers/course_videos_controller.rb @@ -0,0 +1,35 @@ +class CourseVideosController < ApplicationController + before_action :require_login + before_action :validate_params + before_action :find_course, only: [:create] + before_action :find_video, only: [:update] + before_action :teacher_allowed + + def create + title = params[:name].strip + link = params[:link].strip + course_second_category_id = params[:category_id] || 0 + @course.course_videos.create!(title: title, link: link, is_link: 1, user_id: current_user.id, course_second_category_id: course_second_category_id) + render_ok + end + + def update + title = params[:name].strip + link = params[:link].strip + @video.update!(title: title, link: link) + render_ok + end + + private + + def validate_params + tip_exception("视频名称不能为空") if params[:name].blank? + tip_exception("链接地址不能为空") if params[:link].blank? + end + + def find_video + @video = CourseVideo.find params[:id] + @course = @video.course + end + +end \ No newline at end of file diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index d0a2e5e4b..a102f92de 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -30,14 +30,14 @@ class CoursesController < ApplicationController :informs, :update_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :delete_informs, :change_member_role, :course_groups, :join_course_group, :statistics, - :work_score, :act_score, :calculate_all_shixun_scores] + :work_score, :act_score, :calculate_all_shixun_scores, :move_to_category] before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course, :search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list] before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate, :transfer_to_course_group, :delete_from_course, :export_member_scores_excel, :search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, :add_teacher, :export_couser_info, :export_member_act_score, - :update_informs, :new_informs, :delete_informs, :switch_to_student] + :update_informs, :new_informs, :delete_informs, :switch_to_student, :move_to_category] before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin, :set_course_group, :create_group_by_importing_file, :update_task_position, :tasks_list] @@ -102,22 +102,60 @@ class CoursesController < ApplicationController end def course_videos - logger.info("########[#{@course}") - videos = @course.videos + videos = @course.course_videos + @video_module = @course.course_modules.find_by(module_type: "video") + if params[:category_id].present? && params[:category_id].to_i != 0 + @category = @video_module&.course_second_categories.find_by(id: params[:category_id]) + tip_exception("子目录id有误") if !@category.present? + videos = videos.where(course_second_category_id: params[:category_id].to_i) + end + videos = custom_sort(videos, params[:sort_by], params[:sort_direction]) - @count = videos.count + + #sql = "left join videos on videos.id=course_videos.video_id AND (videos.transcoded=1 OR videos.user_id = #{current_user.id})" + #@videos = paginate videos.joins(sql).includes(video: [user: :user_extension], user: :user_extension) + videos = videos.includes(video: [user: :user_extension], user: :user_extension) + videos = videos.where(videos: {transcoded: true}) + .or(videos.where(videos: {user_id: current_user.id})) + .or(videos.where(course_videos: {is_link: true})) + @count = videos.count @videos = paginate videos end def delete_course_video - video = Video.find_by(id: params[:video_id]) - tip_exception(404, "找不到资源") if video.blank? - tip_exception(403, "...") unless (video.user_id == current_user.id || current_user.admin_or_business?) - video.destroy! - AliyunVod::Service.delete_video([video.uuid]) rescue nil + if params[:is_link] + video = @course.course_videos.find_by!(id: params[:video_id]) + tip_exception(403, "...") unless (video.user_id == current_user.id || current_user.admin_or_business?) + video.destroy! + else + video = Video.find_by(id: params[:video_id]) + tip_exception(404, "找不到资源") if video.blank? + tip_exception(403, "...") unless (video.user_id == current_user.id || current_user.admin_or_business?) + video.destroy! + end + render_ok end + # 视频移动到目录 + def move_to_category + + tip_exception("请选择要移动的目录") if params[:new_category_id].blank? + + category = @course.course_second_categories.find_by(id: params[:new_category_id]) + if params[:new_category_id].to_i == 0 || category.present? + video = @course.course_videos.where(video_id: params[:video_ids]).or(@course.course_videos.where(id: params[:video_ids])).first + + user_id = video.user_id || video.video.user_id + tip_exception("您不是课堂管理员或者视频发布者,暂不能移动视频。") unless @user_course_identity < Course::PROFESSOR || user_id == current_user.id + + video.update!(course_second_category_id: params[:new_category_id]) + normal_status(0, "视频移动成功!") + else + normal_status(-1, "目录不存在") + end + end + def visits_plus_one new_visits = @course.visits + 1 @course.update_visits(new_visits) @@ -153,8 +191,8 @@ class CoursesController < ApplicationController begin ActiveRecord::Base.transaction do @course = Course.new(name: params[:name], class_period: params[:class_period], credit: params[:credit], - end_date: params[:end_date], is_public: params[:is_public], school_id: @school.id, - authentication: params[:authentication], professional_certification: params[:professional_certification]) + end_date: params[:end_date], is_public: params[:is_public], school_id: @school.id) + # authentication: params[:authentication], professional_certification: params[:professional_certification]) @course.tea_id = current_user.id if params[:subject_id].blank? @@ -219,8 +257,8 @@ class CoursesController < ApplicationController extra_params[:is_end] = 1 end - extra_params[:authentication] = params[:authentication] - extra_params[:professional_certification] = params[:professional_certification] + # extra_params[:authentication] = params[:authentication] + # extra_params[:professional_certification] = params[:professional_certification] if @course.subject @course.start_date = params[:start_date] @@ -395,9 +433,7 @@ class CoursesController < ApplicationController def destroy if @course.is_delete == 0 @course.delete! - Tiding.where(belong_container: @course).update_all(is_delete: 1) - Tiding.create!(user_id: current_user.id, trigger_user_id: current_user.id, container_id: @course.id, - container_type: 'DeleteCourse', tiding_type: 'System', belong_container: @course, extra: @course.name) + DeleteCourseNotifyJob.perform_later(@course.id, current_user.id) normal_status(0, "成功") else normal_status(-1, "课堂已删除,无需重复操作") @@ -749,6 +785,12 @@ class CoursesController < ApplicationController ActiveRecord::Base.transaction do course_student.destroy! course_teacher.update!(is_active: 1) + teacher_course_record = @course.teacher_group_records.find_by(user_id: current_user.id) + if teacher_course_record.present? + teacher_course_record.update!(group_id: course_student.course_group_id) + else + TeacherGroupRecord.create!(user_id: current_user.id, course_id: @course.id, group_id: course_student.course_group_id) + end CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, [current_user.id]) end normal_status(0, "切换成功") @@ -768,6 +810,12 @@ class CoursesController < ApplicationController ActiveRecord::Base.transaction do course_student.destroy! course_teacher.update!(is_active: 1) + teacher_course_record = @course.teacher_group_records.find_by(user_id: current_user.id) + if teacher_course_record.present? + teacher_course_record.update!(group_id: course_student.course_group_id) + else + TeacherGroupRecord.create!(user_id: current_user.id, course_id: @course.id, group_id: course_student.course_group_id) + end CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, [current_user.id]) end normal_status(0, "切换成功") @@ -790,7 +838,9 @@ class CoursesController < ApplicationController course_student.update_attributes!(is_active: 1) else # 学生身份不存在则创建 - CourseMember.create!(user_id: current_user.id, role: 4, course_id: @course.id) + course_group_id = @course.teacher_group_records.find_by(user_id: current_user.id)&.group_id.to_i + course_group_id = @course.course_groups.find_by(id: course_group_id)&.id.to_i + CourseMember.create!(user_id: current_user.id, role: 4, course_id: @course.id, course_group_id: course_group_id) CourseAddStudentCreateWorksJob.perform_later(@course.id, [current_user.id]) end normal_status(0, "切换成功") @@ -1145,10 +1195,10 @@ class CoursesController < ApplicationController return normal_status(-1, "课堂已结束,无法加入") if course.is_end # 实名认证和职业认证的身份判断 - return normal_status(-2, "该课堂要求成员完成实名和职业认证") if course.authentication && - course.professional_certification && (!current_user.authentication && !current_user.professional_certification) - return normal_status(-2, "该课堂要求成员完成实名认证") if course.authentication && !current_user.authentication - return normal_status(-2, "该课堂要求成员完成职业认证") if course.professional_certification && !current_user.professional_certification + # return normal_status(-2, "该课堂要求成员完成实名和职业认证") if course.authentication && + # course.professional_certification && (!current_user.authentication && !current_user.professional_certification) + # return normal_status(-2, "该课堂要求成员完成实名认证") if course.authentication && !current_user.authentication + # return normal_status(-2, "该课堂要求成员完成职业认证") if course.professional_certification && !current_user.professional_certification # 身份验证 if params[:professor].blank? && params[:assistant_professor].blank? && params[:student].blank? @@ -1247,7 +1297,7 @@ class CoursesController < ApplicationController @is_teacher = @user_course_identity < Course::ASSISTANT_PROFESSOR @course_modules = @course.course_modules.where(hidden: 0) @hidden_modules = @course.course_modules.where(hidden: 1) - @second_category_type = ["shixun_homework", "graduation", "attachment", "board", "course_group"] + @second_category_type = ["shixun_homework", "graduation", "attachment", "board", "course_group", "video", "common_homework", "group_homework"] end def board_list @@ -1440,6 +1490,7 @@ class CoursesController < ApplicationController # Never trust parameters from the scary internet, only allow the white list through. def course_params params.require(:course).permit(:name, :class_period, :credit, :end_date, :is_public, :authentication, :professional_certification) + # params.require(:course).permit(:name, :class_period, :credit, :end_date, :is_public, :authentication, :professional_certification) end def validate_course_name diff --git a/app/controllers/discusses_controller.rb b/app/controllers/discusses_controller.rb index 3b1dff96b..c1850559d 100644 --- a/app/controllers/discusses_controller.rb +++ b/app/controllers/discusses_controller.rb @@ -46,7 +46,7 @@ class DiscussesController < ApplicationController end sql = "select d.id from discusses d join shixuns s on d.dis_id = s.id where s.status = 2 and s.hidden = false and d.root_id is null - and d.hidden = false #{sql1} #{sql2} order by d.created_at desc" + and d.hidden = false and d.dis_type = 'Shixun' #{sql1} #{sql2} order by d.created_at desc" memo_ids = Discuss.find_by_sql(sql).pluck(:id) @memo_count = memo_ids.size @@ -81,8 +81,7 @@ class DiscussesController < ApplicationController begin @discuss = Discuss.create!(:dis_id => params[:container_id], :dis_type => params[:container_type], :content => params[:content].gsub(" \;", "").strip, :user_id => current_user.id, - :praise_count => 0, :position => params[:position], :challenge_id => params[:challenge_id], - :hidden => !current_user.admin?) # 管理员回复的能够显示 + :praise_count => 0, :position => params[:position], :challenge_id => params[:challenge_id]) rescue Exception => e uid_logger_error("create discuss failed : #{e.message}") raise Educoder::TipException.new("评论异常,原因:#{e.message}") diff --git a/app/controllers/ecloud_controller.rb b/app/controllers/ecloud_controller.rb new file mode 100644 index 000000000..c3421215d --- /dev/null +++ b/app/controllers/ecloud_controller.rb @@ -0,0 +1,378 @@ +#encoding=utf-8 +# 云启训练场(EduCoder)个人版 产品编码(appId) 9200108 +# 产品名称 计费类型 套餐编码 +# 云启训练场(EduCoder)个人版 固定包月 9200108001 +# 固定包月 9200108002 +# 固定包月 9200108003 +# --------------------------------------------------- +# 产品名称 计费类型 套餐编码 +# 云启训练场(EduCoder))院校版 包月+按license 9200109001 +# 包月+按license 9200109002 + +# 云启训练场(EduCoder))院校版 产品编码(appId) 9200109 + +require 'net/http' +require 'digest' + +class EcloudController < ApplicationController + + before_action :save_para + before_action :check_sign_key, only: [:ps_new, :ps_update, :bs_new, :bs_update] + + + def index + render file: 'public/react-oschina/build/index.html', :layout => false + end + + def trustie_login + end + + # 测试环境 + # CLIENT_ID = '1022' + # CLIENT_SECRET = '2112037a-6d7a-432b-9081-feb1153d8668' + # SERVER_URL = "https://221.176.54.92:9081/restful/services/" + # + + # 正式环境 + CLIENT_ID = '1056' + CLIENT_SECRET = '2e84256a-3de4-4713-9e02-10ee88a14592' + SERVER_URL = "https://221.176.53.130:44390/services/" + + + ## 签名 + def sign(timestamp) + Digest::MD5.hexdigest("client_id=#{CLIENT_ID}client_key=#{CLIENT_SECRET}timestamp=#{timestamp}").upcase + end + + + # 企业/个人业务开通 + # applyno 申请单号,唯一 + # ecordercode 唯一标志一个企业的订购关系 + # params['opttype']:操作类型。0开通;1变更;2试用转商用;4再次开通 + # 业务列表opttype: 0新增;1注销;2修改 + # serviceparas: 但是由于企业版是按成员收费的,所以serviceparas里面会传成员数;个人版是包月计费的,serviceparas不会传成员数 + def bs_new + ActiveRecord::Base.transaction do + begin + if params['opttype'] == 0 # 开通企业/个人业务 + ecloud = Ecloud.create!(eloud_params) + create_service(params['services'], ecloud.try(:id)) if params['services'].present? + create_product_params(params['productparas'], ecloud.try(:id)) if params['productparas'].present? + # 为管理员添加一条记录 + # 开通的时候都是用户的opttype也是0 + # 如果管理员已经存在,则不用重复开通 + euser = EcloudUser.where(userid: params['userid'], custid: params['custid']).first + unless euser + EcloudUser.create!(custid: params['custid'], opttype: params['opttype'], userid: params['userid'], + username: params['username'], useralias: params['useralias'], + mobile: params['mobile'], email: params['email'], begintime: params['begintime'], + endtime: params['endtime']) + end + elsif params['opttype'] == 2 # 试用转商用 # 变更企业/个人业务 + # 业务列表:注销业务(注销试用的套餐),另一个业务项的操作代码是:新增业务(开通商用的套餐) + # 需要通过产品服务编号和业务编码来区分哪个产品 + ecloud = Ecloud.where(custid: params['custid'], custcode: params['custcode'], productcode: params['productcode'], trial: true).first + + # 注销试用的套餐 + ## 需要注销的套餐; 试用转商用是不会有批量操作的,所以可以使用first + des_service = params['services'].select{|s| s['opttype'] == 1}.first + esd = EcloudService.where(ecloud_id: ecloud.try(:id), code: des_service['code']).first + ecloud.update_attribute(:trial, params['trial']) + esd.update_attributes!(opttype: des_service['opttype'], begintime: des_service['begintime'], endtime: des_service['endtime']) + + # 试用转商用是不会有批量操作的 + # 新增业务 + add_service = params['services'].select{|s| s['opttype'] == 0} + create_service(add_service, ecloud.try(:id)) + # + + elsif params['opttype'] == 1 # 业务变更 + ecloud = Ecloud.where(custid: params['custid'], custcode: params['custcode'], productcode: params['productcode']).first + # 套餐变更 + # 操作代码 0:新增业务,1:注销业务2:修改业务 + # # 新增服务 + add_service = params['services'].select{|s| s['opttype'] == 0} + create_service(add_service, ecloud.try(:id)) if add_service.present? + + # 如果有注销业务,注销业务 + des_services = params['services'].select{|s| s['opttype'] == 1} + if des_services.present? + des_services.each do |ds| + + logger.info("666666#{ecloud.try(:id)}, 55555555#{ds['code']}") + esd = EcloudService.where(ecloud_id: ecloud.try(:id), code: ds['code']).first + esd.update_attributes!(opttype: ds['opttype'], begintime: ds['begintime'], endtime: ds['endtime']) + end + end + + # 变更业务 + edt_services = params['services'].select{|s| s['opttype'] == 2} + if edt_services.present? + edt_services.each do |es| + ese = EcloudService.where(ecloud_id: ecloud.try(:id), code: es['code']).first + ese.update_attributes!(opttype: es['opttype'], begintime: es['begintime'], endtime: es['endtime']) if ese.present? + create_serviceparas(es['serviceparas'].first, ese.id) if ese.present? + end + end + + + # ecloud = Ecloud.where(bossorderid: params['bossorderid']).first + # ecloud.update_attributes!(eloud_params) + # ecloud.ecloud_productparas.destroy + # ecloud.ecloud_services.destroy + # + # ecloud_id = ecloud.try(:id) + + elsif params['opttype'] == 4 # 再次重复开通 + # 再次申请开通,这种情况就是累加时间 + ecloud = Ecloud.where(custid: params['custid'], custcode: params['custcode'], productcode: params['productcode']).first + create_service(params['services'], ecloud.try(:id)) + create_product_params(params['productparas'], ecloud.try(:id)) if params['productparas'].present? + end + + # + # # 非试用情况下,为管理员单独创建一条账号,企业账号 + # unless params['trial'] + # EcloudUser.create!(ecloud_id: ecloud.try(:id), opttype: params['opttype'], userid: params['userid'], + # username: params['username'], useralias: params['useralias'], + # mobile: params['mobile'], email: params['email']) + # end + + render :json => {result: true, errmsg: ""} + # rescue Exception => e + # logger.error(e.message) + # render :json => {code: 500, msg: "#{e.message}"} + # raise ActiveRecord::Rollback + end + end + end + + # 企业/个人业务变更、注销 + # 处理业务平台退订、暂停(欠费暂停,信控暂停等)、暂停后恢复应用业务 + # 这个接口是不会新增业务的 + # 试用退订的时候bossorderid传空字符 + def bs_update + ActiveRecord::Base.transaction do + begin + case params['opttype'] + when 0 # 退订 + opttype = 6 + when 1 # 暂停 + opttype = 7 + when 2 # 恢复 + opttype = 8 + end + if params['bossorderid'].present? + ecloud = Ecloud.where(bossorderid: params['bossorderid']).first + ecloud.update_attribute(:opttype, opttype) + else + # 试用退订 + params['services'].each do |service| + ecloud_id = EcloudService.where(code: service['packagecode']).first.try(:ecloud_id) + Ecloud.find(ecloud_id).update_attribute(:opttype, opttype) + end + end + render :json => {result: true, errmsg: ""} + rescue Exception => e + logger.error(e.message) + render :json => {code: 500, msg: "#{e.message}"} + raise ActiveRecord::Rollback + end + end + end + + # 用户业务开通与变更接口 + # 授权statu为1,取消授权status为0 + # user['opttype']: 操作类型0:开通;1:变更;3: 取消授权;4:暂停;5:恢复; + + def ps_new + ActiveRecord::Base.transaction do + begin + user_params = params['users'] + user_params.each do |user_param| + if user_param['opttype'] == 0 # 开通 + EcloudUser.create!(custid: params['custid'], opttype: user_param['opttype'], userid: user_param['userid'], + username: user_param['username'], useralias: user_param['useralias'], + mobile: user_param['mobile'], email: user_param['email'], begintime: user_param['begintime'].to_s, + endtime: user_param['endtime'].to_s) + elsif user_param['opttype'] == 1 # 变更 + ecloud_user = EcloudUser.where(custid: params['custid'], userid: user_param['userid']).first + ecloud_user.update_attributes!(opttype: user_param['opttype'], username: user_param['username'], + useralias: user_param['useralias'], mobile: user_param['mobile'], email: user_param['email'], + begintime: user_param['begintime'].to_s, endtime: user_param['endtime'].to_s) + end + end + + render :json => {success: true, errmsg: ""} + # end + rescue Exception => e + logger.error(e.message) + render :json => {code: 500, msg: "#{e.message}"} + raise ActiveRecord::Rollback + end + end + end + + # 用户业务状态变更 + # user['opttype']: 操作类型0:开通;1:变更;3: 取消授权;4:暂停;5:恢复; + def ps_update + begin + user_params = params['users'] + user_params.each do |user_param| + case user_param['opttype'] + when 0 # 取消授权 + opttype = 3 + when 1 # 暂停 + opttype = 4 + when 2 # 恢复 + opttype = 5 + end + ecloud_user = EcloudUser.where(custid: params['custid'], userid: user_param['userid']).first + ecloud_user.update_attributes!(opttype: opttype, operatime: params['operatime'], effecttime: params['effecttime']) + end + + render :json => {success: true, errmsg: ""} + rescue Exception => e + logger.error(e.message) + render :json => {code: 500, msg: "#{e.message}"} + end + end + + def ecloud_login_callback + if params[:test] + user_info = decode '{"userid":2147,"custid":2104,"custcode":"E0002018042810010054","custtype":2,"status":2,"username":"15111030087@QW_er","useralias":"15111030087","isadmin":true,"entprise":"04**004","departments":"","departmentnames":"","mobile":"15365386520","email":"15111030087@139.com"}' + else + res = request_ecloud_authorization + + logger.info "oauth2 authorization resp: #{res}" + raise '登录失败' unless res["access_token"] + + user_info = decode get_ecloud_user(res) + logger.info "oauth2 get user info: #{user_info}" + end + + open_user = OpenUsers::Ecloud.find_or_initialize_by(uid: user_info['userid']) do |u| + u.extra = user_info + end + + redirect_to "/users/#{open_user.user.login}/courses" and return if open_user.persisted? + + ActiveRecord::Base.transaction do + user = User.find_or_initialize_by(phone: user_info["mobile"]) do |u| + u.login = "ecoder_#{user_info['mobile']}" + u.type ='User' + u.status = User::STATUS_ACTIVE + u.nickname = user_info['username'] + u.lastname = user_info['username'] + end + + if !user.persisted? + user.mail = user_info["email"] unless user_info["email"].blank? || User.find_by_mail(user_info["email"]) + user.save! + user.create_user_extension! + end + + open_user.user = user + open_user.save! + successful_authentication(user) + + redirect_to "/users/#{user.login}/courses" + end + rescue Exception => e + render :json => {code: 500, msg: "#{e.message}"} + end + + private + + def request_ecloud_authorization + url = "#{SERVER_URL}/oauth2/authorization?grant_type=authorization_code&client_id=#{CLIENT_ID}&scope=&redirect_uri=&code=#{params[:code]}" + decode post(url) + end + + def get_ecloud_user(body) + res = get("#{SERVER_URL}/user/info?access_token=#{body['access_token']}&userid=#{body['uid']}") + end + + def get(url) + uri = URI(url) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = url.start_with?('https') + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + request = Net::HTTP::Get.new(uri.request_uri) + request['Content-Type'] = 'application/json' + request['Accept'] = 'application/json' + response = http.request(request) + + response.body + end + + def post(url) + uri = URI(url) + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = url.start_with?('https') + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + request = Net::HTTP::Post.new(uri.request_uri) + request['Content-Type'] = 'application/json' + request['Accept'] = 'application/json' + response = http.request(request) + + response.body + end + + def decode(s) + begin + obj = ActiveSupport::JSON.decode(s) + rescue ActiveSupport::JSON.parse_error + logger.error("Attempted to decode invalid JSON: #{s}") + end + end + + private + def eloud_params + return {applyno: params['applyno'], ecordercode: params['ecordercode'], opttype: params['opttype'], + trial: params['trial'], bossorderid: params['bossorderid'], custid: params['custid'], custtype: params['custtype'], + custcode: params['custcode'], registersource: params['registersource'], custname: params['custname'], + userid: params['userid'], username: params['username'], useralias: params['useralias'], mobile: params['mobile'], + email: params['email'], productcode: params['productcode'], begintime: params['begintime'], + endtime: params['endtime']} + end + + # 新增套餐 + def create_service services, ecloud_id + services.each do |service| + ese = EcloudService.create!(opttype: service['opttype'], code: service['code'], begintime: service['begintime'], + endtime: service['endtime'], ecloud_id: ecloud_id) + if service['serviceparas'].present? + create_serviceparas(service['serviceparas'].first, ese.id) + end + end + end + + def create_serviceparas params_serviceparas, ese_id + if params_serviceparas.present? + EcloudServieceServicepara.create(key: params_serviceparas['key'], value: params_serviceparas['value'], ecloud_service_id: ese_id) + end + end + + def create_product_params params, ecloud_id + params.each do |p| + EcloudProductpara.create(key: p['key'], value: p['value'], ecloud_id: ecloud_id) + end + end + + def check_sign_key + sign = sign(params['timestamp']) + if sign != params['sign'] + render :json => {code: 501, msg: "sign的值错误"} + return + end + end + + def save_para + EcloudLog.create(url: request.url, para_value: params, applyno: params['applyno'], custid: params['custid'], + custcode: params['custcode'], userid: params['userid'], username: params['username']) + end + +end diff --git a/app/controllers/examination_banks_controller.rb b/app/controllers/examination_banks_controller.rb index bf3bb7a3a..3df11a432 100644 --- a/app/controllers/examination_banks_controller.rb +++ b/app/controllers/examination_banks_controller.rb @@ -1,9 +1,9 @@ class ExaminationBanksController < ApplicationController include PaginateHelper before_action :require_login - before_action :certi_identity_auth, only: [:create, :edit, :update, :destroy, :set_public, :revoke_item] - before_action :find_exam, except: [:index, :create] - before_action :edit_auth, only: [:update, :destroy, :set_public, :revoke_item] + before_action :certi_identity_auth, only: [:create, :edit, :update, :destroy, :set_public, :revoke_item, :cancel_items] + before_action :find_exam, except: [:index, :create, :cancel_items] + before_action :edit_auth, only: [:update, :destroy, :set_public, :revoke_item, :cancel_items] before_action :identity_auth, only: [:index] def index @@ -36,8 +36,8 @@ class ExaminationBanksController < ApplicationController end current_user.item_baskets.destroy_all + render_ok({exam_id: exam.id}) end - render_ok rescue ApplicationService::Error => ex render_error(ex.message) end @@ -76,6 +76,11 @@ class ExaminationBanksController < ApplicationController render_ok end + def cancel_items + current_user.item_baskets.where(item_bank_id: params[:item_ids]).destroy_all + render_ok + end + private def form_params diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 15f5c67d9..73bd07954 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -379,6 +379,7 @@ class ExercisesController < ApplicationController #试卷的提交设置 def commit_setting + tip_exception("助教无权限修改本试卷") if @user_course_identity == Course::ASSISTANT_PROFESSOR && !@exercise.assistant_auth ActiveRecord::Base.transaction do error_count = 0 # 判断循环里是否有已发布/已截止的,且时间更改了的分班。 # course_group_ids = @course.teacher_course_group_ids(current_user.id) #当前老师的班级id数组 @@ -398,6 +399,7 @@ class ExercisesController < ApplicationController choice_random = params[:choice_random] ? true : false score_open = params[:score_open] ? true : false #分数是否公开 answer_open = params[:answer_open] ? true : false #答案是否公开 + assistant_auth = params[:assistant_auth] # 助教权限 # 统一设置或者分班为0,则更新试卷,并删除试卷分组 if unified_setting || (course_group_ids.size == 0) @@ -426,7 +428,8 @@ class ExercisesController < ApplicationController :answer_open => answer_open, :exercise_status => exercise_status_n, :publish_time => params_publish_time, - :end_time => params_end_time + :end_time => params_end_time, + :assistant_auth => assistant_auth } @exercise.update!(exercise_params) @exercise.exercise_group_settings.destroy_all @@ -529,7 +532,8 @@ class ExercisesController < ApplicationController :answer_open => answer_open, :exercise_status => exercise_status, :publish_time => p_time, - :end_time => e_time + :end_time => e_time, + :assistant_auth => assistant_auth } @exercise.update!(exercise_params) if @exercise.exercise_status == Exercise::PUBLISHED @@ -770,10 +774,11 @@ class ExercisesController < ApplicationController ex_group_setting = exercise.exercise_group_settings old_exercise_groups = ex_group_setting.find_in_exercise_group("course_group_id", g_course) #试卷的分组设置 left_course_groups = teacher_course_group_ids - g_course + all_left_groups = all_course_group_ids - g_course left_exercise_groups = ex_group_setting.find_in_exercise_group("course_group_id", left_course_groups) if left_exercise_groups.blank? && exercise.unified_setting - if left_course_groups.size > 0 #开始为统一设置,但是立即截止为分班。则创建没有立即截止的班级的exercise_group_setting - left_course_groups.each do |g| + if all_left_groups.size > 0 #开始为统一设置,但是立即截止为分班。则创建没有立即截止的班级的exercise_group_setting + all_left_groups.each do |g| ex_group_options = { :exercise_id => exercise.id, :course_group_id => g, diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 25e0de44a..427545716 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -2,13 +2,13 @@ class FilesController < ApplicationController include MessagesHelper before_action :require_login, :check_auth, except: %i[index] - before_action :find_course, except: %i[public_with_course_and_project mine_with_course_and_project] + before_action :find_course, except: %i[public_with_course_and_project mine_with_course_and_project update_visits] before_action :find_ids, only: %i[bulk_delete bulk_send bulk_move bulk_public bulk_publish] before_action :file_validate_sort_type, only: :index before_action :validate_send_message_to_course_params, only: :bulk_send before_action :set_pagination, only: %i[index public_with_course_and_project mine_with_course_and_project] - before_action :validate_upload_params, only: %i[upload import] - before_action :find_file, only: %i[show setting update] + before_action :validate_upload_params, only: %i[import] + before_action :find_file, only: %i[show setting update update_visits] before_action :publish_params, only: %i[upload import update] SORT_TYPE = %w[created_on downloads quotes] @@ -163,6 +163,7 @@ class FilesController < ApplicationController # 上传资源 def upload + find_course_second_category_id attachment_ids = params[:attachment_ids] course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id # is_unified_setting = params.has_key?(:is_unified_setting) ? params[:is_unified_setting] : true @@ -170,25 +171,48 @@ class FilesController < ApplicationController # course_group_publish_times = params[:course_group_publish_times] || [] begin - attachment_ids.each do |attchment_id| - attachment = Attachment.find_by_id attchment_id - unless attachment.nil? - attachment.container = @course - attachment.course_second_category_id = course_second_category_id - attachment.description = params[:description] - attachment.is_public = params[:is_public] && @course.is_public == 1 ? 1 : 0 - attachment.is_publish = @atta_is_publish - attachment.delay_publish = @atta_delay_publish - attachment.publish_time = @atta_publish_time - attachment.unified_setting = @unified_setting - if @unified_setting == 0 - attachment_group_setting attachment, params[:group_settings] + if attachment_ids.present? + attachment_ids.each do |attchment_id| + attachment = Attachment.find_by_id attchment_id + unless attachment.nil? + attachment.container = @course + attachment.course_second_category_id = course_second_category_id + attachment.description = params[:description] + attachment.is_public = params[:is_public] && @course.is_public == 1 ? 1 : 0 + attachment.is_publish = @atta_is_publish + attachment.delay_publish = @atta_delay_publish + attachment.publish_time = @atta_publish_time + attachment.unified_setting = @unified_setting + if @unified_setting == 0 + attachment_group_setting attachment, params[:group_settings] + end + # attachment.set_publish_time(publish_time) if is_unified_setting + # attachment.set_course_group_publish_time(@course, course_group_publish_times) if @course.course_groups.size > 0 && !is_unified_setting && publish_time.blank? + attachment.save! end - # attachment.set_publish_time(publish_time) if is_unified_setting - # attachment.set_course_group_publish_time(@course, course_group_publish_times) if @course.course_groups.size > 0 && !is_unified_setting && publish_time.blank? - attachment.save! end + else + tip_exception("资源名称不能为空") if params[:name].blank? + tip_exception("资源名称不能超过60个字符") if params[:name].strip.length > 60 + tip_exception("链接地址不能为空") if params[:link].blank? + attachment = Attachment.new + attachment.container = @course + attachment.course_second_category_id = course_second_category_id + attachment.author_id = current_user.id + attachment.filename = params[:name].strip + attachment.link = params[:link].strip + attachment.description = params[:description] + attachment.is_public = params[:is_public] && @course.is_public == 1 ? 1 : 0 + attachment.is_publish = @atta_is_publish + attachment.delay_publish = @atta_delay_publish + attachment.publish_time = @atta_publish_time + attachment.unified_setting = @unified_setting + if @unified_setting == 0 + attachment_group_setting attachment, params[:group_settings] + end + attachment.save! end + rescue Exception => e uid_logger_error(e.message) tip_exception(e.message) @@ -265,6 +289,11 @@ class FilesController < ApplicationController @old_attachment.save! @new_attachment.delete end + if params[:name].present? && params[:link].present? + tip_exception("资源名称不能超过60个字符") if params[:name].strip.length > 60 + @old_attachment.filename = params[:name].strip + @old_attachment.link = params[:link].strip + end @old_attachment.is_public = is_public == true && @course.is_public == 1 ? 1 : 0 @old_attachment.is_publish = @atta_is_publish @old_attachment.delay_publish = @atta_delay_publish @@ -326,6 +355,11 @@ class FilesController < ApplicationController end end + def update_visits + @file.increment!(:downloads) + render_ok + end + private def find_file @file = Attachment.find params[:id] diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index cfcf92e4c..7015cca40 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -963,7 +963,7 @@ class GamesController < ApplicationController begin shixun = game.myshixun.shixun shixun_tomcat = edu_setting('cloud_bridge') - #service_host = edu_setting('vnc_url') + service_host = edu_setting('vnc_url') tpiGitURL = "#{edu_setting('git_address_domain')}/#{game.myshixun.repo_path}" uri = "#{shixun_tomcat}/bridge/vnc/getvnc" @@ -973,14 +973,15 @@ class GamesController < ApplicationController if res && res['code'].to_i != 0 raise("实训云平台繁忙(繁忙等级:99)") end - @vnc_url = res['showServer'] - # if request.subdomain == "pre-newweb" || request.subdomain == "test-newweb" - # # 无域名版本 - # "http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless" - # else - # # 有域名版本 - # "https://#{res['port']}.#{service_host}/vnc_lite.html?password=headless" - # end + # @vnc_url = res['showServer'] + @vnc_url = + if request.subdomain == "pre-newweb" || request.subdomain == "test-newweb" + # 无域名版本 + "http://#{service_host}:#{res['port']}/vnc_lite.html?password=headless" + else + # 有域名版本 + "https://#{res['port']}.#{service_host}/vnc_lite.html?password=headless" + end @vnc_evaluate = shixun.vnc_evaluate rescue Exception => e Rails.logger.error(e.message) diff --git a/app/controllers/hack_user_lastest_codes_controller.rb b/app/controllers/hack_user_lastest_codes_controller.rb index 353833ee2..3f5efb0ca 100644 --- a/app/controllers/hack_user_lastest_codes_controller.rb +++ b/app/controllers/hack_user_lastest_codes_controller.rb @@ -13,7 +13,8 @@ class HackUserLastestCodesController < ApplicationController end def update_code - @my_hack.update_attribute(:code, params[:code]) + # 防止tpm改动了语言,所以更新语言字段 + @my_hack.update_attributes(code: params[:code], language: @hack.language) render_ok end @@ -72,8 +73,8 @@ class HackUserLastestCodesController < ApplicationController def record_detail @hack_user = HackUserCode.find params[:id] set = HackSet.find_by(id: @hack_user.error_test_set_id) - @pass_set_count = set ? set.position - 1 : 0 @set_count = @hack_user.hack.hack_sets.count + @pass_set_count = set ? set.position - 1 : (@hack_user.status == 0 ? @set_count : 0) @my_hack = @hack_user.hack_user_lastest_code end @@ -87,7 +88,7 @@ class HackUserLastestCodesController < ApplicationController # 只有编译出错时,才正则匹配错误行数 error_line= if ojEvaResult['status'] == "4" || ojEvaResult['status'] == "5" - regular_match_error_line ojEvaResult['outPut'], @my_hack.hack.language + regular_match_error_line ojEvaResult['outPut'], @hack.language end # debug 与submit 公用的参数 @@ -100,7 +101,7 @@ class HackUserLastestCodesController < ApplicationController if ojEvaResult['execMode'] == "debug" save_debug_data ds_params elsif ojEvaResult['execMode'] == "submit" - save_submit_data ds_params.merge(expected_output: testCase['expectedOutput'], + save_submit_data ds_params.merge(expected_output: testCase['expectedOutput'], language: @hack.language, error_test_set_id: ojEvaResult['failCaseNum']) end # 评测完成后,还原评测中的状态 @@ -134,10 +135,11 @@ class HackUserLastestCodesController < ApplicationController end testCases = Base64.encode64(test_sets.to_json) #codeFileContent = Base64.urlsafe_encode64(@my_hack.code) + Rails.logger.info("##########code_#{@my_hack.identifier}: #{@my_hack.code}") debug_params = {execMode: exec_mode, tpiID: @my_hack.identifier, testCases: testCases, - platform: @my_hack.language, + platform: @my_hack.hack.language, codeFileContent: @my_hack.code, timeLimit: @hack.time_limit, sec_key: Time.now.to_i} @@ -148,7 +150,7 @@ class HackUserLastestCodesController < ApplicationController # 正则错误行数 def regular_match_error_line content, language - content = Base64.decode64(content).force_encoding("utf-8") + content = Base64.decode64(content) #.force_encoding("utf-8") logger.info("######content: #{content}") case language when 'Java' diff --git a/app/controllers/hacks_controller.rb b/app/controllers/hacks_controller.rb index 6eb454feb..6337a426b 100644 --- a/app/controllers/hacks_controller.rb +++ b/app/controllers/hacks_controller.rb @@ -64,7 +64,7 @@ class HacksController < ApplicationController render_ok({identifier: hack.identifier}) rescue => e logger.error("########create_hack_error: #{e.message}") - render_error("创建失败: #{e.message}") + render_error("#{e.message}") end end @@ -96,7 +96,7 @@ class HacksController < ApplicationController render_ok rescue Exception => e logger.error("####update_hack_error: #{e.message}") - render_error("更新失败: #{e.message}") + render_error("#{e.message}") end end diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 082c8118d..faea30242 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -6,7 +6,7 @@ class HomeworkCommonsController < ApplicationController before_action :require_login, :check_auth, except: [:index, :choose_category] before_action :find_course, only: [:index, :create, :new, :shixuns, :subjects, :create_shixun_homework, :publish_homework, - :end_homework, :set_public, :choose_category, :move_to_category, :choose_category, + :end_homework, :set_public, :move_to_category, :choose_category, :create_subject_homework, :multi_destroy, :add_to_homework_bank] before_action :find_homework, only: [:edit, :show, :update, :group_list, :homework_code_repeat, :code_review_results, :code_review_detail, :show_comment, :settings, :works_list, :update_settings, @@ -34,7 +34,7 @@ class HomeworkCommonsController < ApplicationController module_type = @homework_type == 4 ? "shixun_homework" : @homework_type == 1 ? "common_homework" : "group_homework" @homework_commons = @course.homework_commons.where(homework_type: @homework_type) @main_category = @course.course_modules.find_by(module_type: module_type) - if @homework_type == 4 && !params[:category].blank? + if !params[:category].blank? @category = @main_category.course_second_categories.find_by(id: params[:category]) tip_exception("子目录id有误") if !@category.present? @homework_commons = @homework_commons.where(course_second_category_id: params[:category]) @@ -58,7 +58,7 @@ class HomeworkCommonsController < ApplicationController end @published_count = @user_course_identity < Course::STUDENT ? @homework_commons.homework_published.size : - @homework_commons.size + @homework_commons.size unless search.blank? @homework_commons = @homework_commons.where("homework_commons.name like ?", "%#{search}%") @@ -70,9 +70,25 @@ class HomeworkCommonsController < ApplicationController else case order when '1' - sql_str = %Q(homework_detail_manuals.comment_status = #{order} and homework_commons.end_time > '#{Time.now}') + if @user_course_identity == Course::STUDENT + unified_homework_ids = @homework_commons.published_no_end.unified_setting.pluck(:id) + group_homework_ids = @course.homework_group_settings.where("course_group_id = #{@member.course_group_id&.to_i}").published_no_end.pluck(:homework_common_id) + homework_ids = unified_homework_ids + group_homework_ids + homework_ids = homework_ids.blank? ? "(-1)" : "(" + homework_ids.join(",") + ")" + sql_str = %Q(homework_commons.id in #{homework_ids}) + else + sql_str = %Q(homework_detail_manuals.comment_status = #{order} and homework_commons.end_time > '#{Time.now}') + end when '2' - sql_str = %Q(allow_late = 1 and homework_commons.end_time < '#{Time.now}' and (late_time is null or late_time > '#{Time.now}')) + if @user_course_identity == Course::STUDENT + unified_homework_ids = @homework_commons.homework_ended.unified_setting.pluck(:id) + group_homework_ids = @course.homework_group_settings.where("course_group_id = #{@member.course_group_id&.to_i}").has_end.pluck(:homework_common_id) + homework_ids = unified_homework_ids + group_homework_ids + homework_ids = homework_ids.blank? ? "(-1)" : "(" + homework_ids.join(",") + ")" + sql_str = %Q(homework_commons.id in #{homework_ids} and allow_late = 1 and (late_time is null or late_time > '#{Time.now}')) + else + sql_str = %Q(allow_late = 1 and homework_commons.end_time < '#{Time.now}' and (late_time is null or late_time > '#{Time.now}')) + end when '3' sql_str = %Q(homework_detail_manuals.comment_status = #{order} and homework_detail_manuals.evaluation_end > '#{Time.now}') when '4' @@ -87,16 +103,16 @@ class HomeworkCommonsController < ApplicationController end @task_count = @homework_commons.size - order_str = @homework_type == 4 ? "position DESC" : "IF(ISNULL(homework_commons.publish_time),0,1), homework_commons.publish_time DESC, + order_str = @homework_type == 4 ? "homework_commons.position DESC" : "IF(ISNULL(homework_commons.publish_time),0,1), homework_commons.publish_time DESC, homework_commons.created_at DESC" @homework_commons = @homework_commons.order(order_str).page(page).per(15) if @homework_type == 4 - @homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings, :shixuns) + @homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings, :homework_group_settings, :shixuns, :course_second_category, user: :user_extension) elsif @homework_type == 3 - @homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings, :homework_detail_group) + @homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings, :homework_group_settings, :homework_detail_group, :course_second_category, user: :user_extension) else - @homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings) + @homework_commons = @homework_commons.includes(:homework_detail_manual, :published_settings, :homework_group_settings, :course_second_category, user: :user_extension) end end @@ -112,7 +128,7 @@ class HomeworkCommonsController < ApplicationController if @homework.publish_time.blank? || (@homework.publish_time > Time.now) @student_works = [] if (params[:format] == "xlsx") || (params[:format] == "zip") - normal_status(-1,"作业未发布") + normal_status(-1, "作业未发布") end else if @user_course_identity == Course::STUDENT @@ -129,14 +145,14 @@ class HomeworkCommonsController < ApplicationController # 学生已提交作品且补交(提交)已截止、作品公开、非匿评阶段 if @work&.work_status.to_i > 0 && (@homework.work_public || @homework.score_open) && - ((!@homework.anonymous_comment && @homework.end_or_late) || (@homework_detail_manual.comment_status > 4 && @homework.end_or_late)) + ((!@homework.anonymous_comment && @homework.end_or_late) || (@homework_detail_manual.comment_status > 4 && @homework.end_or_late)) @student_works = student_works.where("student_works.user_id != #{@work.user_id}") # 匿评、申诉阶段只能看到分配给自己的匿评作品 elsif @work&.work_status.to_i > 0 && @homework.anonymous_comment && @homework_detail_manual.comment_status > 2 && @homework_detail_manual.comment_status <= 4 @is_evaluation = true @student_works = student_works.joins(:student_works_evaluation_distributions).where( - "student_works_evaluation_distributions.user_id = #{@current_user.id}") + "student_works_evaluation_distributions.user_id = #{@current_user.id}") else @student_works = [] end @@ -157,7 +173,7 @@ class HomeworkCommonsController < ApplicationController unless params[:teacher_comment].blank? student_work_ids = StudentWorksScore.where(student_work_id: @student_works.map(&:id)).pluck(:student_work_id) if params[:teacher_comment].to_i == 0 - @student_works = @student_works.where.not(id: student_work_ids) + @student_works = @student_works.where.not(id: student_work_ids) elsif params[:teacher_comment].to_i == 1 @student_works = @student_works.where(id: student_work_ids) end @@ -166,7 +182,7 @@ class HomeworkCommonsController < ApplicationController # 作品状态 0: 未提交, 1 按时提交, 2 延迟提交 if params[:work_status].present? params_work_status = params[:work_status] - work_status = params_work_status.map{|status| status.to_i} + work_status = params_work_status.map {|status| status.to_i} if @homework.homework_type == "practice" @student_works = @student_works.where(compelete_status: work_status) else @@ -225,17 +241,17 @@ class HomeworkCommonsController < ApplicationController if @user_course_identity >= Course::STUDENT tip_exception(403, "无权限操作") elsif @work_excel.blank? || @work_excel.size == 0 - normal_status(-1,"暂无用户提交!") + normal_status(-1, "暂无用户提交!") elsif params[:export].present? && params[:export] - normal_status(0,"正在下载中") + normal_status(0, "正在下载中") else respond_to do |format| - format.xlsx{ + format.xlsx { set_export_cookies - student_work_to_xlsx(@work_excel,@homework) + student_work_to_xlsx(@work_excel, @homework) exercise_export_name = "#{current_user.real_name}_#{@course.name}_#{@homework.name}_#{Time.now.strftime('%Y%m%d_%H%M%S')}" - render xlsx: "#{exercise_export_name.strip}",template: "homework_commons/works_list.xlsx.axlsx",locals: - {table_columns: @work_head_cells,task_users: @work_cells_column} + render xlsx: "#{exercise_export_name.strip}", template: "homework_commons/works_list.xlsx.axlsx", locals: + {table_columns: @work_head_cells, task_users: @work_cells_column} } end end @@ -252,10 +268,10 @@ class HomeworkCommonsController < ApplicationController if status == 0 if params[:export].present? && params[:export] - normal_status(0,"正在下载中") + normal_status(0, "正在下载中") else respond_to do |format| - format.zip{ + format.zip { set_export_cookies zipfile = zip_homework_common @homework, zip_works file = decode64(zipfile[0][:base64file]) @@ -273,33 +289,21 @@ class HomeworkCommonsController < ApplicationController def update_score tip_exception("作业还未发布,暂不能计算成绩") if @homework.publish_time.nil? || @homework.publish_time > Time.now - begin - @homework.update_homework_work_score - normal_status("更新成功") - rescue Exception => e - uid_logger(e.message) - tip_exception(e.message) - raise ActiveRecord::Rollback - end + @homework.update_homework_work_score + normal_status("更新成功") end def update_student_score work = @homework.student_works.find_by(user_id: current_user.id) myshixun = Myshixun.find_by(shixun_id: params[:shixun_id], user_id: current_user.id) ActiveRecord::Base.transaction do - begin - if work && myshixun - challenge_settings = @homework.homework_challenge_settings - games = myshixun.games.where(challenge_id: challenge_settings.pluck(:challenge_id)) - HomeworksService.new.update_myshixun_work_score work, myshixun, games, @homework, challenge_settings - normal_status("更新成功") - else - normal_status("还未开启挑战,暂不能更新成绩") - end - rescue Exception => e - uid_logger(e.message) - tip_exception(e.message) - raise ActiveRecord::Rollback + if work && myshixun + challenge_settings = @homework.homework_challenge_settings + games = myshixun.games.where(challenge_id: challenge_settings.pluck(:challenge_id)) + HomeworksService.new.update_myshixun_work_score work, myshixun, games, @homework, challenge_settings + normal_status("更新成功") + else + normal_status("还未开启挑战,暂不能更新成绩") end end end @@ -355,6 +359,9 @@ class HomeworkCommonsController < ApplicationController def new tip_exception("type参数有误") if params[:type].blank? || ![1, 3].include?(params[:type].to_i) @homework_type = params[:type].to_i + module_type = params[:type].to_i == 1 ? "common_homework" : "group_homework" + @main_category = @course.course_modules.find_by(module_type: module_type) + @category = @main_category.course_second_categories.find_by(id: params[:category]) if params[:category].present? end def create @@ -366,47 +373,51 @@ class HomeworkCommonsController < ApplicationController end ActiveRecord::Base.transaction do - begin - @homework = HomeworkCommon.new(homework_params) - @homework.reference_answer = params[:reference_answer].to_s.strip - @homework.homework_type = @homework_type - @homework.user_id = current_user.id - @homework.course_id = @course.id - - homework_detail_manual = HomeworkDetailManual.new - @homework.homework_detail_manual = homework_detail_manual - homework_detail_manual.te_proportion = 0.7 - homework_detail_manual.ta_proportion = 0.3 - - if @homework_type == 3 - homework_detail_group = HomeworkDetailGroup.new(min_num: params[:min_num].to_i, max_num: params[:max_num].to_i, - base_on_project: params[:base_on_project]) - @homework.homework_detail_group = homework_detail_group - end + @homework = HomeworkCommon.new(homework_params) + @homework.reference_answer = params[:reference_answer].to_s.strip + @homework.homework_type = @homework_type + @homework.user_id = current_user.id + @homework.course_id = @course.id + + if params[:category].present? + category = @course.course_second_categories.find_by(id: params[:category]) + @homework.course_second_category_id = category&.id.to_i + end - if @homework.save! - homework_detail_manual.save! if homework_detail_manual - homework_detail_group.save! if homework_detail_group + homework_detail_manual = HomeworkDetailManual.new + @homework.homework_detail_manual = homework_detail_manual + homework_detail_manual.te_proportion = 0.7 + homework_detail_manual.ta_proportion = 0.3 - # 作业描述的附件 - Attachment.associate_container(params[:attachment_ids], @homework.id, @homework.class) if params[:attachment_ids] - # 作业参考答案的附件 - Attachment.associate_container(params[:reference_attachment_ids], @homework.id, @homework.class, 2) if params[:reference_attachment_ids] + if @homework_type == 3 + homework_detail_group = HomeworkDetailGroup.new(min_num: params[:min_num].to_i, max_num: params[:max_num].to_i, + base_on_project: params[:base_on_project]) + @homework.homework_detail_group = homework_detail_group + end - HomeworksService.new.create_works_list(@homework, @course) - else - tip_exception("创建失败") - end - rescue Exception => e - uid_logger(e.message) - tip_exception(e.message) - raise ActiveRecord::Rollback + if @homework.save! + homework_detail_manual.save! if homework_detail_manual + homework_detail_group.save! if homework_detail_group + + # 作业描述的附件 + Attachment.associate_container(params[:attachment_ids], @homework.id, @homework.class) if params[:attachment_ids] + # 作业参考答案的附件 + Attachment.associate_container(params[:reference_attachment_ids], @homework.id, @homework.class, 2) if params[:reference_attachment_ids] + + HomeworksService.new.create_works_list(@homework, @course) + else + tip_exception("创建失败") end end end def edit - + if @homework.course_second_category_id == 0 + module_type = @homework.homework_type == "normal" ? "common_homework" : "group_homework" + @main_category = @course.course_modules.find_by(module_type: module_type) + else + @category = @homework.course_second_category + end end def update @@ -416,30 +427,25 @@ class HomeworkCommonsController < ApplicationController end ActiveRecord::Base.transaction do - begin - @homework.update_attributes!(homework_params) - @homework.reference_answer = params[:reference_answer].to_s.strip - - if @homework.homework_type == "group" - homework_detail_group = @homework.homework_detail_group - param_min = params[:min_num].to_i - param_max = params[:max_num].to_i - homework_detail_group.min_num = @homework.has_commit_work ? [param_min, homework_detail_group.min_num].min : param_min - homework_detail_group.max_num = @homework.has_commit_work ? [param_max, homework_detail_group.max_num].max : param_max - homework_detail_group.base_on_project = params[:base_on_project] unless @homework.has_relate_project - homework_detail_group.save! - end - # 作业描述的附件 - Attachment.associate_container(params[:attachment_ids], @homework.id, @homework.class) if params[:attachment_ids] - # 作业参考答案的附件 - Attachment.associate_container(params[:reference_attachment_ids], @homework.id, @homework.class, 2) if params[:reference_attachment_ids] - - normal_status(0, "更新成功") - rescue Exception => e - uid_logger(e.message) - tip_exception(e.message) - raise ActiveRecord::Rollback + @homework.update_attributes!(homework_params) + @homework.reference_answer = params[:reference_answer].to_s.strip + + if @homework.homework_type == "group" + homework_detail_group = @homework.homework_detail_group + param_min = params[:min_num].to_i + param_max = params[:max_num].to_i + homework_detail_group.min_num = @homework.has_commit_work ? [param_min, homework_detail_group.min_num].min : param_min + homework_detail_group.max_num = @homework.has_commit_work ? [param_max, homework_detail_group.max_num].max : param_max + homework_detail_group.base_on_project = params[:base_on_project] unless @homework.has_relate_project + homework_detail_group.save! end + # 作业描述的附件 + Attachment.associate_container(params[:attachment_ids], @homework.id, @homework.class) if params[:attachment_ids] + # 作业参考答案的附件 + Attachment.associate_container(params[:reference_attachment_ids], @homework.id, @homework.class, 2) if params[:reference_attachment_ids] + + normal_status(0, "更新成功") + end end @@ -451,304 +457,298 @@ class HomeworkCommonsController < ApplicationController end def update_settings - begin - # 课堂结束后不能再更新 - unless @course.is_end - - # 发布设置 - UpdateHomeworkPublishSettingService.call(@homework, publish_params) - - # 补交设置 - tip_exception("缺少allow_late参数") if params[:allow_late].nil? - tip_exception("缺少late_penalty参数") if params[:allow_late] && params[:late_penalty].blank? - tip_exception("缺少late_time参数") if params[:allow_late] && params[:late_time].blank? - - current_late_penalty = @homework.late_penalty - if params[:allow_late] - tip_exception("补交结束时间必须晚于截止时间") if params[:late_time] <= strf_time(@homework.end_time) - tip_exception("补交结束时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && params[:late_time] > - strf_time(@course.end_date.end_of_day) - tip_exception("迟交扣分不能小于0") if params[:late_penalty] && params[:late_penalty].to_i < 0 + # 课堂结束后不能再更新 + unless @course.is_end + + # 发布设置 + UpdateHomeworkPublishSettingService.call(@homework, publish_params) + + # 补交设置 + tip_exception("缺少allow_late参数") if params[:allow_late].nil? + tip_exception("缺少late_penalty参数") if params[:allow_late] && params[:late_penalty].blank? + tip_exception("缺少late_time参数") if params[:allow_late] && params[:late_time].blank? + + current_late_penalty = @homework.late_penalty + if params[:allow_late] + tip_exception("补交结束时间必须晚于截止时间") if params[:late_time] <= strf_time(@homework.end_time) + tip_exception("补交结束时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && params[:late_time] > + strf_time(@course.end_date.end_of_day) + tip_exception("迟交扣分不能小于0") if params[:late_penalty] && params[:late_penalty].to_i < 0 + + @homework.allow_late = true + @homework.late_time = params[:late_time] + @homework.late_penalty = params[:late_penalty].to_i + else + @homework.allow_late = false + @homework.late_penalty = 0 + @homework.late_time = nil + end - @homework.allow_late = true - @homework.late_time = params[:late_time] - @homework.late_penalty = params[:late_penalty].to_i - else - @homework.allow_late = false - @homework.late_penalty = 0 - @homework.late_time = nil + # 迟交扣分有变动则更新迟交学生的成绩 + late_penalty_change = @homework.late_penalty != current_late_penalty + + if @homework.homework_type == "practice" + + # 实训作业的评分设置 + tip_exception("总分值不能为空") if params[:total_score].blank? + tip_exception("总分值不能小于0") if params[:total_score].to_i < 0 + tip_exception("缺少answer_open_evaluation参数") if params[:answer_open_evaluation].nil? + tip_exception("缺少work_efficiency参数") if params[:work_efficiency].nil? + tip_exception("缺少eff_score参数") if params[:work_efficiency] && params[:eff_score].blank? + tip_exception("效率分不能小于等于0") if params[:work_efficiency] && params[:eff_score] && params[:eff_score].to_f <= 0 + tip_exception("效率分不能大于总分值") if params[:work_efficiency] && params[:eff_score] && params[:eff_score].to_f.round(2) > params[:total_score].to_f.round(2) + tip_exception("缺少shixun_evaluation参数") if params[:shixun_evaluation].blank? + tip_exception("缺少challenge_settings参数") if params[:challenge_settings].blank? + # tip_exception("缺少challenge_id参数") if params[:challenge_settings][:challenge_id].blank? + # tip_exception("缺少challenge_score参数") if params[:challenge_settings][:challenge_score].blank? + # tip_exception("challenge_id参数的长度与challenge_score参数的长度不匹配") if + # params[:challenge_settings][:challenge_score].length != params[:challenge_settings][:challenge_id].length + + sum_challenge_score = params[:challenge_settings].pluck(:challenge_score).reject(&:blank?)&.map {|score| score.to_f}.sum + total_score = params[:work_efficiency] ? (params[:eff_score].to_f + sum_challenge_score) : sum_challenge_score + tip_exception("分值之和必须等于总分值:#{params[:total_score]}分") if params[:total_score].to_f.round(2) != total_score.to_f.round(2) + + current_eff_score = @homework.eff_score.to_f.round(2) + @homework.total_score = params[:total_score] + @homework.work_efficiency = params[:work_efficiency] + @homework.eff_score = params[:work_efficiency] ? params[:eff_score].to_f.round(2) : 0 + + update_eff_score = current_eff_score.round(2) != @homework.eff_score.round(2) + + if @homework_detail_manual.answer_open_evaluation != params[:answer_open_evaluation] + @homework_detail_manual.answer_open_evaluation = params[:answer_open_evaluation] + score_change = true end - # 迟交扣分有变动则更新迟交学生的成绩 - late_penalty_change = @homework.late_penalty != current_late_penalty - - if @homework.homework_type == "practice" + @homework_detail_manual.shixun_evaluation = params[:shixun_evaluation].to_i - # 实训作业的评分设置 - tip_exception("总分值不能为空") if params[:total_score].blank? - tip_exception("总分值不能小于0") if params[:total_score].to_i < 0 - tip_exception("缺少answer_open_evaluation参数") if params[:answer_open_evaluation].nil? - tip_exception("缺少work_efficiency参数") if params[:work_efficiency].nil? - tip_exception("缺少eff_score参数") if params[:work_efficiency] && params[:eff_score].blank? - tip_exception("效率分不能小于等于0") if params[:work_efficiency] && params[:eff_score] && params[:eff_score].to_f <= 0 - tip_exception("效率分不能大于总分值") if params[:work_efficiency] && params[:eff_score] && params[:eff_score].to_f.round(2) > params[:total_score].to_f.round(2) - tip_exception("缺少shixun_evaluation参数") if params[:shixun_evaluation].blank? - tip_exception("缺少challenge_settings参数") if params[:challenge_settings].blank? - # tip_exception("缺少challenge_id参数") if params[:challenge_settings][:challenge_id].blank? - # tip_exception("缺少challenge_score参数") if params[:challenge_settings][:challenge_score].blank? - # tip_exception("challenge_id参数的长度与challenge_score参数的长度不匹配") if - # params[:challenge_settings][:challenge_score].length != params[:challenge_settings][:challenge_id].length - - sum_challenge_score = params[:challenge_settings].pluck(:challenge_score).reject(&:blank?)&.map{|score| score.to_f}.sum - total_score = params[:work_efficiency] ? (params[:eff_score].to_f + sum_challenge_score) : sum_challenge_score - tip_exception("分值之和必须等于总分值:#{params[:total_score]}分") if params[:total_score].to_f.round(2) != total_score.to_f.round(2) - - current_eff_score = @homework.eff_score.to_f.round(2) - @homework.total_score = params[:total_score] - @homework.work_efficiency = params[:work_efficiency] - @homework.eff_score = params[:work_efficiency] ? params[:eff_score].to_f.round(2) : 0 - - update_eff_score = current_eff_score.round(2) != @homework.eff_score.round(2) - - if @homework_detail_manual.answer_open_evaluation != params[:answer_open_evaluation] - @homework_detail_manual.answer_open_evaluation = params[:answer_open_evaluation] - score_change = true - end - - @homework_detail_manual.shixun_evaluation = params[:shixun_evaluation].to_i - - if params[:challenge_settings] - params[:challenge_settings].each do |challenge| - setting = @homework.homework_challenge_settings.find_by(challenge_id: challenge[:challenge_id]) - score = challenge[:challenge_score] - if setting && setting.score != score - score_change = true - setting.update_attributes!(score: score) - elsif setting.blank? - score_change = true - HomeworkChallengeSetting.create!(homework_common_id: @homework.id, challenge_id: challenge[:challenge_id], - shixun_id: @homework.homework_commons_shixun.try(:shixun_id), score: score) - end - end - - if @homework.homework_challenge_settings.where.not(challenge_id: params[:challenge_settings].pluck(:challenge_id)).count > 0 + if params[:challenge_settings] + params[:challenge_settings].each do |challenge| + setting = @homework.homework_challenge_settings.find_by(challenge_id: challenge[:challenge_id]) + score = challenge[:challenge_score] + if setting && setting.score != score + score_change = true + setting.update_attributes!(score: score) + elsif setting.blank? score_change = true - @homework.homework_challenge_settings.where.not(challenge_id: params[:challenge_settings].pluck(:challenge_id)).destroy_all + HomeworkChallengeSetting.create!(homework_common_id: @homework.id, challenge_id: challenge[:challenge_id], + shixun_id: @homework.homework_commons_shixun.try(:shixun_id), score: score) end end - # 公开设置 - tip_exception("缺少score_open参数") if params[:score_open].nil? - @homework.score_open = params[:score_open] - - @homework.save! - if score_change && @homework.end_or_late_none_group - UpdateShixunWorkScoreJob.perform_later(@homework.id) - elsif update_eff_score && (@homework.end_or_late_none_group || @homework.max_efficiency > 0) - # 更新所有学生的效率分(作业允许补交且补交已截止 或者 作业不允许补交且提交已截止 或者作业已计算过效率分) - HomeworksService.new.update_student_eff_score HomeworkCommon.find_by(id: @homework.id) + if @homework.homework_challenge_settings.where.not(challenge_id: params[:challenge_settings].pluck(:challenge_id)).count > 0 + score_change = true + @homework.homework_challenge_settings.where.not(challenge_id: params[:challenge_settings].pluck(:challenge_id)).destroy_all end + end - # 更新迟交扣分 - if !(score_change || update_eff_score) && late_penalty_change - @homework.student_works.where(work_status: 2).each do |work| - work.late_penalty = @homework.late_penalty - work.save! - end - end + # 公开设置 + tip_exception("缺少score_open参数") if params[:score_open].nil? + @homework.score_open = params[:score_open] - unless @homework.allow_late - @homework.student_works.where(work_status: 2).update_all(work_status: 1) - end + @homework.save! + if score_change && @homework.end_or_late_none_group + UpdateShixunWorkScoreJob.perform_later(@homework.id) + elsif update_eff_score && (@homework.end_or_late_none_group || @homework.max_efficiency > 0) + # 更新所有学生的效率分(作业允许补交且补交已截止 或者 作业不允许补交且提交已截止 或者作业已计算过效率分) + HomeworksService.new.update_student_eff_score HomeworkCommon.find_by(id: @homework.id) + end - @homework_detail_manual.save! - @homework.save! - else + # 更新迟交扣分 + if !(score_change || update_eff_score) && late_penalty_change + @homework.student_works.where(work_status: 2).each do |work| + work.late_penalty = @homework.late_penalty + work.save! + end + end - # 普通和分组作业的匿评设置 - current_absence_penalty = @homework_detail_manual.absence_penalty - current_appeal_penalty = @homework_detail_manual.appeal_penalty - - # 匿评未开启前可以更新:是否开启匿评、匿评开始时间、匿评数 - if @homework_detail_manual.comment_status < 3 - tip_exception("缺少anonymous_comment参数") if params[:anonymous_comment].nil? - # anonymous_comment :true 是启用,false 是不启用 - if params[:anonymous_comment] - tip_exception("匿评开启时间不能为空") if params[:evaluation_start].blank? - tip_exception("匿评开启时间不能早于截止时间") if params[:evaluation_start] < strf_time(@homework.end_time) - tip_exception("匿评结束时间不能为空") if params[:evaluation_end].blank? - tip_exception("匿评截止时间必须晚于匿评开启时间") if params[:evaluation_end] <= params[:evaluation_start] - tip_exception("匿评截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && params[:evaluation_end] > - strf_time(@course.end_date.end_of_day) - tip_exception("匿评数必须为正整数") if params[:evaluation_num].blank? || params[:evaluation_num].to_i < 1 - tip_exception("缺评扣分不能为空") if params[:absence_penalty].blank? - tip_exception("缺评扣分不能小于0") if params[:absence_penalty].to_i < 0 - tip_exception("缺评扣分不能大于100") if params[:absence_penalty].to_i > 100 - end + unless @homework.allow_late + @homework.student_works.where(work_status: 2).update_all(work_status: 1) + end - @homework.anonymous_comment = params[:anonymous_comment] - @homework_detail_manual.evaluation_start = !@homework.anonymous_comment ? nil : params[:evaluation_start] - @homework_detail_manual.evaluation_num = !@homework.anonymous_comment ? 0 : params[:evaluation_num] - - # 不启用匿评时还原申诉设置和教师、助教的评分比例 - unless @homework.anonymous_comment - @homework.anonymous_appeal = false - @homework_detail_manual.appeal_time = nil - @homework_detail_manual.appeal_penalty = 0 - @homework_detail_manual.te_proportion = 1 - @homework_detail_manual.ta_proportion = 0 - end + @homework_detail_manual.save! + @homework.save! + else + # 普通和分组作业的匿评设置 + current_absence_penalty = @homework_detail_manual.absence_penalty + current_appeal_penalty = @homework_detail_manual.appeal_penalty + + # 匿评未开启前可以更新:是否开启匿评、匿评开始时间、匿评数 + if @homework_detail_manual.comment_status < 3 + tip_exception("缺少anonymous_comment参数") if params[:anonymous_comment].nil? + # anonymous_comment :true 是启用,false 是不启用 + if params[:anonymous_comment] + tip_exception("匿评开启时间不能为空") if params[:evaluation_start].blank? + tip_exception("匿评开启时间不能早于截止时间") if params[:evaluation_start] < strf_time(@homework.end_time) + tip_exception("匿评结束时间不能为空") if params[:evaluation_end].blank? + tip_exception("匿评截止时间必须晚于匿评开启时间") if params[:evaluation_end] <= params[:evaluation_start] + tip_exception("匿评截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && params[:evaluation_end] > + strf_time(@course.end_date.end_of_day) + tip_exception("匿评数必须为正整数") if params[:evaluation_num].blank? || params[:evaluation_num].to_i < 1 + tip_exception("缺评扣分不能为空") if params[:absence_penalty].blank? + tip_exception("缺评扣分不能小于0") if params[:absence_penalty].to_i < 0 + tip_exception("缺评扣分不能大于100") if params[:absence_penalty].to_i > 100 end - # 匿评未截止时可以更新匿评结束时间 - if @homework_detail_manual.comment_status < 4 - tip_exception("匿评结束时间不能为空") if @homework.anonymous_comment && params[:evaluation_end].blank? - tip_exception("匿评截止时间必须晚于匿评开启时间") if @homework.anonymous_comment && - params[:evaluation_end] <= strf_time(@homework_detail_manual.evaluation_start) - tip_exception("匿评截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @homework.anonymous_comment && - @course.end_date.present? && params[:evaluation_end] > strf_time(@course.end_date.end_of_day) + @homework.anonymous_comment = params[:anonymous_comment] + @homework_detail_manual.evaluation_start = !@homework.anonymous_comment ? nil : params[:evaluation_start] + @homework_detail_manual.evaluation_num = !@homework.anonymous_comment ? 0 : params[:evaluation_num] - @homework_detail_manual.evaluation_end = !@homework.anonymous_comment ? nil : params[:evaluation_end] + # 不启用匿评时还原申诉设置和教师、助教的评分比例 + unless @homework.anonymous_comment + @homework.anonymous_appeal = false + @homework_detail_manual.appeal_time = nil + @homework_detail_manual.appeal_penalty = 0 + @homework_detail_manual.te_proportion = 1 + @homework_detail_manual.ta_proportion = 0 end - # 作业未结束可以更新缺评扣分 - tip_exception("缺评扣分不能为空") if @homework.anonymous_comment && params[:absence_penalty].blank? - tip_exception("缺评扣分不能小于0") if @homework.anonymous_comment && params[:absence_penalty].to_i < 0 - tip_exception("缺评扣分不能大于100") if @homework.anonymous_comment && params[:absence_penalty].to_i > 100 - @homework_detail_manual.absence_penalty = !@homework.anonymous_comment ? 0 : params[:absence_penalty].to_i + end + # 匿评未截止时可以更新匿评结束时间 + if @homework_detail_manual.comment_status < 4 + tip_exception("匿评结束时间不能为空") if @homework.anonymous_comment && params[:evaluation_end].blank? + tip_exception("匿评截止时间必须晚于匿评开启时间") if @homework.anonymous_comment && + params[:evaluation_end] <= strf_time(@homework_detail_manual.evaluation_start) + tip_exception("匿评截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @homework.anonymous_comment && + @course.end_date.present? && params[:evaluation_end] > strf_time(@course.end_date.end_of_day) - # 匿评申诉设置 - # 匿评申诉未开启前可以更新:是否启用匿评申诉 - if @homework_detail_manual.comment_status < 4 && @homework.anonymous_comment - tip_exception("缺少anonymous_appeal参数") if params[:anonymous_appeal].nil? - @homework.anonymous_appeal = params[:anonymous_appeal] - end + @homework_detail_manual.evaluation_end = !@homework.anonymous_comment ? nil : params[:evaluation_end] + end + # 作业未结束可以更新缺评扣分 + tip_exception("缺评扣分不能为空") if @homework.anonymous_comment && params[:absence_penalty].blank? + tip_exception("缺评扣分不能小于0") if @homework.anonymous_comment && params[:absence_penalty].to_i < 0 + tip_exception("缺评扣分不能大于100") if @homework.anonymous_comment && params[:absence_penalty].to_i > 100 + @homework_detail_manual.absence_penalty = !@homework.anonymous_comment ? 0 : params[:absence_penalty].to_i - # 匿评申诉未结束前可以更新:匿评申诉结束时间 - if @homework_detail_manual.comment_status < 5 - tip_exception("匿评申诉结束时间不能为空") if @homework.anonymous_appeal && params[:appeal_time].blank? - tip_exception("匿评开启时间不能早于匿评截止时间") if @homework.anonymous_appeal && - params[:appeal_time] <= strf_time(@homework_detail_manual.evaluation_end) - tip_exception("匿评申诉结束不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @homework.anonymous_appeal && - @course.end_date.present? && params[:appeal_time] > strf_time(@course.end_date.end_of_day) - @homework_detail_manual.appeal_time = @homework.anonymous_appeal ? params[:appeal_time] : nil - end + # 匿评申诉设置 + # 匿评申诉未开启前可以更新:是否启用匿评申诉 + if @homework_detail_manual.comment_status < 4 && @homework.anonymous_comment + tip_exception("缺少anonymous_appeal参数") if params[:anonymous_appeal].nil? + @homework.anonymous_appeal = params[:anonymous_appeal] + end - # 作业未结束可以更新违规匿评扣分 - tip_exception("违规匿评扣分不能为空") if @homework.anonymous_appeal && params[:appeal_penalty].blank? - tip_exception("违规匿评扣分不能小于0") if @homework.anonymous_appeal && params[:appeal_penalty].to_i < 0 - tip_exception("违规匿评扣分不能大于100") if @homework.anonymous_appeal && params[:appeal_penalty].to_i > 100 - @homework_detail_manual.appeal_penalty = @homework.anonymous_appeal ? params[:appeal_penalty].to_i : 0 - - # 如果缺评扣分的设置有变更且匿评已截止 - absence_penalty_change = current_absence_penalty != @homework_detail_manual.absence_penalty && - @homework_detail_manual.comment_status >= 4 - # 如果违规匿评扣分的设置有变更且匿评已截止 - appeal_penalty_change = current_appeal_penalty != @homework_detail_manual.appeal_penalty && - @homework_detail_manual.comment_status >= 4 - - # 评分设置 - tip_exception("助教评分模式不能为空") if params[:ta_mode].blank? - - # 助教评分模式的变更 - ta_mode_change = @homework_detail_manual.ta_mode != params[:ta_mode].to_i - @homework_detail_manual.ta_mode = params[:ta_mode].to_i - - # 最终成绩组成 - tip_exception("最终成绩组成模式不能为空") if params[:final_mode].nil? - - final_mode_change = @homework_detail_manual.final_mode != params[:final_mode] - @homework_detail_manual.final_mode = params[:final_mode] - if !@homework_detail_manual.final_mode - tip_exception("教师评分比例不能为空") if params[:te_proportion].blank? - te_proportion = params[:te_proportion].to_f.round(2) - tip_exception("教师评分比例不能小于0") if te_proportion < 0 - tip_exception("助教评分比例不能为空") if params[:ta_proportion].blank? - ta_proportion = params[:ta_proportion].to_f.round(2) - tip_exception("助教评分比例不能小于0") if ta_proportion < 0 - if !@homework.anonymous_comment - tip_exception("评分比例之和不能大于100") if (te_proportion + ta_proportion) > 1.0 - else - tip_exception("学生评分比例不能为空") if params[:st_proportion].blank? - st_proportion = params[:st_proportion].to_f.round(2) - tip_exception("学生评分比例不能小于0") if st_proportion < 0 - tip_exception("评分比例之和不能大于100") if (te_proportion + ta_proportion + st_proportion) > 1.0 - end - proportion_change = @homework_detail_manual.te_proportion.round(2) != te_proportion || - @homework_detail_manual.ta_proportion.round(2) != ta_proportion - @homework_detail_manual.te_proportion = te_proportion - @homework_detail_manual.ta_proportion = ta_proportion + # 匿评申诉未结束前可以更新:匿评申诉结束时间 + if @homework_detail_manual.comment_status < 5 + tip_exception("匿评申诉结束时间不能为空") if @homework.anonymous_appeal && params[:appeal_time].blank? + tip_exception("匿评开启时间不能早于匿评截止时间") if @homework.anonymous_appeal && + params[:appeal_time] <= strf_time(@homework_detail_manual.evaluation_end) + tip_exception("匿评申诉结束不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @homework.anonymous_appeal && + @course.end_date.present? && params[:appeal_time] > strf_time(@course.end_date.end_of_day) + + @homework_detail_manual.appeal_time = @homework.anonymous_appeal ? params[:appeal_time] : nil + end + + # 作业未结束可以更新违规匿评扣分 + tip_exception("违规匿评扣分不能为空") if @homework.anonymous_appeal && params[:appeal_penalty].blank? + tip_exception("违规匿评扣分不能小于0") if @homework.anonymous_appeal && params[:appeal_penalty].to_i < 0 + tip_exception("违规匿评扣分不能大于100") if @homework.anonymous_appeal && params[:appeal_penalty].to_i > 100 + @homework_detail_manual.appeal_penalty = @homework.anonymous_appeal ? params[:appeal_penalty].to_i : 0 + + # 如果缺评扣分的设置有变更且匿评已截止 + absence_penalty_change = current_absence_penalty != @homework_detail_manual.absence_penalty && + @homework_detail_manual.comment_status >= 4 + # 如果违规匿评扣分的设置有变更且匿评已截止 + appeal_penalty_change = current_appeal_penalty != @homework_detail_manual.appeal_penalty && + @homework_detail_manual.comment_status >= 4 + + # 评分设置 + tip_exception("助教评分模式不能为空") if params[:ta_mode].blank? + + # 助教评分模式的变更 + ta_mode_change = @homework_detail_manual.ta_mode != params[:ta_mode].to_i + @homework_detail_manual.ta_mode = params[:ta_mode].to_i + + # 最终成绩组成 + tip_exception("最终成绩组成模式不能为空") if params[:final_mode].nil? + + final_mode_change = @homework_detail_manual.final_mode != params[:final_mode] + @homework_detail_manual.final_mode = params[:final_mode] + if !@homework_detail_manual.final_mode + tip_exception("教师评分比例不能为空") if params[:te_proportion].blank? + te_proportion = params[:te_proportion].to_f.round(2) + tip_exception("教师评分比例不能小于0") if te_proportion < 0 + tip_exception("助教评分比例不能为空") if params[:ta_proportion].blank? + ta_proportion = params[:ta_proportion].to_f.round(2) + tip_exception("助教评分比例不能小于0") if ta_proportion < 0 + if !@homework.anonymous_comment + tip_exception("评分比例之和不能大于100") if (te_proportion + ta_proportion) > 1.0 else - @homework_detail_manual.te_proportion = 1 - @homework_detail_manual.ta_proportion = 0 + tip_exception("学生评分比例不能为空") if params[:st_proportion].blank? + st_proportion = params[:st_proportion].to_f.round(2) + tip_exception("学生评分比例不能小于0") if st_proportion < 0 + tip_exception("评分比例之和不能大于100") if (te_proportion + ta_proportion + st_proportion) > 1.0 end - # 公开属性设置 - tip_exception("缺少work_public参数") if params[:work_public].nil? - tip_exception("缺少score_open参数") if params[:score_open].nil? - tip_exception("缺少answer_public参数") if params[:answer_public].nil? - @homework.work_public = params[:work_public] - @homework.score_open = params[:score_open] - @homework.answer_public = params[:answer_public] - - @homework_detail_manual.save! - @homework.save! - - # 迟交扣分、缺评扣分、违规匿评扣分、助教评分模式变更、最终成绩组成、评分比例变更都需要更新学生成绩 - if late_penalty_change || absence_penalty_change || appeal_penalty_change || ta_mode_change || - final_mode_change || proportion_change - - student_works = @homework.student_works.has_committed - work_ids = student_works.pluck(:id) - - student_works.each do |student_work| - # 迟交扣分 - student_work.late_penalty = student_work.work_status == 1 ? 0 : @homework.late_penalty - - # 缺评扣分的更新 如果之前的作业缺评扣分为0,则需重新计算缺评次数 - if absence_penalty_change - absence_penalty_count = current_absence_penalty == 0 ? student_work.absence_count : - (student_work.absence_penalty / current_absence_penalty).to_i - student_work.absence_penalty = absence_penalty_count * @homework_detail_manual.absence_penalty - end + proportion_change = @homework_detail_manual.te_proportion.round(2) != te_proportion || + @homework_detail_manual.ta_proportion.round(2) != ta_proportion + @homework_detail_manual.te_proportion = te_proportion + @homework_detail_manual.ta_proportion = ta_proportion + else + @homework_detail_manual.te_proportion = 1 + @homework_detail_manual.ta_proportion = 0 + end - # 违规匿评扣分 如果之前的作业违规扣分为0,则需重新计算违规匿评次数 - if appeal_penalty_change - appeal_penalty_count = current_appeal_penalty == 0 ? student_work.appeal_count : - (student_work.appeal_penalty / current_appeal_penalty).to_i - student_work.appeal_penalty = appeal_penalty_count * @homework_detail_manual.appeal_penalty - end + # 公开属性设置 + tip_exception("缺少work_public参数") if params[:work_public].nil? + tip_exception("缺少score_open参数") if params[:score_open].nil? + tip_exception("缺少answer_public参数") if params[:answer_public].nil? + @homework.work_public = params[:work_public] + @homework.score_open = params[:score_open] + @homework.answer_public = params[:answer_public] + + @homework_detail_manual.save! + @homework.save! + + # 迟交扣分、缺评扣分、违规匿评扣分、助教评分模式变更、最终成绩组成、评分比例变更都需要更新学生成绩 + if late_penalty_change || absence_penalty_change || appeal_penalty_change || ta_mode_change || + final_mode_change || proportion_change + + student_works = @homework.student_works.has_committed + work_ids = student_works.pluck(:id) + + student_works.each do |student_work| + # 迟交扣分 + student_work.late_penalty = student_work.work_status == 1 ? 0 : @homework.late_penalty + + # 缺评扣分的更新 如果之前的作业缺评扣分为0,则需重新计算缺评次数 + if absence_penalty_change + absence_penalty_count = current_absence_penalty == 0 ? student_work.absence_count : + (student_work.absence_penalty / current_absence_penalty).to_i + student_work.absence_penalty = absence_penalty_count * @homework_detail_manual.absence_penalty + end - # 助教模式变更且有助教评分记录时才更新 - if ta_mode_change && student_work.student_works_scores.where("reviewer_role = 2 AND score IS NOT NULL").count > 0 - student_work.teaching_asistant_score = student_work.ta_score @homework_detail_manual.ta_mode - end + # 违规匿评扣分 如果之前的作业违规扣分为0,则需重新计算违规匿评次数 + if appeal_penalty_change + appeal_penalty_count = current_appeal_penalty == 0 ? student_work.appeal_count : + (student_work.appeal_penalty / current_appeal_penalty).to_i + student_work.appeal_penalty = appeal_penalty_count * @homework_detail_manual.appeal_penalty + end - student_work.save! + # 助教模式变更且有助教评分记录时才更新 + if ta_mode_change && student_work.student_works_scores.where("reviewer_role = 2 AND score IS NOT NULL").count > 0 + student_work.teaching_asistant_score = student_work.ta_score @homework_detail_manual.ta_mode end - end + student_work.save! + end end - normal_status(0, "更新成功") - else - tip_exception("课堂已结束不能再更新") + end + normal_status(0, "更新成功") + else + tip_exception("课堂已结束不能再更新") + end # rescue ActiveRecord::RecordInvalid # render_error("保存失败") # rescue ApplicationService::Error => ex # uid_logger(ex.message) # render_error(ex.message) # raise ActiveRecord::Rollback - rescue Exception => e - uid_logger(e.backtrace) - tip_exception(e.message) - raise ActiveRecord::Rollback - end end # 选用实训 @@ -783,7 +783,7 @@ class HomeworkCommonsController < ApplicationController @total_count = @shixuns.size ## 分页参数 - page = params[:page] || 1 + page = params[:page] || 1 @shixuns = @shixuns.reorder("shixuns.created_at desc").includes(:challenges, user: [user_extension: :school]).page(page).per(10) end @@ -806,15 +806,15 @@ class HomeworkCommonsController < ApplicationController def subjects @tags = Repertoire.where(nil).order("updated_at desc") # select = params[:select] # 路径导航类型 - select = params[:type] # 路径导航类型 + select = params[:type] # 路径导航类型 reorder = params[:order] || "myshixun_count" sort = params[:sort] || "desc" search = params[:search] ## 分页参数 - page = params[:page] || 1 + page = params[:page] || 1 limit = params[:limit] || 15 - offset = (page.to_i-1) * limit + offset = (page.to_i - 1) * limit # 最热排序 if reorder == "myshixun_count" @@ -887,8 +887,8 @@ class HomeworkCommonsController < ApplicationController # 为实训作业创建与stage同名的子目录 category = CourseSecondCategory.find_by(name: stage.name, course_id: @course.id, category_type: "shixun_homework") || - CourseSecondCategory.create!(name: stage.name, course_id: @course.id, category_type: "shixun_homework", - course_module_id: course_module.id, position: course_module.course_second_categories.count + 1) + CourseSecondCategory.create!(name: stage.name, course_id: @course.id, category_type: "shixun_homework", + course_module_id: course_module.id, position: course_module.course_second_categories.count + 1) # 去掉不对当前用户的单位公开的实训,已发布的实训 stage.shixuns.no_jupyter.where(id: shixun_ids).unhidden.each do |shixun| @@ -902,7 +902,8 @@ class HomeworkCommonsController < ApplicationController def publish_groups @current_user = current_user - if @homework.publish_immediately @current_user + charge_ids = @course.charge_group_ids(@current_user) + if @homework.publish_immediately charge_ids # 可立即发布的分班:当前用户管理的分班去除已发布的分班 group_ids = @course.charge_group_ids(@current_user) - @homework.homework_group_settings.group_published.pluck(:course_group_id) @course_groups = @course.course_groups.where(id: group_ids) @@ -918,16 +919,14 @@ class HomeworkCommonsController < ApplicationController if params[:detail].blank? tip_exception("缺少截止时间参数") if params[:end_time].blank? tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now) - tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) + tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day) else tip_exception("缺少分班截止时间参数") if params[:group_end_times].blank? - group_end_times = params[:group_end_times].reject(&:blank?)&.map{|time| time.to_time} + group_end_times = params[:group_end_times].reject(&:blank?)&.map {|time| time.to_time} tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length group_end_times.each do |time| tip_exception("分班截止时间不能早于当前时间") if time <= Time.now - tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if - @course.end_date.present? && time > @course.end_date.end_of_day + tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")})") if @course.end_date.present? && time > @course.end_date.end_of_day end end @@ -938,104 +937,100 @@ class HomeworkCommonsController < ApplicationController publish_groups = charge_group_ids & group_ids if group_ids # ActiveRecord::Base.transaction do - begin - homeworks.each do |homework| - # 作业未发布时 - if homework.homework_detail_manual.try(:comment_status) == 0 - if !params[:group_ids].blank? - - # 全选即统一设置,unified_setting为true - if @course.course_groups.where(id: publish_groups).size == @course.course_groups.size && - ((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) - homework.homework_group_settings.destroy_all - homework.unified_setting = true - homework.end_time = params[:detail] ? group_end_times.max : params[:end_time] - else - homework.unified_setting = false - # 创建作业分班设置:homework_group_setting - homework.create_homework_group_settings - - # 选中的分班设置的发布时间改为当前时间,截止时间改为传的截止时间参数 - if params[:detail] - group_ids.each_with_index do |group_id, index| - homework.homework_group_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now, - end_time: group_end_times[index]) - end - else - homework.homework_group_settings.where(course_group_id: publish_groups).update_all(publish_time: Time.now, - end_time: params[:end_time]) - end - # 发消息 - tiding_group_ids = publish_groups - end - else - homework.homework_group_settings.destroy_all - # students = @course.students - end - - homework.publish_time = Time.now - - # 截止时间的处理 - if homework.end_time.nil? - homework.end_time = params[:detail] ? group_end_times.max : params[:end_time] - elsif homework.max_group_end_time - homework.end_time = homework.max_group_end_time - end - homework.homework_detail_manual.update_attributes!(comment_status: 1) - - if homework.course_act.blank? - homework.course_act << CourseActivity.new(user_id: homework.user_id, course_id: homework.course_id) - end - # 发消息 - HomeworkCommonPushNotifyJob.perform_later(homework.id, tiding_group_ids) + homeworks.each do |homework| + # 作业未发布时 + if homework.homework_detail_manual.try(:comment_status) == 0 + if !params[:group_ids].blank? + + # 全选即统一设置,unified_setting为true + if @course.course_groups.where(id: publish_groups).size == @course.course_groups.size && + ((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) + homework.homework_group_settings.destroy_all + homework.unified_setting = true + homework.end_time = params[:detail] ? group_end_times.max : params[:end_time] else + homework.unified_setting = false + # 创建作业分班设置:homework_group_setting homework.create_homework_group_settings - none_publish_settings = homework.homework_group_settings.where(course_group_id: publish_groups).none_published + # 选中的分班设置的发布时间改为当前时间,截止时间改为传的截止时间参数 if params[:detail] group_ids.each_with_index do |group_id, index| - none_publish_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now, - end_time: group_end_times[index]) + homework.homework_group_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now, + end_time: group_end_times[index]) end else - none_publish_settings.update_all(publish_time: Time.now, end_time: params[:end_time]) + homework.homework_group_settings.where(course_group_id: publish_groups).update_all(publish_time: Time.now, + end_time: params[:end_time]) end - - if homework.max_group_end_time - homework.end_time = homework.max_group_end_time - end - HomeworkCommonPushNotifyJob.perform_later(homework.id, none_publish_settings.pluck(:course_group_id)) + # 发消息 + tiding_group_ids = publish_groups end + else + homework.homework_group_settings.destroy_all + # students = @course.students + end + homework.publish_time = Time.now - if homework.end_time > Time.now && homework.homework_detail_manual.try(:comment_status) > 1 - homework.homework_detail_manual.update_attributes!(comment_status: 1) - end + # 截止时间的处理 + if homework.end_time.nil? + homework.end_time = params[:detail] ? group_end_times.max : params[:end_time] + elsif homework.max_group_end_time + homework.end_time = homework.max_group_end_time + end + homework.homework_detail_manual.update_attributes!(comment_status: 1) - # 补交结束时间 - homework.late_time = Time.at(homework.end_time.to_i + 30*24*3600) if homework.allow_late && homework.late_time.nil? + if homework.course_act.blank? + CourseActivity.create!(user_id: homework.user_id, course_id: homework.course_id, course_act: homework) + end + # 发消息 + HomeworkCommonPushNotifyJob.perform_later(homework.id, tiding_group_ids) + else + homework.create_homework_group_settings - homework.save! + none_publish_settings = homework.homework_group_settings.where(course_group_id: publish_groups).none_published + if params[:detail] + group_ids.each_with_index do |group_id, index| + none_publish_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now, + end_time: group_end_times[index]) + end + else + none_publish_settings.update_all(publish_time: Time.now, end_time: params[:end_time]) + end - # 更新学生状态及成绩(手动点击计算) - # HomeworkPublishUpdateWorkStatusJob.perform_later(tiding_group_ids, homework.id) + if homework.max_group_end_time + homework.end_time = homework.max_group_end_time end - normal_status(0, "发布成功") - rescue Exception => e - uid_logger(e.message) - tip_exception("发布失败") - raise ActiveRecord::Rollback + HomeworkCommonPushNotifyJob.perform_later(homework.id, none_publish_settings.pluck(:course_group_id)) end + + + if homework.end_time > Time.now && homework.homework_detail_manual.try(:comment_status) > 1 + homework.homework_detail_manual.update_attributes!(comment_status: 1) + end + + # 补交结束时间 + homework.late_time = Time.at(homework.end_time.to_i + 30 * 24 * 3600) if homework.allow_late && homework.late_time.nil? + + homework.save! + + # 更新学生状态及成绩(手动点击计算) + # HomeworkPublishUpdateWorkStatusJob.perform_later(tiding_group_ids, homework.id) + end + normal_status(0, "发布成功") + # end end def end_groups @current_user = current_user - if @homework.end_immediately @current_user + charge_ids = @course.charge_group_ids(@current_user) + if @homework.end_immediately charge_ids # 可立即截止的分班:统一设置则是用户管理的所有分班,否则是当前用户管理的分班中已发布且未截止的 - charge_group_ids = @course.charge_group_ids(@current_user) # 当前用户管理的分班 + charge_group_ids = @course.charge_group_ids(@current_user) # 当前用户管理的分班 group_ids = @homework.unified_setting ? charge_group_ids : - @homework.homework_group_settings.where(course_group_id: charge_group_ids).published_no_end.pluck(:course_group_id) + @homework.homework_group_settings.where(course_group_id: charge_group_ids).published_no_end.pluck(:course_group_id) @course_groups = @course.course_groups.where(id: group_ids) else tip_exception("没有可截止的分班") @@ -1056,56 +1051,50 @@ class HomeworkCommonsController < ApplicationController group_ids = params[:group_ids]&.reject(&:blank?)&.map(&:to_i) end_groups = charge_group_ids & group_ids if group_ids - begin - homeworks.each do |homework| - ActiveRecord::Base.transaction do - homework_detail_manual = homework.homework_detail_manual - - # 分组设置 - if !params[:group_ids].blank? - # 确保之前是统一设置或者有新创建的分班的数据一致性 - homework.create_homework_group_settings + homeworks.each do |homework| + ActiveRecord::Base.transaction do + homework_detail_manual = homework.homework_detail_manual - homework.unified_setting = false if homework.unified_setting && end_groups.length != @course.course_groups_count + # 分组设置 + if !params[:group_ids].blank? + # 确保之前是统一设置或者有新创建的分班的数据一致性 + homework.create_homework_group_settings - # 已发布且未截止的分班 - none_end_settings = homework.homework_group_settings.where(course_group_id: end_groups).published_no_end + homework.unified_setting = false if homework.unified_setting && end_groups.length != @course.course_groups_count - none_end_settings.update_all(end_time: time) - student_works = homework.student_works.where(user_id: course_students.where(course_group_id: none_end_settings. - pluck(:course_group_id)).pluck(:user_id)).has_committed if homework.homework_type == "practice" + # 已发布且未截止的分班 + none_end_settings = homework.homework_group_settings.where(course_group_id: end_groups).published_no_end - homework.end_time = homework.max_group_end_time - if homework.end_time > time && homework_detail_manual.try(:comment_status) > 1 - homework_detail_manual.update_attributes!(comment_status: 1) - end + none_end_settings.update_all(end_time: time) + student_works = homework.student_works.where(user_id: course_students.where(course_group_id: none_end_settings. + pluck(:course_group_id)).pluck(:user_id)).has_committed if homework.homework_type == "practice" - # 统一设置 - elsif homework.unified_setting - student_works = homework.student_works.has_committed if homework.homework_type == "practice" - homework.end_time = time + homework.end_time = homework.max_group_end_time + if homework.end_time > time && homework_detail_manual.try(:comment_status) > 1 + homework_detail_manual.update_attributes!(comment_status: 1) end - # homework_detail_manual.update_attributes!(comment_status: 2) if homework.end_time <= time + # 统一设置 + elsif homework.unified_setting + student_works = homework.student_works.has_committed if homework.homework_type == "practice" + homework.end_time = time + end - # 实训作业的作品需要计算是否迟交 - if homework.homework_type == "practice" - # shixun = homework.shixuns.first - # homework_challenge_settings = homework.homework_challenge_settings - unless student_works.blank? - student_works.joins(:myshixun).where("myshixuns.status != 1").update_all(late_penalty: homework.late_penalty) if homework.allow_late - end + # homework_detail_manual.update_attributes!(comment_status: 2) if homework.end_time <= time + + # 实训作业的作品需要计算是否迟交 + if homework.homework_type == "practice" + # shixun = homework.shixuns.first + # homework_challenge_settings = homework.homework_challenge_settings + unless student_works.blank? + student_works.joins(:myshixun).where("myshixuns.status != 1").update_all(late_penalty: homework.late_penalty) if homework.allow_late end - homework.save! end - HomeworkEndUpdateScoreJob.perform_later(homework.id) if !homework.allow_late && homework.end_time <= time + homework.save! end - normal_status(0, "更新成功") - rescue Exception => e - uid_logger(e.message) - tip_exception("操作失败") - raise ActiveRecord::Rollback + HomeworkEndUpdateScoreJob.perform_later(homework.id) if !homework.allow_late && homework.end_time <= time end + normal_status(0, "更新成功") end def set_public @@ -1117,7 +1106,7 @@ class HomeworkCommonsController < ApplicationController def choose_category @main_catrgory = @course.course_modules.where(module_type: "shixun_homework") - @homework_category = @main_catrgory.take.course_second_categories + @homework_category = @main_catrgory.take.course_second_categories.includes(:homework_commons) end # 实训作业移动到目录 @@ -1138,19 +1127,13 @@ class HomeworkCommonsController < ApplicationController # 删除多个作业 def multi_destroy ActiveRecord::Base.transaction do - begin - homeworks = @course.homework_commons.where(id: params[:homework_ids]) - homeworks.destroy_all - - # 这些写是因为model中的关联删除无法删除is_delete=0的作品 - StudentWork.where(homework_common_id: homeworks.pluck(:id)).destroy_all - normal_status(0, "删除成功") - - rescue Exception => e - uid_logger(e.message) - tip_exception("删除失败") - raise ActiveRecord::Rollback - end + homeworks = @course.homework_commons.where(id: params[:homework_ids]) + homeworks.destroy_all + + # 这些写是因为model中的关联删除无法删除is_delete=0的作品 + StudentWork.where(homework_common_id: homeworks.pluck(:id)).destroy_all + normal_status(0, "删除成功") + end end @@ -1160,41 +1143,35 @@ class HomeworkCommonsController < ApplicationController homeworks.each do |homework| ActiveRecord::Base.transaction do - begin - homework_bank = current_user.homework_banks.find_by(homework_common_id: homework.id) - if homework_bank.present? - # 如果作业加入过题库则更新参数 - if homework_bank.homework_type == 1 - homework_bank.update_attributes!(name: homework.name, description: homework.description, - reference_answer: homework.reference_answer, course_list_id: @course.course_list_id) - elsif homework_bank.homework_type == 3 - homework_detail_group = homework.homework_detail_group - homework_bank.update_attributes!(name: homework.name, description: homework.description, - reference_answer: homework.reference_answer, course_list_id: @course.course_list_id, - min_num: homework_detail_group.min_num, max_num: homework_detail_group.max_num, - base_on_project: homework_detail_group.base_on_project) - end - - # 附件的更新 - homework_bank.attachments.destroy_all - homework.attachments.each do |attachment| - att = attachment.copy - att.author_id = homework_bank.user_id - att.copy_from = attachment.id - homework_bank.attachments << att - end - else - new_homework_bank = add_to_homework_bank_f homework - new_homework_bank.save! + homework_bank = current_user.homework_banks.find_by(homework_common_id: homework.id) + if homework_bank.present? + # 如果作业加入过题库则更新参数 + if homework_bank.homework_type == 1 + homework_bank.update_attributes!(name: homework.name, description: homework.description, + reference_answer: homework.reference_answer, course_list_id: @course.course_list_id) + elsif homework_bank.homework_type == 3 + homework_detail_group = homework.homework_detail_group + homework_bank.update_attributes!(name: homework.name, description: homework.description, + reference_answer: homework.reference_answer, course_list_id: @course.course_list_id, + min_num: homework_detail_group.min_num, max_num: homework_detail_group.max_num, + base_on_project: homework_detail_group.base_on_project) + end - homework.update_attributes!(homework_bank_id: new_homework_bank.id) + # 附件的更新 + homework_bank.attachments.destroy_all + homework.attachments.each do |attachment| + att = attachment.copy + att.author_id = homework_bank.user_id + att.copy_from = attachment.id + homework_bank.attachments << att end + else + new_homework_bank = add_to_homework_bank_f homework + new_homework_bank.save! - rescue Exception => e - uid_logger(e.message) - tip_exception("删除失败") - raise ActiveRecord::Rollback + homework.update_attributes!(homework_bank_id: new_homework_bank.id) end + end end normal_status(0, "加入成功") @@ -1211,7 +1188,7 @@ class HomeworkCommonsController < ApplicationController # 班级作品查重 def homework_code_repeat - tip_exception(-1,"分班id不能为空!") if params[:group_ids].nil? + tip_exception(-1, "分班id不能为空!") if params[:group_ids].nil? shixun = @homework.shixuns.take # 通过代码文件来判断语言 language = shixun.challenges.practice_type.pluck(:path).first @@ -1229,16 +1206,16 @@ class HomeworkCommonsController < ApplicationController games.each do |game| game.game_codes.each do |game_code| code_infos << { - path: game_code.path, - content: Base64.urlsafe_encode64(game_code.new_code.to_s, padding: false), - passed_time: game.end_time.try(:strftime, '%Y-%m-%d %H:%M:%S') + path: game_code.path, + content: Base64.urlsafe_encode64(game_code.new_code.to_s, padding: false), + passed_time: game.end_time.try(:strftime, '%Y-%m-%d %H:%M:%S') } end end if code_infos.size != 0 user_lists << { - user_id: user_id, - code_info: code_infos + user_id: user_id, + code_info: code_infos } end end @@ -1252,13 +1229,13 @@ class HomeworkCommonsController < ApplicationController normal_status("代码查重成功") else if result.status == 1 - tip_exception(-4,"代码查重异常,请稍后重试") + tip_exception(-4, "代码查重异常,请稍后重试") else - tip_exception(-3,"正在查重,请在几分钟后刷新页面查看结果") + tip_exception(-3, "正在查重,请在几分钟后刷新页面查看结果") end end else - tip_exception(-2,"平台目前支持java、python语言的查重