Skip to content

Commit

Permalink
Version 2.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
hbiede committed Feb 24, 2021
1 parent fce1ba1 commit 63dcecc
Show file tree
Hide file tree
Showing 24 changed files with 372 additions and 62 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
],
"react/jsx-one-expression-per-line": "off",
"react/jsx-wrap-multilines": "off",
"react/require-default-props": "off",
"import/no-unresolved": "error",
"no-nested-ternary": "off",
"key-spacing": "off",
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ default: clean local_build upload
test: clean local_build upload_test

local_build:
yarn eslint
yarn run eslint
cp -r public build
yarn webpack --mode production --config webpack.config.ts
yarn run build
rm -rf ./build/.idea

upload:
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"name": "la-feedback-system",
"version": "2.0.4",
"version": "2.1.0",
"repository": "https://github.com/hbiede/LA-Feedback-System",
"readme": "./README.md",
"private": true,
"license": "Apache-2.0",
"description": "A website to gather feedback from students after interactions with LAs",
Expand Down Expand Up @@ -52,7 +54,7 @@
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build": "yarn webpack --mode production --config webpack.config.ts",
"eject": "react-scripts eject",
"test": "react-scripts test",
"eslint": "eslint --fix ./src --ext .js,.jsx,.ts,.tsx",
Expand Down
33 changes: 27 additions & 6 deletions public/admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,23 @@
// username: string (username of the Admin running the check),
//}


// Returns a JSON encoded object formatted as follows:
//{
// time: int (average time to complete the feedback form),
// sentiment: int (sentiment of a all comments ([-100,100] inclusive)),
// isAdmin: boolean,
// logins: LoginRecord[],
// outstanding: int (number of outstanding requests for feedback),
// ratings: InteractionRecord[]
// sentiment: int (sentiment of a all comments ([-100,100] inclusive)),
// time: int (average time to complete the feedback form),
//}
// If the username in the POST call is not an admin, this script returns empty data

// Where LoginRecord is an object summarizing a LA login timestamp as follows:
//{
// la: string (LA's name),
// time_of_interaction: date,
//}
// If the username in the POST call is not an admin, this script returns an empty array

// Where InteractionRecord is an object summarizing a single LA formatted as follows:
//{
Expand Down Expand Up @@ -60,6 +70,14 @@

ini_set('error_log', './log/admin.log');

function get_logins() {
return run_accessor("SELECT UNIX_TIMESTAMP(time_of_interaction) AS 'time_of_interaction', " .
"CONCAT(IFNULL(cul.name, cul.username), IF(is_admin, ' (Admin)', '')) AS 'la' " .
"FROM logins l " .
"LEFT JOIN cse_usernames cul on l.la_username_key = cul.username_key " .
"ORDER BY time_of_interaction");
}

