Skip to content

Commit

Permalink
Merge branch 'SKIL-555' of https://github.com/Lunatic-Labs/rubricapp
Browse files Browse the repository at this point in the history
…into SKIL-555
  • Loading branch information
sah0017 committed Dec 23, 2024
2 parents 8108cd7 + 412faa1 commit dfd5ae1
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 79 deletions.
26 changes: 19 additions & 7 deletions BackEndFlask/Functions/teamBulkUpload.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from models.team_user import *
from models.user_course import *
from models.course import *
from models.queries import does_team_user_exist
from Functions.test_files.PopulationFunctions import xlsx_to_csv

from datetime import date
Expand Down Expand Up @@ -35,14 +36,14 @@ def __expect(lst: list[list[str]], cols: int | None = None) -> list[str]:
will modify the original list passed.
"""
hd: list[str] = lst.pop(0)

# Clean the row - specifically handle 'Unnamed:' columns and empty strings
cleaned = []
for x in hd:
stripped = x.strip()
if stripped and not stripped.startswith('Unnamed:'):
cleaned.append(stripped)

if cols is not None and len(cleaned) != cols:
raise TooManyColumns(1, cols, len(cleaned))
return cleaned
Expand Down Expand Up @@ -71,7 +72,16 @@ def __parse(lst: list[list[str]]) -> list[TBUTeam]:
raise EmptyTeamName if team_name == "" else EmptyTAEmail
teams.append(TBUTeam(team_name, ta, students))
students = []
current_state = EXPECT_TA

multiple_observers = True
if len(lst) > 2:
hd = __expect(lst)
lookAhead = __expect(lst)
lst.insert(0, lookAhead)
lst.insert(0, hd)
multiple_observers = len(hd) == len(lookAhead) == 1

current_state = EXPECT_TA if multiple_observers else EXPECT_TEAM
continue

# Process based on what type of row we're expecting
Expand Down Expand Up @@ -263,10 +273,12 @@ def __handle_student(student: TBUStudent, team_name: str, tainfo):
else:
set_inactive_status_of_user_to_active(user_course.user_course_id)

create_team_user({
"team_id": team_id,
"user_id": user_id
})
# Prevents duplicaition in the team user table.
if not does_team_user_exist(user_id, team_id):
create_team_user({
"team_id": team_id,
"user_id": user_id
})

tainfo = __handle_ta()

Expand Down
3 changes: 1 addition & 2 deletions BackEndFlask/controller/Routes/Rating_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def get_student_individual_ratings():
@jwt_required()
@bad_token_check()
@AuthCheck()
@admin_check()
def student_view_feedback():
"""
Description:
Expand All @@ -67,7 +66,7 @@ def student_view_feedback():
used to calculate lag time.
"""
try:
user_id = request.json.get("user_id")
user_id = request.args.get("user_id")
completed_assessment_id = request.json.get("completed_assessment_id")

exists = check_feedback_exists(user_id, completed_assessment_id)
Expand Down
2 changes: 1 addition & 1 deletion BackEndFlask/models/completed_assessment.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_completed_assessment_count(assessment_task_id):
@error_log
def completed_assessment_exists(team_id, assessment_task_id, user_id):
if (user_id == -1): # Team assessment, otherwise individual assessment
return CompletedAssessment.query.filter_by(team_id=team_id, assessment_task_id=assessment_task_id, user_id=user_id).first()
return CompletedAssessment.query.filter_by(team_id=team_id, assessment_task_id=assessment_task_id).first()
else:
return CompletedAssessment.query.filter_by(user_id=user_id, assessment_task_id=assessment_task_id).first()

Expand Down
28 changes: 27 additions & 1 deletion BackEndFlask/models/queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,4 +1188,30 @@ def get_students_for_emailing(is_teams: bool, completed_at_id: int = None, at_id
CompletedAssessment.completed_assessment_id == completed_at_id
)

return student_info.all()
return student_info.all()

def does_team_user_exist(user_id:int, team_id:int):
"""
Description:
Returns true or false if the (user_id,team_id) entry exists in TeamUser table.
Paramaters:
user_id: <class 'int'> (User Id)
team_id: <class 'int'> (Team Id)
Returns:
<class 'bool'> (If a (user_id, team_id) entry is present in TeamUser table)
Exceptions:
None except what the db or oem may raise.
"""
is_entry = db.session.query(
TeamUser.team_user_id
).filter(
TeamUser.user_id == user_id,
TeamUser.team_id == team_id
).all()

if len(is_entry) == 0:
return False
return True
62 changes: 34 additions & 28 deletions FrontEndReact/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,41 @@
},
"dependencies": {
"@babel/plugin-proposal-private-property-in-object": "",
"@babel/preset-react": "^7.23.3",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.8",
"@mui/icons-material": "^5.14.12",
"@mui/material": "^5.14.12",
"@mui/x-date-pickers": "^6.18.3",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.2.3",
"date-fns": "^2.30.0",
"date-fns-tz": "^2.0.1",
"dayjs": "^1.11.10",
"dotenv": "^16.3.1",
"dotenv-expand": "^10.0.0",
"@babel/preset-react": "^7.25.9",
"@emotion/react": "^11.13.5",
"@emotion/styled": "^11.13.5",
"@fontsource/roboto": "^5.1.0",
"@mui/icons-material": "^5.15.6",
"@mui/material": "^5.15.6",
"@mui/system": "^5.15.14",
"@mui/x-date-pickers": "^6.19.2",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/user-event": "^14.5.2",
"bootstrap": "^5.3.3",
"date-fns": "^3.2.0",
"date-fns-tz": "^3.2.0",
"dayjs": "^1.11.13",
"dotenv": "^16.4.5",
"dotenv-expand": "^12.0.1",
"eventsource-client": "^1.1.3",
"jest-dom": "^4.0.0",
"mui-datatables": "^4.3.0",
"mui-one-time-password-input": "^2.0.1",
"react": "^18.2.0",
"mui-one-time-password-input": "^3.0.1",
"react": "^18.3.1",
"react-beautiful-dnd": "^13.1.1",
"react-cookie": "^6.1.1",
"react-datepicker": "^4.12.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.10.0",
"react-cookie": "^7.2.2",
"react-datepicker": "^7.5.0",
"react-dom": "^18.3.1",
"react-router-dom": "^6.28.0",
"react-script": "^2.0.5",
"react-scripts": "^5.0.1",
"react-select": "^5.7.3",
"react-select": "^5.8.3",
"react-validation": "^3.0.7",
"recharts": "",
"ts-node": "^10.9.2",
"universal-cookie": "^6.1.1",
"validator": "^13.9.0",
"web-vitals": "^2.1.4"
"universal-cookie": "^7.2.2",
"validator": "^13.12.0",
"web-vitals": "^4.2.4"
},
"scripts": {
"start": "react-scripts start",
Expand All @@ -68,10 +70,14 @@
]
},
"devDependencies": {
"@babel/preset-typescript": "^7.23.3",
"@testing-library/react": "^14.1.2",
"@babel/preset-typescript": "^7.26.0",
"@testing-library/react": "^16.0.1",
"jest": "^29.7.0",
"react-test-renderer": "^18.2.0",
"react-test-renderer": "^18.3.1",
"resize-observer-polyfill": "1.5.1"
},
"overrides": {
"nth-check": "^2.0.1",
"postcss": "^8.4.31"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { genericResourceGET, genericResourcePOST, genericResourcePUT, getDueDate
import { Box, Button, FormControl, Typography, IconButton, TextField, Tooltip, FormControlLabel, Checkbox, MenuItem, Select, InputLabel, Radio, RadioGroup, FormLabel, FormGroup } from '@mui/material';
import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFnsV3';
import ImageModal from "../AddCustomRubric/CustomRubricModal.js";
import RubricDescriptionsImage from "../../../../../src/RubricDetailedOverview.png";
import FormHelperText from '@mui/material/FormHelperText';
Expand Down
91 changes: 71 additions & 20 deletions FrontEndReact/src/View/Admin/View/CompleteAssessmentTask/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Box, Tab, Button } from '@mui/material';
import Tabs, { tabsClasses } from '@mui/material/Tabs';
import UnitOfAssessmentTab from './UnitOfAssessmentTab.js';
import StatusIndicator, { StatusIndicatorState } from './StatusIndicator.js';
import { genericResourcePOST, genericResourcePUT } from '../../../../utility.js';
import { genericResourcePOST, genericResourcePUT, debounce } from '../../../../utility.js';
import Cookies from 'universal-cookie';
import Alert from '@mui/material/Alert';
import { getUnitCategoryStatus } from './cat_utils.js';
Expand All @@ -28,6 +28,8 @@ import { getUnitCategoryStatus } from './cat_utils.js';
* @property {number} state.currentCategoryTabIndex - Index of the currently selected rubric `categoryList`.
* @property {Object} state.section - Section object of the category `currentCategoryTabIndex` from `categoryList`.
* @property {boolean} state.displaySavedNotification - Boolean indicating whether to display the pop-up window that confirms the assessment is saved.
*
* @property {Set<number>} unitsThatNeedSaving - A set of all the unit indexes that need saving for autosave.
*/
class Form extends Component {
constructor(props) {
Expand All @@ -39,8 +41,10 @@ class Form extends Component {
categoryList: null,
currentCategoryTabIndex: 0,
section: null,
displaySavedNotification: false
}
displaySavedNotification: false,
};

this.unitsThatNeedSaving = new Set();

/**
* @method handleUnitTabChange - Handles the change of the unit tab.
Expand All @@ -53,7 +57,9 @@ class Form extends Component {
currentUnitTabIndex: newUnitTabIndex,
currentCategoryTabIndex: 0,
},
this.generateCategoriesAndSection
() => {
this.generateCategoriesAndSection();
}
);
}
};
Expand All @@ -68,7 +74,9 @@ class Form extends Component {
{
currentCategoryTabIndex: newCategoryTabIndex,
},
this.generateCategoriesAndSection
() => {
this.generateCategoriesAndSection();
}
);
}
};
Expand Down Expand Up @@ -161,7 +169,7 @@ class Form extends Component {
key={index}

modifyUnitCategoryProperty={this.modifyUnitCategoryProperty}
handleSubmit={this.handleSubmit}
markForAutosave={this.markForAutosave}
/>;
}
});
Expand All @@ -186,32 +194,37 @@ class Form extends Component {

/**
*
* @method handleSubmit - Handles the submission of the form.
* @param {boolean} newIsDone - The new completion status of the unit.
* @method saveUnit - Saves a unit to the server.
* @param {number} unitIndex - The index of the unit to save.
* @param {boolean} markDone - If the unit should be marked as done or retain the current done status.
*/
this.handleSubmit = (newIsDone) => {
this.saveUnit = (unitIndex, markDone) => {
const chosenAssessmentTaskId = this.props.navbar.state.chosenAssessmentTask["assessment_task_id"];
const selectedUnitIndex = this.state.currentUnitTabIndex;
const selectedUnit = this.state.units[selectedUnitIndex];
const unit = this.state.units[unitIndex];

const cookies = new Cookies();
const currentUserId = cookies.get("user")["user_id"];
const currentDate = new Date();

const newCAT = selectedUnit.generateNewCAT(chosenAssessmentTaskId, currentUserId, currentDate, newIsDone);
const newUnit = selectedUnit.withNewCAT(newCAT);

if (selectedUnit.completedAssessmentTask) {
const catId = selectedUnit.completedAssessmentTask["completed_assessment_id"];
// If markDone then mark the unit as done, otherwise use the original done status.
const newIsDone = markDone ? true : unit.isDone;

const newUnit = unit.withNewIsDone(newIsDone);
const newCAT = newUnit.generateNewCAT(chosenAssessmentTaskId, currentUserId, currentDate);

let promise;

if (unit.completedAssessmentTask) {
const catId = unit.completedAssessmentTask["completed_assessment_id"];

genericResourcePUT(
promise = genericResourcePUT(
`/completed_assessment?completed_assessment_id=${catId}`,
this,
JSON.stringify(newCAT),
{ rawResponse: true }
);
} else {
genericResourcePOST(
promise = genericResourcePOST(
`/completed_assessment?assessment_task_id=${chosenAssessmentTaskId}&${newUnit.getSubmitQueryParam()}`,
this,
JSON.stringify(newCAT),
Expand All @@ -224,7 +237,7 @@ class Form extends Component {
prevState => {
const updatedUnits = [...prevState.units];

updatedUnits[selectedUnitIndex] = newUnit;
updatedUnits[unitIndex] = newUnit;

return {
displaySavedNotification: true,
Expand All @@ -233,12 +246,50 @@ class Form extends Component {
}
);

// Once the CAT entry has been updated, insert the new CAT entry into the unit object
promise.then(result => {
const completeAssessmentEntry = result?.["content"]?.["completed_assessments"]?.[0]; // The backend returns a list of a single entry

if (completeAssessmentEntry) {
this.setState(
prevState => {
const updatedUnits = [...prevState.units];

updatedUnits[unitIndex] = updatedUnits[unitIndex].withNewCAT(completeAssessmentEntry);

return { units: updatedUnits };
}
);
}
});

setTimeout(() => {
this.setState({
displaySavedNotification: false
});
}, 3000);
};

/**
* @method markForAutosave - Marks a unit to be autosaving soon.
* @param {number} unitIndex - The index of the unit.
*/
this.markForAutosave = (unitIndex) => {
this.unitsThatNeedSaving.add(unitIndex);

this.doAutosave();
}

/**
* @method doAutosave - Performs an autosave.
*/
this.doAutosave = debounce(() => {
this.unitsThatNeedSaving.forEach(unitIndex => {
this.saveUnit(unitIndex, false);
});

this.unitsThatNeedSaving.clear();
}, 2000);
}

componentDidMount() {
Expand Down Expand Up @@ -266,7 +317,7 @@ class Form extends Component {
aria-label="saveButton"

onClick={() => {
this.handleSubmit(this.areAllCategoriesCompleted());
this.saveUnit(this.state.currentUnitTabIndex, this.areAllCategoriesCompleted());
}}

disabled={!this.areAllCategoriesCompleted()}
Expand Down
Loading

0 comments on commit dfd5ae1

Please sign in to comment.