diff --git a/README.md b/README.md
index 359b677..1897386 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ Secure check-in and scoring. Easy for instructors, easy for students.
- Automatically generates QR codes for check-in
- Easily export data to CSV, with filters
- Add/remove users from comprehensive admin console
+- Flag suspicious activity automatically
+- Enforce lab section timeslots
Learn more [here](https://cmulab.quantumstack.xyz)
@@ -30,11 +32,11 @@ Learn more [here](https://cmulab.quantumstack.xyz)
## Images
-![Home Page](https://i.imgur.com/DxFn4NR.jpg)
+
-![Checkin Page](https://i.imgur.com/NkSU5vm.jpg)
+
-![Administration Page](https://i.imgur.com/RxAPdSZ.png)
+
## Contributing
diff --git a/server/config.json b/server/config.json
index 611115c..9a7c8c2 100644
--- a/server/config.json
+++ b/server/config.json
@@ -5,6 +5,7 @@
"offsetMinutes": 0,
"minScore": 0,
"maxScore": 3,
+ "showDelete": true,
"lowercaseStudents": false,
"radioInput": true,
"manualLab": false,
@@ -12,5 +13,6 @@
"flagAttempts": "",
"flagAttemptsThreshold": 3600000,
"flagGhosts": true,
+ "allowOverride": true,
"sections": {}
}
diff --git a/server/public/javascripts/datarow.js b/server/public/javascripts/datarow.js
index 7e36606..0b537fa 100644
--- a/server/public/javascripts/datarow.js
+++ b/server/public/javascripts/datarow.js
@@ -53,6 +53,7 @@ var DataRow = function (_React$Component) {
key: 'render',
value: function render() {
var _props = this.props,
+ showStudent = _props.showStudent,
_id = _props._id,
section = _props.section,
student_id = _props.student_id,
@@ -76,7 +77,7 @@ var DataRow = function (_React$Component) {
React.createElement(
'td',
null,
- student_id
+ showStudent ? student_id : '••••••'
),
React.createElement(
'td',
diff --git a/server/public/javascripts/datatable.js b/server/public/javascripts/datatable.js
index 3d8f2e0..00e26af 100644
--- a/server/public/javascripts/datatable.js
+++ b/server/public/javascripts/datatable.js
@@ -17,10 +17,12 @@ var DataTable = function (_React$Component) {
var _this = _possibleConstructorReturn(this, (DataTable.__proto__ || Object.getPrototypeOf(DataTable)).call(this, props));
_this.state = {
+ studentVisibility: false,
lab: '',
labActive: false,
preserve: false
};
+ _this.toggleStudentVisibility = _this.toggleStudentVisibility.bind(_this);
_this.toggleLab = _this.toggleLab.bind(_this);
_this.onChangeLab = _this.onChangeLab.bind(_this);
_this.onChangePreserve = _this.onChangePreserve.bind(_this);
@@ -29,10 +31,18 @@ var DataTable = function (_React$Component) {
}
_createClass(DataTable, [{
+ key: 'toggleStudentVisibility',
+ value: function toggleStudentVisibility() {
+ this.setState(function (_ref) {
+ var studentVisibility = _ref.studentVisibility;
+ return { studentVisibility: !studentVisibility };
+ });
+ }
+ }, {
key: 'toggleLab',
value: function toggleLab() {
- this.setState(function (_ref) {
- var labActive = _ref.labActive;
+ this.setState(function (_ref2) {
+ var labActive = _ref2.labActive;
return { labActive: !labActive };
});
}
@@ -67,7 +77,9 @@ var DataTable = function (_React$Component) {
sort = _props.sort,
entries = _props.entries,
updateSort = _props.updateSort;
- var labActive = this.state.labActive;
+ var _state2 = this.state,
+ studentVisibility = _state2.studentVisibility,
+ labActive = _state2.labActive;
var columns = [['Section', 'section'], ['Student ID', 'student_id'], ['Score', 'score'], ['Lab', 'lab'], ['Date', 'date'], ['TA', 'ta'], ['Flags', 'flags']];
return React.createElement(
@@ -79,10 +91,10 @@ var DataTable = function (_React$Component) {
React.createElement(
'tr',
null,
- columns.map(function (_ref2) {
- var _ref3 = _slicedToArray(_ref2, 2),
- title = _ref3[0],
- name = _ref3[1];
+ columns.map(function (_ref3) {
+ var _ref4 = _slicedToArray(_ref3, 2),
+ title = _ref4[0],
+ name = _ref4[1];
return React.createElement(
'th',
@@ -172,6 +184,15 @@ var DataTable = function (_React$Component) {
{ className: 'icon' },
React.createElement('i', { className: 'fas fa-caret-' + (sort[name] > 0 ? 'down' : 'up') })
)
+ ),
+ name === 'student_id' && React.createElement(
+ 'a',
+ { className: 'tooltip', 'data-tooltip': 'Toggle visibility', onClick: _this2.toggleStudentVisibility },
+ React.createElement(
+ 'span',
+ { className: 'icon has-text-info' },
+ React.createElement('i', { className: 'fas fa-' + (studentVisibility ? 'eye-slash' : 'eye') })
+ )
)
);
})
@@ -181,7 +202,7 @@ var DataTable = function (_React$Component) {
'tbody',
null,
entries.map(function (entry) {
- return React.createElement(DataRow, Object.assign({ key: entry._id }, entry));
+ return React.createElement(DataRow, Object.assign({ showStudent: studentVisibility, key: entry._id }, entry));
})
)
);
diff --git a/server/public/javascripts/dataview.js b/server/public/javascripts/dataview.js
index cb8b029..0975f7a 100644
--- a/server/public/javascripts/dataview.js
+++ b/server/public/javascripts/dataview.js
@@ -17,6 +17,7 @@ var DataView = function (_React$Component) {
_this.state = {
error: null,
isLoaded: false,
+ showData: true,
entries: [],
filters: {
good: true
@@ -49,10 +50,9 @@ var DataView = function (_React$Component) {
filters: this.state.filters,
sort: this.state.sort
}).then(function (res) {
- return _this2.setState({
- isLoaded: true,
- entries: res.data
- });
+ return _this2.setState(Object.assign({
+ isLoaded: true
+ }, res.data));
}, function (err) {
return _this2.setState({
isLoaded: true,
@@ -194,6 +194,7 @@ var DataView = function (_React$Component) {
var _state = this.state,
error = _state.error,
isLoaded = _state.isLoaded,
+ showDelete = _state.showDelete,
entries = _state.entries,
filters = _state.filters,
sort = _state.sort,
@@ -221,7 +222,7 @@ var DataView = function (_React$Component) {
'div',
null,
React.createElement(FilterPane, { filters: filters, filtersActive: filtersActive, toggleFilters: this.toggleFilters, updateFilters: this.updateFilters, getData: this.getData }),
- React.createElement(DataViewBar, { entriesCount: entries.length, filters: filters, toggleFilters: this.toggleFilters, updateFilters: this.updateFilters, getData: this.getData, downloadData: this.downloadData, deleteData: this.deleteData }),
+ React.createElement(DataViewBar, { showDelete: showDelete, entriesCount: entries.length, filters: filters, toggleFilters: this.toggleFilters, updateFilters: this.updateFilters, getData: this.getData, downloadData: this.downloadData, deleteData: this.deleteData }),
React.createElement(DataDownload, { isActive: modalActive, toggleModal: this.toggleModal, data: downloadData, type: downloadType }),
React.createElement(DataTable, { sort: sort, entries: entries, updateSort: this.updateSort, assignLab: this.assignLab })
);
diff --git a/server/public/javascripts/dataviewbar.js b/server/public/javascripts/dataviewbar.js
index 09a2d12..1bb37e2 100644
--- a/server/public/javascripts/dataviewbar.js
+++ b/server/public/javascripts/dataviewbar.js
@@ -50,6 +50,7 @@ var DataViewBar = function (_React$Component) {
key: 'render',
value: function render() {
var _props = this.props,
+ showDelete = _props.showDelete,
filters = _props.filters,
toggleFilters = _props.toggleFilters,
updateFilters = _props.updateFilters,
@@ -212,7 +213,7 @@ var DataViewBar = function (_React$Component) {
)
)
),
- React.createElement(
+ showDelete && React.createElement(
'p',
{ className: 'level-item' },
React.createElement(
diff --git a/server/public/javascripts/flag-dismiss.js b/server/public/javascripts/flag-dismiss.js
deleted file mode 100644
index d9f699a..0000000
--- a/server/public/javascripts/flag-dismiss.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function flagDismiss() {
- document.getElementById('flags').classList.add('is-hidden');
- document.getElementById('main').classList.remove('is-fullheight-with-navbar');
- document.getElementById('main').classList.add('is-fullheight');
-}
diff --git a/server/public/javascripts/flag-manipulation.js b/server/public/javascripts/flag-manipulation.js
new file mode 100644
index 0000000..ea1259a
--- /dev/null
+++ b/server/public/javascripts/flag-manipulation.js
@@ -0,0 +1,28 @@
+function flagDismiss() {
+ document.getElementById('flags').classList.add('is-hidden');
+ document.getElementById('main').classList.remove('is-fullheight-with-navbar');
+ document.getElementById('main').classList.add('is-fullheight');
+}
+
+function flagOverride() {
+ const override = document.getElementById('override').value;
+ if (override) {
+ document.getElementById('flag-toggle').innerHTML = 'Override';
+ document.getElementById('override').value = '';
+ document.getElementById('flag-icon').classList.add('fa-exclamation-triangle');
+ document.getElementById('flag-icon').classList.remove('fa-check');
+ document.getElementById('flag-notification').classList.add('is-danger');
+ document.getElementById('flag-notification').classList.remove('is-warning');
+ document.getElementById('flags').classList.add('is-danger');
+ document.getElementById('flags').classList.remove('is-warning');
+ } else {
+ document.getElementById('flag-toggle').innerHTML = 'Flag';
+ document.getElementById('override').value = 'good';
+ document.getElementById('flag-icon').classList.remove('fa-exclamation-triangle');
+ document.getElementById('flag-icon').classList.add('fa-check');
+ document.getElementById('flag-notification').classList.remove('is-danger');
+ document.getElementById('flag-notification').classList.add('is-warning');
+ document.getElementById('flags').classList.remove('is-danger');
+ document.getElementById('flags').classList.add('is-warning');
+ }
+}
diff --git a/server/public/javascripts/react/datarow.jsx b/server/public/javascripts/react/datarow.jsx
index 51d99f5..110db22 100644
--- a/server/public/javascripts/react/datarow.jsx
+++ b/server/public/javascripts/react/datarow.jsx
@@ -28,14 +28,14 @@ class DataRow extends React.Component {
}
render() {
- const { _id, section, student_id, score, lab, date, ta, flags } = this.props;
+ const { showStudent, _id, section, student_id, score, lab, date, ta, flags } = this.props;
const { good, isActive } = this.state;
return