function get_sentiment() {
$result = -1;
$conn = get_connection();
Expand Down Expand Up @@ -102,8 +120,9 @@ function get_outstanding_feedback() {

function get_interactions() {
$conn = get_connection();
$ps = $conn->prepare('SELECT username, name, cse_usernames.course, ' .
'COUNT(i.interaction_key) AS count, COUNT(t.interaction_key) AS wCount, COUNT(f.feedback_key) AS fCount, ' .
$ps = $conn->prepare('SELECT username, CONCAT(name, IF(is_admin, \' (Admin)\', \'\')) AS \'name\', ' .
'cse_usernames.course, COUNT(i.interaction_key) AS count, COUNT(t.interaction_key) AS wCount, ' .
'COUNT(f.feedback_key) AS fCount, ' .
'AVG(rating) AS avg, AVG(sentiment) AS sentiment FROM cse_usernames ' .
'LEFT JOIN interactions i on cse_usernames.username_key = i.la_username_key ' .
'LEFT JOIN feedback f on i.interaction_key = f.interaction_key ' .
Expand All @@ -125,7 +144,7 @@ function get_interactions() {
function get_ratings($la_username) {
$conn = get_connection();
$ps = $conn->prepare('SELECT rating, comment, course, sentiment, ' .
' DATE_FORMAT(time_of_interaction, "%Y-%m-%dT%TZ") AS time FROM feedback ' .
' UNIX_TIMESTAMP(time_of_interaction) AS time FROM feedback ' .
'LEFT JOIN interactions i on feedback.interaction_key = i.interaction_key WHERE ' .
'feedback.interaction_key IN (SELECT interaction_key FROM interactions WHERE la_username_key = ' .
'(SELECT username_key FROM cse_usernames WHERE username = ?)) ORDER BY rating DESC;');
Expand Down Expand Up @@ -167,6 +186,7 @@ function get_ratings($la_username) {
} else if ($isAdmin) {
$response = [
'isAdmin' => true,
'logins' => get_logins(),
'ratings' => get_interactions(),
'time' => get_time_to_complete(),
'outstanding' => get_outstanding_feedback(),
Expand All @@ -176,6 +196,7 @@ function get_ratings($la_username) {
} else {
$response = [
'isAdmin' => false,
'logins' => [],
'ratings' => [],
'time' => -1,
'outstanding' => 0,
Expand Down
13 changes: 2 additions & 11 deletions public/getStudents.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,11 @@
//}

include_once 'sqlManager.php';
ini_set('error_log', './log/student_list_accessor.log');


function get_students() {
$conn = get_connection();
$ps = $conn->prepare('SELECT username_key AS id, username, name, course, canvas_username FROM cse_usernames;');
$ps->execute();
$result = $ps->get_result();
$returnVal = [];
while ($row = $result->fetch_assoc()) {
array_push($returnVal, $row);
}
$ps->close();
$conn->close();
return $returnVal;
return run_accessor('SELECT username_key AS id, username, name, course, canvas_username FROM cse_usernames;');
}

$response = [
Expand Down
1 change: 0 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="./feedback.js"></script>
<link rel="stylesheet" href="https://unpkg.com/easymde/dist/easymde.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">
<link rel="stylesheet" href="https://unpkg.com/react-bootstrap-typeahead/css/Typeahead.css">
</body>
Expand Down
22 changes: 22 additions & 0 deletions public/sqlManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,28 @@ function get_course_counts() {
}
}

function run_accessor($query) {
$conn = get_connection();
$ps = $conn->prepare($query);
$returnVal = [];
if ($ps) {
$ps->execute();
$result = $ps->get_result();
if ($ps->error) {
error_log($ps->error);
} else {
while ($row = $result->fetch_assoc()) {
array_push($returnVal, $row);
}
}
$ps->close();
} else {
error_log($ps->error);
}
$conn->close();
return $returnVal;
}

function get_email($student_id) {
if ($student_id === null) return null;

Expand Down
8 changes: 5 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function App() {
adminAsLA,
]);

const AdminTable = React.lazy(() => import('screens/AdminTable'));
const AdminScreen = React.lazy(() => import('screens/AdminScreen'));
const loadingLabel = <h4 style={styles.loadingSpinner}>Loading</h4>;

return (
Expand Down Expand Up @@ -78,7 +78,9 @@ function App() {
variant={response?.class}
id="responseDiv"
onClose={() => setResponse(null)}
dismissible={response !== null}
dismissible={
response !== null && response.dismissable !== false
}
hidden={
response === null ||
response.content === null ||
Expand All @@ -92,7 +94,7 @@ function App() {
</Alert>
</Collapse>
<React.Suspense fallback={loadingLabel}>
<AdminTable style={styles.tableContainer} />
<AdminScreen style={styles.tableContainer} />
</React.Suspense>
</>
) : (
Expand Down
1 change: 1 addition & 0 deletions src/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"changes": [
"## 2.1.0\n- Add login records table to admin view\n- Prevent viewing feedback from LAs who have not received feedback",
"## 2.0.4\n- Prevent course accounts from logging interactions\n- Move course list to an API call",
"## 2.0.3\n- Add appropriate cursors to sortable table headers",
"## 2.0.2\n- Shows the number of interactions with each student to the expended interaction section\n- Adds weekly interactions to the admin table",
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@
- File created by Hundter Biede for the UNL CSE Learning Assistant Program
-----------------------------------------------------------------------------*/

import React, {
ChangeEvent,
CSSProperties,
useCallback,
useEffect,
useState,
} from 'react';
import React, { ChangeEvent, useCallback, useEffect, useState } from 'react';

import Button from 'react-bootstrap/Button';
import Dropdown from 'react-bootstrap/Dropdown';
Expand All @@ -21,19 +15,15 @@ import InputGroup from 'react-bootstrap/InputGroup';

import shallow from 'zustand/shallow';

import SentimentText from 'components/SentimentText';
import SentimentText from 'components/AdminComponents/SentimentText';

import Redux, { AppReduxState } from 'redux/modules';
import { DEFAULT_COURSE_NAME } from 'redux/modules/Types';

import LASummaryTable from 'components/LASummaryTable';
import LADetailTable from 'components/LADetailTable';
import FeedbackTimeText from 'components/FeedbackTimeText';
import OutstandingFeedbackText from 'components/OutstandingFeedbackText';

type Props = {
style?: CSSProperties;
};
import LASummaryTable from 'components/AdminComponents/LASummaryTable';
import LADetailTable from 'components/AdminComponents/LADetailTable';
import FeedbackTimeText from 'components/AdminComponents/FeedbackTimeText';
import OutstandingFeedbackText from 'components/AdminComponents/OutstandingFeedbackText';

type LA = {
username: string;
Expand All @@ -44,7 +34,7 @@ type LA = {
/**
* The main page for admins
*/
const AdminTable = ({ style }: Props) => {
const LAFeedbackTable = () => {
const {
interactions,
getInteractions,
Expand All @@ -57,6 +47,7 @@ const AdminTable = ({ style }: Props) => {
courses,
course,
setCourse,
setResponse,
} = Redux(
(state: AppReduxState) => ({
interactions: state.interactions,
Expand All @@ -70,6 +61,7 @@ const AdminTable = ({ style }: Props) => {
courses: state.courses,
course: state.course,
setCourse: state.setCourse,
setResponse: state.setResponse,
}),
shallow
);
Expand All @@ -83,12 +75,25 @@ const AdminTable = ({ style }: Props) => {

const showLA = useCallback(
(la: LA) => {
setSelectedLA(la);
setSelectedUsername(la);
setNewName(la.name ?? la.username);
setCourseRecord(la.course ?? null);
if (
(
interactions.ratings.find(
(rating) => rating.username === la.username
) ?? { fCount: 0 }
).fCount > 0
) {
setSelectedLA(la);
setSelectedUsername(la);
setNewName(la.name ?? la.username);
setCourseRecord(la.course ?? null);
} else {
setResponse({
class: 'danger',
content: `${la.name ?? la.username} has no recorded feedback`,
});
}
},
[setSelectedUsername]
[interactions.ratings, setSelectedUsername, setResponse]
);

useEffect(() => {
Expand Down Expand Up @@ -168,17 +173,17 @@ const AdminTable = ({ style }: Props) => {

if (selectedLA === null) {
return (
<div style={style} className="col-md-10">
<>
<LASummaryTable showLA={showLA} />
<SentimentText />
<OutstandingFeedbackText />
<FeedbackTimeText />
</div>
</>
);
}

return (
<div style={style} className="col-md-10">
<>
<Form.Row>
<InputGroup style={{ marginTop: 10 }} className="mb-3">
<Button variant="dark" type="button" onClick={clearSelection}>
Expand Down Expand Up @@ -238,12 +243,12 @@ const AdminTable = ({ style }: Props) => {
</InputGroup>
</Form.Row>
<LADetailTable />
</div>
</>
);
};

AdminTable.defaultProps = {
LAFeedbackTable.defaultProps = {
style: {},
};

export default AdminTable;
export default LAFeedbackTable;
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ import Table from 'react-bootstrap/Table';

import shallow from 'zustand/shallow';

import ServiceInterface from 'statics/ServiceInterface';
import { InteractionRecord, SORT_CHARS, SortConfig } from 'statics/Types';

import getRowClass from 'components/TableRowColors';
import Redux, { AppReduxState } from 'redux/modules';
import PaginationButtons, {
RATINGS_PER_PAGE,
} from 'components/PaginationButtons';

import ServiceInterface from '../statics/ServiceInterface';
import Redux, { AppReduxState } from 'redux/modules';

type Props = {
showLA: (la: InteractionRecord) => void;
};

const LA_ID = 'la_name';
const COURSE_ID = 'course';
const INTERACTION_COUNT_ID = 'int_count';
const WEEKLY_INTERACTION_COUNT_ID = 'week_int_count';
const AVERAGE_RATING_ID = 'avg_rating';
const LA_ID = 'la_name_column';
const COURSE_ID = 'course_column';
const INTERACTION_COUNT_ID = 'int_count_column';
const WEEKLY_INTERACTION_COUNT_ID = 'week_int_count_column';
const AVERAGE_RATING_ID = 'avg_rating_column';

const LASummaryTable = ({ showLA }: Props) => {
const { interactions } = Redux(
Expand Down
Loading

0 comments on commit 63dcecc

Please sign in to comment.