From e68ecc1d0bac97e580600a4013dad3a4b1dc213d Mon Sep 17 00:00:00 2001 From: Brennan Cottrell Date: Fri, 22 Dec 2023 17:07:44 -0500 Subject: [PATCH 001/160] mysql support --- BackEndFlask/README.md | 27 +++++- BackEndFlask/controller/Routes/Login_route.py | 8 +- BackEndFlask/controller/Routes/User_routes.py | 4 +- BackEndFlask/core/__init__.py | 7 +- BackEndFlask/dbcreate.py | 1 + BackEndFlask/logs/all.log | 22 +++++ BackEndFlask/models/loadExistingRubrics.py | 2 +- BackEndFlask/models/queries.py | 2 +- BackEndFlask/models/schemas.py | 88 ++++++++----------- BackEndFlask/models/user.py | 6 +- 10 files changed, 100 insertions(+), 67 deletions(-) diff --git a/BackEndFlask/README.md b/BackEndFlask/README.md index 9725bf3d0..77459e08d 100644 --- a/BackEndFlask/README.md +++ b/BackEndFlask/README.md @@ -37,4 +37,29 @@ If for some reason you want the sqlite database file and users directory to live You need to run sudo-apt install redis-server modification to appended only service can be done looking at AOF -at: https://redis.io/docs/management/persistence/ \ No newline at end of file +at: https://redis.io/docs/management/persistence/ + +#### MySQL Setup + +First, install MySQL-Server + +``` +sudo apt install mysql-server +``` + +Second, you need to set the MySQL password. To do this, first run the command: +``` +sudo mysql -u root +``` + +This will place you in the MySQL terminal. Once there use this command to set the passowrd to "skillbuilder" (this should be changed for deployment) + +CREATE USER 'skillbuilder'@'localhost' IDENTIFIED BY 'WasPogil1#'; +GRANT ALL PRIVILEGES ON *.* TO 'skillbuilder'@'localhost'; +FLUSH PRIVILEGES; +exit; + +mysql -p skillbuilder -p + + + diff --git a/BackEndFlask/controller/Routes/Login_route.py b/BackEndFlask/controller/Routes/Login_route.py index b7b25fe8c..a85b197e5 100644 --- a/BackEndFlask/controller/Routes/Login_route.py +++ b/BackEndFlask/controller/Routes/Login_route.py @@ -25,7 +25,7 @@ def login(): revokeTokens() return create_bad_response("Bad request: Invalid Email", "login", 400) - isAdmin = user.isAdmin + is_admin = user.is_admin user = userSchema.dump(user) if check_password_hash(get_user_password(user['user_id']), password): @@ -35,7 +35,7 @@ def login(): "email": email, "user_id": user['user_id'], "isSuperAdmin": user['user_id']==1, - "isAdmin": isAdmin, + "is_admin": is_admin, "has_set_password": user['has_set_password'], "user_name": user['first_name'] + " " + user['last_name'] } @@ -102,7 +102,7 @@ def check_reset_code(): if user is None: return create_bad_response(f"Bad request: No such email {email}", "reset_code", 400) - isAdmin = user.isAdmin + is_admin = user.is_admin if check_password_hash(user.reset_code, code): # if code match, log the user in jwt, refresh = createTokens(user.user_id) @@ -111,7 +111,7 @@ def check_reset_code(): "email": email, "user_id": user.user_id, "isSuperAdmin": user.user_id==1, - "isAdmin": isAdmin, + "is_admin": is_admin, "has_set_password": user.has_set_password, } diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 6ea88f13c..0a441bd11 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -46,7 +46,7 @@ @AuthCheck() def getAllUsers(): try: - if request.args and request.args.get("isAdmin"): + if request.args and request.args.get("is_admin"): return create_good_response(users_schema.dump(get_user_admins()), 200, "users") if (request.args and request.args.get("team_id")): @@ -198,7 +198,7 @@ class Meta: 'active', 'has_set_password', 'reset_code', - 'isAdmin', + 'is_admin', 'role_id' ) diff --git a/BackEndFlask/core/__init__.py b/BackEndFlask/core/__init__.py index 2e830dca0..fa75c16f3 100644 --- a/BackEndFlask/core/__init__.py +++ b/BackEndFlask/core/__init__.py @@ -18,10 +18,9 @@ app.config['JSON_SORT_KEYS'] = False jwt = JWTManager(app) accountDBPath = os.getcwd() + os.path.join(os.path.sep, "core") + os.path.join(os.path.sep, "account.db") -if os.path.exists(accountDBPath): - app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///./account.db' -else: - app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///../instance/account.db' + +app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://skillbuilder:WasPogil1#@localhost/account' + db = SQLAlchemy() db.init_app(app) ma = Marshmallow() diff --git a/BackEndFlask/dbcreate.py b/BackEndFlask/dbcreate.py index d0917bda8..488c22040 100644 --- a/BackEndFlask/dbcreate.py +++ b/BackEndFlask/dbcreate.py @@ -29,6 +29,7 @@ print("[dbcreate] attempting to create new db...") time.sleep(sleepTime) try: + db.drop_all() db.create_all() startRedis() except Exception as e: diff --git a/BackEndFlask/logs/all.log b/BackEndFlask/logs/all.log index e69de29bb..4a782c979 100644 --- a/BackEndFlask/logs/all.log +++ b/BackEndFlask/logs/all.log @@ -0,0 +1,22 @@ +2023-12-22 15:53:32,837 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 15:57:05,009 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 15:59:18,203 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:03:31,223 - ERROR - (1452, 'Cannot add or update a child row: a foreign key constraint fails (`account`.`Category`, CONSTRAINT `Category_ibfk_1` FOREIGN KEY (`rubric_id`) REFERENCES `Rubric` (`rubric_id`))') +2023-12-22 16:03:50,632 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:04:07,611 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:07:24,840 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:08:39,291 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:11:39,316 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:12:23,512 - ERROR - (1406, "Data too long for column 'rubric_description' at row 1") +2023-12-22 16:18:58,376 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:19:58,731 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:23:39,483 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:24:29,757 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:24:46,639 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:33:33,424 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:36:19,026 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:36:49,449 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:37:13,768 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:38:18,357 - ERROR - (1054, "Unknown column 'Category.rubric_id' in 'field list'") +2023-12-22 16:43:07,247 - ERROR - (1452, 'Cannot add or update a child row: a foreign key constraint fails (`account`.`User`, CONSTRAINT `User_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `User` (`user_id`))') +2023-12-22 16:58:53,217 - ERROR - (1452, 'Cannot add or update a child row: a foreign key constraint fails (`account`.`User`, CONSTRAINT `User_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `User` (`user_id`))') diff --git a/BackEndFlask/models/loadExistingRubrics.py b/BackEndFlask/models/loadExistingRubrics.py index a59142d64..c547ee94e 100644 --- a/BackEndFlask/models/loadExistingRubrics.py +++ b/BackEndFlask/models/loadExistingRubrics.py @@ -368,7 +368,7 @@ def load_existing_suggestions(): [2, 12, "Confirm that all work that has been published elsewhere or ideas/data that were not generated by the author(s) has been properly cited using appropriate conventions."], [2, 12, "Ask someone else to review and provide feedback on your work."], # Delivery Oral Suggestions 1-5 - [2, 13, "Practice for others or record your talk; i. be sure that your voice can be heard, and your word pronunciations are clear. ii. listen for “ums”, “like”, or other verbal tics/filler words that can detract from your message. iii. observe your natural body language, gestures, and stance in front of the audience to be sure that they express confidence and enhance your message."], + [2, 13, "Practice for others or record your talk; i. be sure that your voice can be heard, and your word pronunciations are clear. ii. listen for \"ums\", \"like\", or other verbal tics/filler words that can detract from your message. iii. observe your natural body language, gestures, and stance in front of the audience to be sure that they express confidence and enhance your message."], [2, 13, "Add variety to your speed or vocal tone to emphasize key points or transitions."], [2, 13, "Try to communicate/engage as if telling a story or having a conversation with the audience."], [2, 13, "Face the audience and do not look continuously at the screen or notes."], diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 7d6df4c31..b694f38a0 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -130,7 +130,7 @@ def get_user_admins(): User.consent, User.owner_id ).filter_by( - isAdmin=True + is_admin=True ).all() db.session.query() return all_user_admins diff --git a/BackEndFlask/models/schemas.py b/BackEndFlask/models/schemas.py index fd96341a8..cdb6c4066 100644 --- a/BackEndFlask/models/schemas.py +++ b/BackEndFlask/models/schemas.py @@ -10,7 +10,7 @@ ObservableCharacteristics(observable_characteristics_id, rubric_id, category_id, observable_characteristics_text) SuggestionsForImprovement(suggestion_id, rubric_id, category_id, suggestion_text) Role(role_id, role_name) - Users(user_id, first_name, last_name, email, password, lms_id, consent, owner_id, has_set_password, reset_code, isAdmin) + Users(user_id, first_name, last_name, email, password, lms_id, consent, owner_id, has_set_password, reset_code, is_admin) Course(course_id, course_number, course_name, year, term, active, admin_id, use_tas, use_fixed_teams) UserCourse(user_course_id, user_id, course_id, role_id) Team(team_id, team_name, course_id, observer_id, date_created, active_until) @@ -22,71 +22,63 @@ class Rubric(db.Model): __tablename__ = "Rubric" - __table_args__ = {'sqlite_autoincrement': True} - rubric_id = db.Column(db.Integer, primary_key=True) - rubric_name = db.Column(db.String(100)) - rubric_description = db.Column(db.String(100), nullable=True) + rubric_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + rubric_name = db.Column(db.Text) + rubric_description = db.Column(db.Text(1000), nullable=True) class Category(db.Model): __tablename__ = "Category" - __table_args__ = {'sqlite_autoincrement': True} - category_id = db.Column(db.Integer, primary_key=True) + category_id = db.Column(db.Integer, primary_key=True, autoincrement=True) rubric_id = db.Column(db.Integer, ForeignKey(Rubric.rubric_id), nullable=False) - category_name = db.Column(db.String(30), nullable=False) + category_name = db.Column(db.Text, nullable=False) class Rating(db.Model): __tablename__ = "Rating" - __table_args__ = {'sqlite_autoincrement': True} - rating_id = db.Column(db.Integer, primary_key=True) - rating_description = db.Column(db.String(255), nullable=False) + rating_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + rating_description = db.Column(db.Text, nullable=False) rating_json = db.Column(db.JSON, nullable=False) category_id = db.Column(db.Integer, ForeignKey(Category.category_id), nullable=False) class ObservableCharacteristic(db.Model): __tablename__ = "ObservableCharacteristic" - __table_args__ = {'sqlite_autoincrement': True} - observable_characteristics_id = db.Column(db.Integer, primary_key=True) + observable_characteristics_id = db.Column(db.Integer, primary_key=True, autoincrement=True) rubric_id = db.Column(db.Integer, ForeignKey(Rubric.rubric_id), nullable=False) category_id = db.Column(db.Integer, ForeignKey(Category.category_id), nullable=False) - observable_characteristic_text = db.Column(db.String(10000), nullable=False) + observable_characteristic_text = db.Column(db.Text, nullable=False) class SuggestionsForImprovement(db.Model): __tablename__ = "SuggestionsForImprovement" - __table_args__ = {'sqlite_autoincrement': True} - suggestion_id = db.Column(db.Integer, primary_key=True) + suggestion_id = db.Column(db.Integer, primary_key=True, autoincrement=True) rubric_id = db.Column(db.Integer, ForeignKey(Rubric.rubric_id), nullable=False) category_id = db.Column(db.Integer, ForeignKey(Category.category_id), nullable=False) suggestion_text = db.Column(db.JSON, nullable=False) class Role(db.Model): __tablename__ = "Role" - __table_args__ = {'sqlite_autoincrement': True} - role_id = db.Column(db.Integer, primary_key=True) - role_name = db.Column(db.String(100), nullable=False) + role_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + role_name = db.Column(db.Text, nullable=False) class User(db.Model): __tablename__ = "User" - __table_args__ = {'sqlite_autoincrement': True} - user_id = db.Column(db.Integer, primary_key=True) - first_name = db.Column(db.String(30), nullable=False) - last_name = db.Column(db.String(30), nullable=False) - email = db.Column(db.String(255), unique=True, nullable=False) - password = db.Column(db.String(80), nullable=False) + user_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + first_name = db.Column(db.Text, nullable=False) + last_name = db.Column(db.Text, nullable=False) + email = db.Column(db.String(254), unique=True, nullable=False) + password = db.Column(db.Text, nullable=False) lms_id = db.Column(db.Integer, nullable=True) consent = db.Column(db.Boolean, nullable=True) owner_id = db.Column(db.Integer, ForeignKey(user_id), nullable=True) has_set_password = db.Column(db.Boolean, nullable=False) - reset_code = db.Column(db.String(6), nullable=True) - isAdmin = db.Column(db.Boolean, nullable=False) + reset_code = db.Column(db.Text, nullable=True) + is_admin = db.Column(db.Boolean, nullable=False) class Course(db.Model): __tablename__ = "Course" - __table_args__ = {'sqlite_autoincrement': True} - course_id = db.Column(db.Integer, primary_key=True) - course_number = db.Column(db.String(10), nullable=False) - course_name = db.Column(db.String(50), nullable=False) + course_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + course_number = db.Column(db.Text, nullable=False) + course_name = db.Column(db.Text, nullable=False) year = db.Column(db.Integer, nullable=False) - term = db.Column(db.String(50), nullable=False) + term = db.Column(db.Text, nullable=False) active = db.Column(db.Boolean, nullable=False) admin_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=False) use_tas = db.Column(db.Boolean, nullable=False) @@ -94,8 +86,7 @@ class Course(db.Model): class UserCourse(db.Model): __tablename__ = "UserCourse" - __table_arges__ = {'sqlite_autoincrement': True} - user_course_id = db.Column(db.Integer, primary_key=True) + user_course_id = db.Column(db.Integer, primary_key=True, autoincrement=True) user_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=False) course_id = db.Column(db.Integer, ForeignKey(Course.course_id), nullable=False) active = db.Column(db.Boolean) @@ -103,9 +94,8 @@ class UserCourse(db.Model): class Team(db.Model): # keeps track of default teams for a fixed team scenario __tablename__ = "Team" - __table_args__ = {'sqlite_autoincrement': True} - team_id = db.Column(db.Integer, primary_key=True) - team_name = db.Column(db.String(25), nullable=False) + team_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + team_name = db.Column(db.Text, nullable=False) course_id = db.Column(db.Integer, ForeignKey(Course.course_id), nullable=False) observer_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=False) date_created = db.Column(db.Date, nullable=False) @@ -113,31 +103,28 @@ class Team(db.Model): # keeps track of default teams for a fixed team scenario class TeamUser(db.Model): __tablename__ = "TeamUser" - __table_args__ = {'sqlite_autoincrement': True} - team_user_id = db.Column(db.Integer, primary_key=True) + team_user_id = db.Column(db.Integer, primary_key=True, autoincrement=True) team_id = db.Column(db.Integer, ForeignKey(Team.team_id), nullable=False) user_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=False) class AssessmentTask(db.Model): __tablename__ = "AssessmentTask" - __table_args__ = {'sqlite_autoincrement' : True} - assessment_task_id = db.Column(db.Integer, primary_key=True) - assessment_task_name = db.Column(db.String(100)) + assessment_task_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + assessment_task_name = db.Column(db.Text) course_id = db.Column(db.Integer, ForeignKey(Course.course_id)) rubric_id = db.Column(db.Integer, ForeignKey(Rubric.rubric_id)) # how to handle updates and deletes role_id = db.Column(db.Integer, ForeignKey(Role.role_id)) due_date = db.Column(db.DateTime, nullable=False) - time_zone = db.Column(db.String(3), nullable=False) + time_zone = db.Column(db.Text, nullable=False) show_suggestions = db.Column(db.Boolean, nullable=False) show_ratings = db.Column(db.Boolean, nullable=False) unit_of_assessment = db.Column(db.Boolean, nullable=False) # true if team, false if individuals - comment = db.Column(db.String(3000), nullable=True) - create_team_password = db.Column(db.String(25), nullable=True) + comment = db.Column(db.Text, nullable=True) + create_team_password = db.Column(db.Text, nullable=True) class Checkin(db.Model): # keeps students checking to take a specific AT __tablename__ = "Checkin" - __table_args__ = {'sqlite_autoincrement': True} - checkin_id = db.Column(db.Integer, primary_key=True) + checkin_id = db.Column(db.Integer, primary_key=True, autoincrement=True) assessment_task_id = db.Column(db.Integer, ForeignKey(AssessmentTask.assessment_task_id), nullable=False) # not a foreign key because in the scenario without fixed teams, there will not be default team entries # to reference. if they are default teams, team_number will equal the team_id of the corresponding team @@ -147,19 +134,18 @@ class Checkin(db.Model): # keeps students checking to take a specific AT class CompletedAssessment(db.Model): __tablename__ = "CompletedAssessment" - __table_args__ = {'sqlite_autoincrement': True} - completed_assessment_id = db.Column(db.Integer, primary_key=True) + completed_assessment_id = db.Column(db.Integer, primary_key=True, autoincrement=True) assessment_task_id = db.Column(db.Integer, ForeignKey(AssessmentTask.assessment_task_id)) team_id = db.Column(db.Integer, ForeignKey(Team.team_id), nullable=True) user_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=True) initial_time = db.Column(db.DateTime(timezone=True), nullable=False) last_update = db.Column(db.DateTime(timezone=True), nullable=True) rating_observable_characteristics_suggestions_data = db.Column(db.JSON, nullable=True) + example_column = db.Column(db.Integer) class Feedback(db.Model): __tablename__ = "Feedback" - __table_args__ = {'sqlite_autoincrement': True} - feedback_id = db.Column(db.Integer, primary_key=True) + feedback_id = db.Column(db.Integer, primary_key=True, autoincrement=True) user_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=False) completed_assessment_id = db.Column(db.Integer, ForeignKey(CompletedAssessment.completed_assessment_id), nullable=False) feedback_time = db.Column(DateTime(timezone=True), nullable=True) diff --git a/BackEndFlask/models/user.py b/BackEndFlask/models/user.py index 3a8833992..8d0ab9afa 100644 --- a/BackEndFlask/models/user.py +++ b/BackEndFlask/models/user.py @@ -178,7 +178,7 @@ def create_user(user_data): lms_id=user_data["lms_id"], consent=user_data["consent"], owner_id=user_data["owner_id"], - isAdmin="role_id" in user_data.keys() and user_data["role_id"]==3, + is_admin="role_id" in user_data.keys() and user_data["role_id"]==3, has_set_password=has_set_password, reset_code=None ) @@ -193,7 +193,7 @@ def create_user(user_data): def makeAdmin(user_id): try: user = User.query.filter_by(user_id=user_id).first() - user.isAdmin = True + user.is_admin = True db.session.add(user) db.session.commit() return user @@ -211,7 +211,7 @@ def load_SuperAdminUser(): "password": str(os.environ.get('SUPER_ADMIN_PASSWORD')), "lms_id": 0, "consent": None, - "owner_id": 0, + "owner_id": None, "role_id": None }) From 5c4e887ff8ae1f48339f6e040b8df852da646559 Mon Sep 17 00:00:00 2001 From: Brennan Cottrell Date: Fri, 29 Dec 2023 10:05:54 -0500 Subject: [PATCH 002/160] MySQL integration works and readme --- BackEndFlask/README.md | 49 +++++++++++++++++----------------- BackEndFlask/dbcreate.py | 34 +++++++++++------------ BackEndFlask/models/schemas.py | 4 +-- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/BackEndFlask/README.md b/BackEndFlask/README.md index 77459e08d..48ac003fd 100644 --- a/BackEndFlask/README.md +++ b/BackEndFlask/README.md @@ -2,6 +2,31 @@ RubricApp is a web application for evaluating students' professional skills, such as teamwork and communication. With RubricApp, instructors can assess teams of students in real-time using [research-based rubrics](http://elipss.com/) or custom rubrics. Instructors can email students their results, as well as download the data for analysis. RubricApp is the software behind ELIPSS SkillBuilder. +#### MySQL Setup + +First, install MySQL-Server + +``` +sudo apt install mysql-server +``` + +Second, you need to set the MySQL password. To do this, first run the command: +``` +sudo mysql -u root +``` + +This will place you in the MySQL terminal. Use these commands to create an account named skillbuilder and set the passowrd to "WasPogil1#" (this should be changed for deployment) + +``` +CREATE USER 'skillbuilder'@'localhost' IDENTIFIED BY 'WasPogil1#'; +GRANT ALL PRIVILEGES ON *.* TO 'skillbuilder'@'localhost'; +FLUSH PRIVILEGES; +exit; +``` + +Once this is done, you use `setupEnv.py` as normal to create the database. If any reason you want to access the database directly, run `mysql -u skillbuilder -p` and then type the password. + + ## Setting up the BackEnd environment Requires python3 and pip3. If you do not have these installed, install python3 and pip3. @@ -29,37 +54,13 @@ python3 run.py This command will allow you to skip past installing/updating all the requirements. -#### Using a different location for the database - -If for some reason you want the sqlite database file and users directory to live somewhere else, replace \`pwd\` in the above commands with the desired path. Just make sure that the paths for both commands are the same. - #### Adding redis You need to run sudo-apt install redis-server modification to appended only service can be done looking at AOF at: https://redis.io/docs/management/persistence/ -#### MySQL Setup - -First, install MySQL-Server - -``` -sudo apt install mysql-server -``` - -Second, you need to set the MySQL password. To do this, first run the command: -``` -sudo mysql -u root -``` - -This will place you in the MySQL terminal. Once there use this command to set the passowrd to "skillbuilder" (this should be changed for deployment) - -CREATE USER 'skillbuilder'@'localhost' IDENTIFIED BY 'WasPogil1#'; -GRANT ALL PRIVILEGES ON *.* TO 'skillbuilder'@'localhost'; -FLUSH PRIVILEGES; -exit; -mysql -p skillbuilder -p diff --git a/BackEndFlask/dbcreate.py b/BackEndFlask/dbcreate.py index da9e72d51..75fde0bee 100644 --- a/BackEndFlask/dbcreate.py +++ b/BackEndFlask/dbcreate.py @@ -37,43 +37,43 @@ os.abort() print("[dbcreate] successfully created new db") time.sleep(sleepTime) - if(get_rubrics().__len__()==0): + if (get_rubrics().__len__() == 0): print("[dbcreate] attempting to load existing rubrics...") time.sleep(sleepTime) load_existing_rubrics() print("[dbcreate] successfully loaded existing rubrics...") time.sleep(sleepTime) - if(get_categories().__len__()==0): + if (get_categories().__len__() == 0): print("[dbcreate] attempting to load exisiting categories...") time.sleep(sleepTime) load_existing_categories() print("[dbcreate] successfully loaded exisiting categories...") time.sleep(sleepTime) - if(get_observable_characteristics().__len__()==0): + if (get_observable_characteristics().__len__() == 0): print("[dbcreate] attempting to load exisiting observable characteristics...") time.sleep(sleepTime) load_existing_observable_characteristics() print("[dbcreate] successfully loaded existing observable characteristics") time.sleep(sleepTime) - if(get_suggestions().__len__()==0): + if (get_suggestions().__len__() == 0): print("[dbcreate] attempting to load exisiting suggestions...") time.sleep(sleepTime) load_existing_suggestions() print("[dbcreate] successfully loaded existing suggestions") - if(get_roles().__len__()==0): + if (get_roles().__len__() == 0): print("[dbcreate] attempting to load existing roles...") time.sleep(sleepTime) load_existing_roles() print("[dbcreate] successfully loaded existing roles") time.sleep(sleepTime) - if(get_users().__len__()==0): + if (get_users().__len__() == 0): print("[dbcreate] attempting to load SuperAdminUser...") time.sleep(sleepTime) load_SuperAdminUser() print("[dbcreate] successfully loaded SuperAdminUser") time.sleep(sleepTime) - if len(sys.argv) == 2 and sys.argv[1]=="demo": - if(get_users().__len__()==1): + if len(sys.argv) == 2 and sys.argv[1] == "demo": + if (get_users().__len__() == 1): print("[dbcreate] attempting to load demo Admin...") time.sleep(sleepTime) load_demo_admin() @@ -89,13 +89,13 @@ load_demo_student() print("[dbcreate] successfully loaded demo Student") time.sleep(sleepTime) - if(get_courses().__len__()==0): + if (get_courses().__len__() == 0): print("[dbcreate] attempting to load demo Course...") time.sleep(sleepTime) load_demo_course() print("[dbcreate] successfully loaded demo Course") time.sleep(sleepTime) - if(get_user_courses().__len__()==0): + if (get_user_courses().__len__() == 0): print("[dbcreate] attempting to load demo UserCourse Admin...") time.sleep(sleepTime) load_demo_user_course_admin() @@ -111,35 +111,35 @@ load_demo_user_course_student() print("[dbcreate] successfully loaded demo CourseStudent") time.sleep(sleepTime) - if(get_teams().__len__()==0): + if (get_teams().__len__() == 0): print("[dbcreate] attempting to load demo Team...") time.sleep(sleepTime) load_demo_team() print("[dbcreate] successfully loaded demo Team") time.sleep(sleepTime) - if(get_team_users().__len__()==0): + if (get_team_users().__len__() == 0): print("[dbcreate] attempting to load demo TeamUser...") time.sleep(sleepTime) load_demo_team_user() print("[dbcreate] successfully loaded demo TeamUser") time.sleep(sleepTime) - if(get_assessment_tasks().__len__()==0): + if (get_assessment_tasks().__len__() == 0): print("[dbcreate] attempting to load demo AssessmentTask...") time.sleep(sleepTime) load_demo_admin_assessmentTask() print("[dbcreate] successfully loaded demo AssessmentTask") time.sleep(sleepTime) - if(get_completed_assessments().__len__()==0): + if (get_completed_assessments().__len__() == 0): print("[dbcreate] attempting to load demo CompletedAssessment...") time.sleep(sleepTime) load_demo_completed_assessment() print("[dbcreate] successfully loaded demo CompletedAssessment") time.sleep(sleepTime) - if(get_feedback().__len__()==0): + if (get_feedback().__len__() == 0): print("[dbcreate] attempting to load demo Feedback...") time.sleep(sleepTime) load_demo_feedback() print("[dbcreate] successfully loaded demo Feedback") time.sleep(sleepTime) - - print("[dbcreate] exiting...") \ No newline at end of file + + print("[dbcreate] exiting...") diff --git a/BackEndFlask/models/schemas.py b/BackEndFlask/models/schemas.py index 6417834bc..fe39b3c43 100644 --- a/BackEndFlask/models/schemas.py +++ b/BackEndFlask/models/schemas.py @@ -46,14 +46,14 @@ class Rubric(db.Model): __table_args__ = {'sqlite_autoincrement': True} rubric_id = db.Column(db.Integer, primary_key=True) rubric_name = db.Column(db.String(100)) - rubric_description = db.Column(db.String(100), nullable=True) + rubric_description = db.Column(db.Text, nullable=True) owner = db.Column(db.Integer, ForeignKey(User.user_id), nullable=True) class Category(db.Model): __tablename__ = "Category" __table_args__ = {'sqlite_autoincrement': True} category_id = db.Column(db.Integer, primary_key=True) - category_name = db.Column(db.String(30), nullable=False) + category_name = db.Column(db.Text, nullable=False) description = db.Column(db.String(255), nullable=False) rating_json = db.Column(db.JSON, nullable=False) From f2dc3b576e9620cb09871e4f7adb3775d3ff920e Mon Sep 17 00:00:00 2001 From: Brian Lugo <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 09:29:26 -0500 Subject: [PATCH 003/160] Update syscontrol.sh - Removed -n flag/ --- AWS/syscontrol.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AWS/syscontrol.sh b/AWS/syscontrol.sh index f3568c913..d0c99ccc3 100755 --- a/AWS/syscontrol.sh +++ b/AWS/syscontrol.sh @@ -432,7 +432,7 @@ function serve_rubricapp() { log "serving front-end" cd "$PROJ_DIR/FrontEndReact" npm run build - serve -s -l -n tcp://0.0.0.0:3000 build & + serve -s -l tcp://0.0.0.0:3000 build & cd - log "done" From 52cfd97575a1a3925ba1d81f8623bff46dfb071f Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 10:40:12 -0500 Subject: [PATCH 004/160] Updated CharacteristicsAndImprovements.js by removing ResponsiveContainer component raising errors in jest testing and added custom css! Updated ViewAssessmentStatus.js by removing ResponsiveContainer component and adding custom css! Created ReportingDashboard.test.js and added 4 tests! --- .../CharacteristicsAndImprovements.js | 20 +++-- .../ViewAssessmentStatus.js | 25 ++---- .../__tests__/ReportingDashboard.test.js | 90 +++++++++++++++++++ 3 files changed, 107 insertions(+), 28 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js diff --git a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js index bc213e39f..221815a5b 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { BarChart, CartesianGrid, XAxis, YAxis, Bar, ResponsiveContainer, LabelList } from 'recharts'; +import { BarChart, CartesianGrid, XAxis, YAxis, Bar, LabelList } from 'recharts'; import Tabs from '@mui/material/Tabs'; import Tab from '@mui/material/Tab'; @@ -18,7 +18,7 @@ export default function CharacteristicsAndImprovements(props) { } return ( - <> +
{ props.showSuggestions && @@ -26,17 +26,19 @@ export default function CharacteristicsAndImprovements(props) { } - +
- @@ -46,7 +48,7 @@ export default function CharacteristicsAndImprovements(props) { - - +
+
) } \ No newline at end of file diff --git a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js index 32b21975e..e4ba62ccc 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js @@ -3,7 +3,7 @@ import Box from '@mui/material/Box'; import { Container } from '@mui/material'; import Button from '@mui/material/Button'; import Grid from '@mui/material/Grid'; -import { BarChart, CartesianGrid, XAxis, YAxis, Bar, ResponsiveContainer, LabelList } from 'recharts'; +import { BarChart, CartesianGrid, XAxis, YAxis, Bar, LabelList } from 'recharts'; import AssessmentTaskDropdown from '../../../../Components/AssessmentTaskDropdown.js'; import CategoryDropdown from '../../../../Components/CategoryDropdown.js'; import CharacteristicsAndImprovements from './CharacteristicsAndImprovements.js'; @@ -137,9 +137,8 @@ export default function ViewAssessmentStatus(props) { return ( - + - {/* Left quadrant: histogram of assessment task ratings */}
- {/* Right quadrant: dropdowns, histogram, evaluation status */} - {/* Top half of quadrant: 2 dropdowns */} - - {/* Dropdown #1 */}
- {/* Dropdown #2 */}
- {/* Bottom half of quadrant: histogram and evaluation status */} - {/* Histogram: distribution of ratings */} { props.showRatings &&
@@ -189,9 +181,7 @@ export default function ViewAssessmentStatus(props) {
Avg: {avg}; StdDev: {stdev}
- - - + @@ -202,20 +192,17 @@ export default function ViewAssessmentStatus(props) { -
} - - {/* Evaluation status of TAs or students, depending on who is completing the assessment task */} +
{ props.completedByTAs && <>

43% of TA evaluations (43/100) are complete

- @@ -224,7 +211,7 @@ export default function ViewAssessmentStatus(props) { <>

43% of student evaluations (43/100) are complete

- } + }
diff --git a/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js b/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js new file mode 100644 index 000000000..2a988025b --- /dev/null +++ b/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js @@ -0,0 +1,90 @@ +import { render, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import Login from '../../../../Login/Login.js'; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + clickFirstElementWithAriaLabel +} from '../../../../../testUtilities.js'; + +import { + demoAdminPassword, +} from '../../../../../App.js'; + + + +var lf = "loginForm"; +var lb = "loginButton"; +var ei = 'emailInput'; +var pi = 'passwordInput'; +var ct = 'coursesTitle'; +var vcib = "viewCourseIconButton"; +var rt = "rosterTitle"; +var rpt = "reportingTab"; +var vasb = "viewAssessmentStatusBox"; + + + +test("NOTE: Tests ?-? will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test("ReportingDashboard.test.js Test 1: Should render Login Form component", () => { + render(); + + expectElementWithAriaLabelToBeInDocument(lf); +}); + + +test("ReportingDashboard.test.js Test 2: Should show Admin View Courses when logging with Admin credentials", async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); +}); + + +test("ReportingDashboard.test.js Test 3: Should show Roster Dashboard when clicking the view course button icon", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); +}); + + +test("ReportingDashboard.test.js Test 4: Should show Reporting Dashboard when clicking the reporting tab", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(rpt); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + }); +}); \ No newline at end of file From 1b4331299109b3f4107a41894bbd3cd0a4a1f34e Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 11:40:45 -0500 Subject: [PATCH 005/160] Updated ReportTabs.js by adding aria labels! Updated ReportingDashboard.test.js by adding 5 jest tests! Updated CharacteristicsAndImprovements.js by adding aria labels! Updated AdminViewRatings.js by adding aria label! --- .../View/Admin/View/Reporting/ReportTabs.js | 4 + .../CharacteristicsAndImprovements.js | 5 +- .../Reporting/ViewRatings/AdminViewRatings.js | 2 +- .../__tests__/ReportingDashboard.test.js | 160 +++++++++++++++++- 4 files changed, 167 insertions(+), 4 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/Reporting/ReportTabs.js b/FrontEndReact/src/View/Admin/View/Reporting/ReportTabs.js index d680dd7c5..4149d028a 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/ReportTabs.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/ReportTabs.js @@ -21,6 +21,8 @@ export default function TabManager (props) { onClick={() => { props.setTab("Assessment Status"); }} + + aria-label='assessmentStatusTab' /> { props.setTab("Ratings and Feedback"); }} + + aria-label='ratingAndFeedbackTab' /> {/* wip */} diff --git a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js index 221815a5b..d660fa5e7 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/CharacteristicsAndImprovements.js @@ -20,9 +20,9 @@ export default function CharacteristicsAndImprovements(props) { return (
- + { props.showSuggestions && - + } @@ -32,6 +32,7 @@ export default function CharacteristicsAndImprovements(props) { data={tabId === 0 ? props.characteristicsData["characteristics"] : props.improvementsData["improvements"]} width={750} height={250} + aria-label={tabId === 0 ? "barChartCharacteristicsData" : "barChartImprovementsData"} > diff --git a/FrontEndReact/src/View/Admin/View/Reporting/ViewRatings/AdminViewRatings.js b/FrontEndReact/src/View/Admin/View/Reporting/ViewRatings/AdminViewRatings.js index d7227193c..80ad18b3e 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/ViewRatings/AdminViewRatings.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/ViewRatings/AdminViewRatings.js @@ -89,7 +89,7 @@ class AdminViewRatings extends Component { } else { return( <> - + { +test("ReportingDashboard.test.js Test 4: Should show Assessment Status page when clicking the reporting tab", async () => { render(); await waitFor(() => { @@ -87,4 +95,154 @@ test("ReportingDashboard.test.js Test 4: Should show Reporting Dashboard when cl await waitFor(() => { expectElementWithAriaLabelToBeInDocument(vasb); }); +}); + + +test("ReportingDashboard.test.js Test 5: Should show Roster Dashboard when clicking the back button", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + + clickFirstElementWithAriaLabel(vcib); + }); + + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + + clickElementWithAriaLabel(rpt); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + + clickElementWithAriaLabel(mhbb); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); + + +test("ReportingDashboard.test.js Test 6: Should show Ratings and Feedback page when clicking the ratings and feedback tab", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(rpt); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + + clickElementWithAriaLabel(raft); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(avrb); + }); +}); + + +test("ReportingDashboard.test.js Test 7: Should show Assessment Status page when clicking the assessment status tab after clicking the ratings and feedback tab", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(rpt); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + + clickElementWithAriaLabel(raft); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(avrb); + + clickElementWithAriaLabel(ast); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + }); +}); + + +test("ReportingDashboard.test.js Test 8: Should show barchart with improvements data when clicking the improvements tab", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(rpt); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + + clickElementWithAriaLabel(caiit); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(bcid); + }); +}); + + +test("ReportingDashboard.test.js Test 9: Should show barchart with characteristics data when clicking the characteristics tab after clicking the improvements tab", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(rpt); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(vasb); + + clickElementWithAriaLabel(caiit); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(bcid); + + clickElementWithAriaLabel(caict); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(bccd); + }); }); \ No newline at end of file From 403fee9ff86ac554c01e105dcb029fb4160ffc98 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 11:42:19 -0500 Subject: [PATCH 006/160] Updated ReportingDashboard.test.js by updating the notes for the jest test! --- .../Admin/View/Reporting/__tests__/ReportingDashboard.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js b/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js index bb7822250..92ab6c6a7 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js @@ -35,7 +35,7 @@ var bccd = "barChartCharacteristicsData"; -test("NOTE: Tests ?-? will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-9 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); From 18de6048a3978dcc61616f13fef0f813e5916af1 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 12:57:50 -0500 Subject: [PATCH 007/160] Updated ReportingDashboard.test.js by converting single to double quotes! --- .../__tests__/ReportingDashboard.test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js b/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js index 92ab6c6a7..95af63587 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/__tests__/ReportingDashboard.test.js @@ -1,25 +1,25 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword, -} from '../../../../../App.js'; +} from "../../../../../App.js"; var lf = "loginForm"; var lb = "loginButton"; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var rt = "rosterTitle"; var rpt = "reportingTab"; From c867b6ee1dc60a26ddb94af7c3bfcc587e0baeea Mon Sep 17 00:00:00 2001 From: aparriaran Date: Wed, 8 May 2024 15:41:07 -0500 Subject: [PATCH 008/160] Made 4 tests for admin add user --- .../View/Admin/Add/AddUsers/AdminAddUser.js | 11 +- .../AddUsers/__tests__/AdminAddUser.test.js | 158 ++++++++++++++++++ 2 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 07ab43c93..06ddc503a 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -215,9 +215,9 @@ class AdminAddUser extends Component { - + - {editUser ? "Edit User" : "Add User"} + {editUser ? "Edit User" : "Add User"} { !navbar.props.isSuperAdmin && state.user !== null && state.addUser === false && @@ -240,6 +240,7 @@ class AdminAddUser extends Component { onChange={this.handleChange} required sx={{mb: 3}} + aria-label="userFirstNameInput" /> { !navbar.props.isSuperAdmin && @@ -306,11 +309,11 @@ class AdminAddUser extends Component { /> - - diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js new file mode 100644 index 000000000..93e725433 --- /dev/null +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -0,0 +1,158 @@ +import { render, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import Login from '../../../../Login/Login.js'; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + expectElementWithAriaLabelToHaveErrorMessage, + clickFirstElementWithAriaLabel +} from '../../../../../testUtilities.js'; + +import { + demoAdminPassword +} from '../../../../../App.js'; + + + +var lb = 'loginButton'; +var ei = 'emailInput'; +var pi = 'passwordInput'; +var aub = "addUserButton"; +var aut = "addUserTitle"; +var ct = "coursesTitle"; +var vcib = "viewCourseIconButton"; +var rt = "rosterTitle"; +var mhbb = "mainHeaderBackButton"; +var caub = "cancelAddUserButton"; +var aosaub = "addOrSaveAddUserButton"; +var auf = "addUserForm"; +var ufni = "userFirstNameInput"; +var ulni = "userLastNameInput"; +var ueai = "userEmailAddressInput"; +var aurd= "addUserRoleDropdown"; + + + + +test("NOTE: Tests ?-? will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + +test('AdminAddUser.test.js Test 1: Should render the AdminAddUser component given the Add User button is clicked', async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(aub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aut); + }); +}); + + +test('AdminAddUser.test.js Test 2: Should render the roster dashboard if the back button on the Add User page is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(aub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aut); + }); + + clickElementWithAriaLabel(mhbb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); + +test('AdminAddUser.test.js Test 3: Should render the roster dashboard if the cancel button on the Add User page is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(aub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aut); + }); + + clickElementWithAriaLabel(caub); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); +test('AdminAddUser.test.js Test 4: HelperText errors should show for each text field when no information is filled', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(aub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aut); + }); + + clickElementWithAriaLabel(aosaub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + expectElementWithAriaLabelToHaveErrorMessage(ufni, "First name cannot be empty"); + + expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last name cannot be empty"); + + expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email Address cannot be empty"); + + clickElementWithAriaLabel(aurd); + + }); +}); \ No newline at end of file From 0d34a7323f6b1da9e16c3f34af31fda3f16f5ef7 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 15:49:36 -0500 Subject: [PATCH 009/160] Updated AdminAddUser.test.js by adding whitespace! --- .../View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 93e725433..3fd730340 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -40,6 +40,7 @@ test("NOTE: Tests ?-? will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); + test('AdminAddUser.test.js Test 1: Should render the AdminAddUser component given the Add User button is clicked', async () => { render(); @@ -95,6 +96,7 @@ test('AdminAddUser.test.js Test 2: Should render the roster dashboard if the bac }); }); + test('AdminAddUser.test.js Test 3: Should render the roster dashboard if the cancel button on the Add User page is clicked', async () => { render(); @@ -122,6 +124,8 @@ test('AdminAddUser.test.js Test 3: Should render the roster dashboard if the can }, 3000); }); }); + + test('AdminAddUser.test.js Test 4: HelperText errors should show for each text field when no information is filled', async () => { render(); From f1a5443c46fe2fed2f770647692a2367e1032b10 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 15:50:06 -0500 Subject: [PATCH 010/160] Updated AdminAddUser.test.js by adding whitespace! --- .../src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 3fd730340..b4caefb24 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -35,7 +35,6 @@ var aurd= "addUserRoleDropdown"; - test("NOTE: Tests ?-? will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); From 1bbe686172160fc39be72650062150b37a9bf729 Mon Sep 17 00:00:00 2001 From: rtkells Date: Wed, 8 May 2024 16:04:23 -0500 Subject: [PATCH 011/160] Updated TextFields in AdminAddUser.js to have helperText errors in the case of incorrect input --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 06ddc503a..5b16ad004 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -237,6 +237,7 @@ class AdminAddUser extends Component { fullWidth value={firstName} error={!!errors.firstName} + helperText={errors.firstName} onChange={this.handleChange} required sx={{mb: 3}} @@ -251,6 +252,7 @@ class AdminAddUser extends Component { fullWidth value={lastName} error={!!errors.lastName} + helperText={errors.lastName} onChange={this.handleChange} required sx={{mb: 3}} @@ -265,6 +267,7 @@ class AdminAddUser extends Component { fullWidth value={email} error={!!errors.email} + helperText={errors.email} onChange={this.handleChange} required sx={{mb: 3}} @@ -304,6 +307,7 @@ class AdminAddUser extends Component { fullWidth value={lmsId} error={!!errors.lmsId} + helperText={errors.lmsId} onChange={this.handleChange} sx={{mb: 3}} /> From cf791b1321188942986c8a8eaa99c277855d296b Mon Sep 17 00:00:00 2001 From: rtkells Date: Wed, 8 May 2024 16:14:19 -0500 Subject: [PATCH 012/160] Dealt with HelperText error message being awkwardly formatted --- .../src/View/Admin/Add/AddUsers/AdminAddUser.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 5b16ad004..708e4e07e 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -78,11 +78,23 @@ class AdminAddUser extends Component { handleChange = (e) => { const { id, value, name } = e.target; + var formatString = ""; + + for (let i = 0; i < id.length; i++) { + if (i === 0) { + formatString += id.charAt(0).toUpperCase(); + } else if (id.charAt(i) === id.charAt(i).toUpperCase()) { + formatString += (" " + id.charAt(i).toLowerCase()); + } else { + formatString += id.charAt(i); + } + } + this.setState({ [id]: value, errors: { ...this.state.errors, - [id]: value.trim() === '' ? `${name.charAt(0).toUpperCase() + name.slice(1)} cannot be empty` : '', + [id]: value.trim() === '' ? `${formatString} cannot be empty` : '', }, }); }; From be6a26f1975b4300d7ae574274201dbbc0b9a438 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Wed, 8 May 2024 16:20:22 -0500 Subject: [PATCH 013/160] Updated AdminAddUser.test.js by removing address! --- .../src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index b4caefb24..8c5312b32 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -153,7 +153,7 @@ test('AdminAddUser.test.js Test 4: HelperText errors should show for each text f expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last name cannot be empty"); - expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email Address cannot be empty"); + expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email cannot be empty"); clickElementWithAriaLabel(aurd); From f2db8b01c614563982e9e789460018b65267ed68 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Wed, 8 May 2024 16:21:37 -0500 Subject: [PATCH 014/160] Updated AdminAddUser.test.js by changing name to Name! --- .../View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 8c5312b32..118c87524 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -149,13 +149,12 @@ test('AdminAddUser.test.js Test 4: HelperText errors should show for each text f await waitFor(() => { expectElementWithAriaLabelToBeInDocument(auf); - expectElementWithAriaLabelToHaveErrorMessage(ufni, "First name cannot be empty"); + expectElementWithAriaLabelToHaveErrorMessage(ufni, "First Name cannot be empty"); - expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last name cannot be empty"); + expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last Name cannot be empty"); expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email cannot be empty"); clickElementWithAriaLabel(aurd); - }); }); \ No newline at end of file From d38365e23af050548ef2545ddb8cbe3b450bf72e Mon Sep 17 00:00:00 2001 From: aparriaran Date: Wed, 8 May 2024 16:22:52 -0500 Subject: [PATCH 015/160] Updated AdminAddUser.js by removing unused variable name! --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 708e4e07e..1f03ccb6b 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -76,7 +76,7 @@ class AdminAddUser extends Component { } handleChange = (e) => { - const { id, value, name } = e.target; + const { id, value } = e.target; var formatString = ""; From b757e2e7fafd47b9a4bf1ae940710b0835338dd6 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Wed, 8 May 2024 16:25:00 -0500 Subject: [PATCH 016/160] Updated AdminAddUser.js by removing whitespace! --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 1f03ccb6b..3208fc98b 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -127,7 +127,7 @@ class AdminAddUser extends Component { errors: { firstName: firstName.trim() === '' ? 'First Name cannot be empty' : '', lastName: lastName.trim() === '' ? 'Last Name cannot be empty' : '', - email: email.trim() === '' ? ' Email cannot be empty' : '', + email: email.trim() === '' ? 'Email cannot be empty' : '', }, }); From b30eb497095eba6ed6b64e9941d072f9dffcbcbb Mon Sep 17 00:00:00 2001 From: rtkells Date: Wed, 8 May 2024 16:52:22 -0500 Subject: [PATCH 017/160] Added more precise invalid input handling for AdminAddCourse.js --- .../View/Admin/Add/AddUsers/AdminAddUser.js | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 3208fc98b..8e4dd8481 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -121,8 +121,13 @@ class AdminAddUser extends Component { var confirmCreateResource = navbar.confirmCreateResource; var chosenCourse = state.chosenCourse; - if (firstName.trim() === '' || lastName.trim() === '' || email.trim() === ''|| (!navbar.props.isSuperAdmin && role === '')) { + var newErrors = { + "firstName": "", + "lastName": "", + "email": "" + }; +<<<<<<< Updated upstream this.setState({ errors: { firstName: firstName.trim() === '' ? 'First Name cannot be empty' : '', @@ -130,45 +135,57 @@ class AdminAddUser extends Component { email: email.trim() === '' ? 'Email cannot be empty' : '', }, }); +======= + if (firstName.trim() === '') + newErrors["firstName"] = "First name cannot be empty"; - } else if (!validator.isEmail(email)) { + if (lastName.trim() === '') + newErrors["lastName"] = "Last name cannot be empty"; + + if (email.trim() === '') + newErrors["email"] = "Email cannot be empty"; +>>>>>>> Stashed changes + + if (!validator.isEmail(email) && newErrors["email"] === '') + newErrors["email"] = "Please enter a valid email address"; + + if (newErrors["firstName"] !== '' || newErrors["lastName"] !== '' || newErrors["email"] !== '') { this.setState({ - errors: { - ...this.state.errors, - email: 'Please enter a valid email address', - }, + errors: newErrors }); - } else { - const cookies = new Cookies(); - - var body = JSON.stringify({ - "first_name": firstName, - "last_name": lastName, - "email": email, - "lms_id": lmsId, - "consent": null, - "owner_id": cookies.get('user')['user_id'], - "role_id": navbar.props.isSuperAdmin ? 3 : role - }); + return; + } - if(user === null && addUser === false) { - if(navbar.props.isSuperAdmin) { - genericResourcePOST(`/user`, this, body); + const cookies = new Cookies(); - } else { - genericResourcePOST(`/user?course_id=${chosenCourse["course_id"]}`, this, body); - } + var body = JSON.stringify({ + "first_name": firstName, + "last_name": lastName, + "email": email, + "lms_id": lmsId, + "consent": null, + "owner_id": cookies.get('user')['user_id'], + "role_id": navbar.props.isSuperAdmin ? 3 : role + }); - } else if (user === null && addUser === true && navbar.props.isSuperAdmin) { + if(user === null && addUser === false) { + if(navbar.props.isSuperAdmin) { genericResourcePOST(`/user`, this, body); } else { - genericResourcePUT(`/user?uid=${user["user_id"]}&course_id=${chosenCourse["course_id"]}`, this, body); + genericResourcePOST(`/user?course_id=${chosenCourse["course_id"]}`, this, body); } - confirmCreateResource("User"); + } else if (user === null && addUser === true && navbar.props.isSuperAdmin) { + genericResourcePOST(`/user`, this, body); + + } else { + genericResourcePUT(`/user?uid=${user["user_id"]}&course_id=${chosenCourse["course_id"]}`, this, body); } + + confirmCreateResource("User"); + } hasErrors = () => { @@ -297,6 +314,7 @@ class AdminAddUser extends Component { label="Role" defaultValue="test" error={!!errors.role} + helperText={errors.role} onChange={this.handleSelect} required sx={{mb: 3}} From 14b07914a416ed3c93dd9f9a4e3d519407ac3bff Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 9 May 2024 09:27:38 -0500 Subject: [PATCH 018/160] Fixed error in AdminAddUser.js --- .../src/View/Admin/Add/AddUsers/AdminAddUser.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 8e4dd8481..7a11c6fc7 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -127,15 +127,6 @@ class AdminAddUser extends Component { "email": "" }; -<<<<<<< Updated upstream - this.setState({ - errors: { - firstName: firstName.trim() === '' ? 'First Name cannot be empty' : '', - lastName: lastName.trim() === '' ? 'Last Name cannot be empty' : '', - email: email.trim() === '' ? 'Email cannot be empty' : '', - }, - }); -======= if (firstName.trim() === '') newErrors["firstName"] = "First name cannot be empty"; @@ -144,7 +135,6 @@ class AdminAddUser extends Component { if (email.trim() === '') newErrors["email"] = "Email cannot be empty"; ->>>>>>> Stashed changes if (!validator.isEmail(email) && newErrors["email"] === '') newErrors["email"] = "Please enter a valid email address"; From 9cc12fd39e65555ba1ab5c8737bf45552eef9026 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Wed, 8 May 2024 17:03:28 -0500 Subject: [PATCH 019/160] Created the 5th test case for AdminAddUser --- .../AddUsers/__tests__/AdminAddUser.test.js | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 118c87524..18495454c 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -31,7 +31,7 @@ var auf = "addUserForm"; var ufni = "userFirstNameInput"; var ulni = "userLastNameInput"; var ueai = "userEmailAddressInput"; -var aurd= "addUserRoleDropdown"; +var aurdd= "addUserRoleDropDown"; @@ -155,6 +155,32 @@ test('AdminAddUser.test.js Test 4: HelperText errors should show for each text f expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email cannot be empty"); - clickElementWithAriaLabel(aurd); }); -}); \ No newline at end of file +}); + +test('AdminAddUser.test.js Test 5: HelperText error should show for the firstName text field when it is left blank while all other information is filled', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + changeElementWithAriaLabelWithInput(ulni, "Anderson"); + changeElementWithAriaLabelWithInput(ueai,"ebanderson@mail.lipscomb.edu"); + + clickElementWithAriaLabel(aub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + expectElementWithAriaLabelToHaveErrorMessage(ufni, "First Name cannot be empty"); + + }); + +}); From befa0f40719447ce57ef384a19b37d1cfa7696ff Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 17:13:02 -0500 Subject: [PATCH 020/160] Updated AdminAddUser.js by changing name to Name! Updated AdminAddUser.test.js by removing whitespace and moving some of the function calls inside the waitfor! --- .../src/View/Admin/Add/AddUsers/AdminAddUser.js | 4 ++-- .../Add/AddUsers/__tests__/AdminAddUser.test.js | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 7a11c6fc7..eacf18657 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -128,10 +128,10 @@ class AdminAddUser extends Component { }; if (firstName.trim() === '') - newErrors["firstName"] = "First name cannot be empty"; + newErrors["firstName"] = "First Name cannot be empty"; if (lastName.trim() === '') - newErrors["lastName"] = "Last name cannot be empty"; + newErrors["lastName"] = "Last Name cannot be empty"; if (email.trim() === '') newErrors["email"] = "Email cannot be empty"; diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 18495454c..dee3e687a 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -154,7 +154,6 @@ test('AdminAddUser.test.js Test 4: HelperText errors should show for each text f expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last Name cannot be empty"); expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email cannot be empty"); - }); }); @@ -169,18 +168,17 @@ test('AdminAddUser.test.js Test 5: HelperText error should show for the firstNam await waitFor(() => { expectElementWithAriaLabelToBeInDocument(rt); - }); - changeElementWithAriaLabelWithInput(ulni, "Anderson"); - changeElementWithAriaLabelWithInput(ueai,"ebanderson@mail.lipscomb.edu"); + changeElementWithAriaLabelWithInput(ulni, "Anderson"); - clickElementWithAriaLabel(aub); + changeElementWithAriaLabelWithInput(ueai,"ebanderson@mail.lipscomb.edu"); + + clickElementWithAriaLabel(aub); + }); await waitFor(() => { expectElementWithAriaLabelToBeInDocument(auf); expectElementWithAriaLabelToHaveErrorMessage(ufni, "First Name cannot be empty"); - }); - }); From 99d1ca9e06f6b5a002b311381aabd30288541756 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Thu, 9 May 2024 09:26:36 -0500 Subject: [PATCH 021/160] created variables for the dropdown options. --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 7 ++++--- .../View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index eacf18657..b1f7a312a 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -308,11 +308,12 @@ class AdminAddUser extends Component { onChange={this.handleSelect} required sx={{mb: 3}} + aria-label ="addUserRoleDropDown" > - Student - - TA/Instructor + Student + + TA/Instructor {/* Admin */} diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index dee3e687a..5ca7bee95 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -32,6 +32,8 @@ var ufni = "userFirstNameInput"; var ulni = "userLastNameInput"; var ueai = "userEmailAddressInput"; var aurdd= "addUserRoleDropDown"; +var aurddso = "addUserRoleDropDownStudentOption"; +var aurddtoio= "addUserRoleDropDownTAOrInstructorOption"; From d8d8ff9410bca05f4282b8679ffeecd9a8c8ebe6 Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 9 May 2024 09:42:54 -0500 Subject: [PATCH 022/160] Fixed Test case: 5 in AdminAddUser.test.js --- .../View/Admin/Add/AddUsers/AdminAddUser.js | 12 +++++----- .../AddUsers/__tests__/AdminAddUser.test.js | 22 +++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index b1f7a312a..cd7e65787 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -128,10 +128,10 @@ class AdminAddUser extends Component { }; if (firstName.trim() === '') - newErrors["firstName"] = "First Name cannot be empty"; + newErrors["firstName"] = "First name cannot be empty"; if (lastName.trim() === '') - newErrors["lastName"] = "Last Name cannot be empty"; + newErrors["lastName"] = "Last name cannot be empty"; if (email.trim() === '') newErrors["email"] = "Email cannot be empty"; @@ -308,12 +308,12 @@ class AdminAddUser extends Component { onChange={this.handleSelect} required sx={{mb: 3}} - aria-label ="addUserRoleDropDown" + aria-label="addUserRoleDropDown" > - Student - - TA/Instructor + Student + + TA/Instructor {/* Admin */} diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 5ca7bee95..b1c75cfd0 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -151,9 +151,9 @@ test('AdminAddUser.test.js Test 4: HelperText errors should show for each text f await waitFor(() => { expectElementWithAriaLabelToBeInDocument(auf); - expectElementWithAriaLabelToHaveErrorMessage(ufni, "First Name cannot be empty"); + expectElementWithAriaLabelToHaveErrorMessage(ufni, "First name cannot be empty"); - expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last Name cannot be empty"); + expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last name cannot be empty"); expectElementWithAriaLabelToHaveErrorMessage(ueai, "Email cannot be empty"); }); @@ -169,18 +169,22 @@ test('AdminAddUser.test.js Test 5: HelperText error should show for the firstNam clickFirstElementWithAriaLabel(vcib); await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(rt); + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); - changeElementWithAriaLabelWithInput(ulni, "Anderson"); + changeElementWithAriaLabelWithInput(ulni, "Anderson"); - changeElementWithAriaLabelWithInput(ueai,"ebanderson@mail.lipscomb.edu"); + changeElementWithAriaLabelWithInput(ueai,"ebanderson@mail.lipscomb.edu"); - clickElementWithAriaLabel(aub); + clickElementWithAriaLabel(aub); + }, 3000); }); await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(auf); - - expectElementWithAriaLabelToHaveErrorMessage(ufni, "First Name cannot be empty"); + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + expectElementWithAriaLabelToHaveErrorMessage(ufni, "First name cannot be empty"); + }, 3000); }); }); From 87c2c49f10fbda687c9ee240b9354d1823eada44 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Thu, 9 May 2024 10:48:54 -0500 Subject: [PATCH 023/160] Added more test cases for AdminAddUser.test.js --- .../View/Admin/Add/AddUsers/AdminAddUser.js | 4 +- .../AddUsers/__tests__/AdminAddUser.test.js | 157 ++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index cd7e65787..194e514af 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -311,9 +311,9 @@ class AdminAddUser extends Component { aria-label="addUserRoleDropDown" > - Student + Student - TA/Instructor + TA/Instructor {/* Admin */} diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index b1c75cfd0..75d5b6e32 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -188,3 +188,160 @@ test('AdminAddUser.test.js Test 5: HelperText error should show for the firstNam }, 3000); }); }); + +test('AdminAddUser.test.js Test 6: HelperText error should show for the LastName text field when it is left blank while all other information is filled', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + + changeElementWithAriaLabelWithInput(ufni, "Elliot"); + + changeElementWithAriaLabelWithInput(ueai,"ebanderson@mail.lipscomb.edu"); + + clickElementWithAriaLabel(aub); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + expectElementWithAriaLabelToHaveErrorMessage(ulni, "Last name cannot be empty"); + }, 3000); + }); +}); + +test('AdminAddUser.test.js Test 7: HelperText error should show for the Email Address text field when it is left blank while all other information is filled', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + + changeElementWithAriaLabelWithInput(ufni, "Elliot"); + + changeElementWithAriaLabelWithInput(ulni,"Anderson"); + + clickElementWithAriaLabel(aub); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + changeElementWithAriaLabelWithInput(ueai,"Email cannot be empty"); + }, 3000); + }); +}); + +test('AdminAddUser.test.js Test 8: Should render all options for the dropdown menu when clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(aub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aut); + }); + + clickElementWithAriaLabel(aurdd); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aurddso); + + expectElementWithAriaLabelToBeInDocument(aurddtoio); + }, 3000); + }); +}); + +test('AdminAddUser.test.js Test 9: HelperText error should show for the Email Address text field when the input is invalid', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + + changeElementWithAriaLabelWithInput(ufni, "Elliot"); + + changeElementWithAriaLabelWithInput(ulni,"Anderson"); + + changeElementWithAriaLabelWithInput(ueai, "ebanderson") + + clickElementWithAriaLabel(aub); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + changeElementWithAriaLabelWithInput(ueai,"Please enter a valid email address"); + }, 3000); + }); +}); + +test('AdminAddUser.test.js Test 10: Filling in valid input and clicking the Add User button should redirect you to the roster dashboard page, and should contain the new user you just added', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + + var userFirstName = "Elliot"; + + changeElementWithAriaLabelWithInput(ufni,userFirstName); + + changeElementWithAriaLabelWithInput(ulni,"Anderson"); + + changeElementWithAriaLabelWithInput(ueai, "ebanderson@mail.lipscomb.edu"); + + clickElementWithAriaLabel(aub); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(auf); + + changeElementWithAriaLabelWithInput(ueai,"Please enter a valid email address"); + }, 3000); + }); +}); \ No newline at end of file From 2eb3c881254a78d0125e55a03a88df952f5737d9 Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 9 May 2024 10:52:09 -0500 Subject: [PATCH 024/160] Added requirement for role to be filled to AdminAddUser.js --- .../src/View/Admin/Add/AddUsers/AdminAddUser.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 194e514af..f9adcd7c3 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -124,7 +124,8 @@ class AdminAddUser extends Component { var newErrors = { "firstName": "", "lastName": "", - "email": "" + "email": "", + "role": "" }; if (firstName.trim() === '') @@ -136,10 +137,14 @@ class AdminAddUser extends Component { if (email.trim() === '') newErrors["email"] = "Email cannot be empty"; + if (role.trim() === '') { + newErrors["role"] = "Role cannot be empty"; + } + if (!validator.isEmail(email) && newErrors["email"] === '') newErrors["email"] = "Please enter a valid email address"; - if (newErrors["firstName"] !== '' || newErrors["lastName"] !== '' || newErrors["email"] !== '') { + if (newErrors["firstName"] !== '' || newErrors["lastName"] !== '' || newErrors["email"] !== '' || newErrors["role"] !== '') { this.setState({ errors: newErrors }); From 44d8f47bd77ef7bae25d2ecafa9067bfc1ec6bec Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 9 May 2024 10:58:11 -0500 Subject: [PATCH 025/160] Fixed AdminAddUser.js again --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index f9adcd7c3..b2cdbb441 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -137,7 +137,7 @@ class AdminAddUser extends Component { if (email.trim() === '') newErrors["email"] = "Email cannot be empty"; - if (role.trim() === '') { + if (role === '') { newErrors["role"] = "Role cannot be empty"; } From d872e9b70fba65bbd3d0d4d1b5bfe15aa74993be Mon Sep 17 00:00:00 2001 From: aparriaran Date: Thu, 9 May 2024 13:20:51 -0500 Subject: [PATCH 026/160] Finished creating the 10 test cases for AdminAddUser. --- .../AddUsers/__tests__/AdminAddUser.test.js | 36 +------------------ 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index 75d5b6e32..a79898335 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -37,7 +37,7 @@ var aurddtoio= "addUserRoleDropDownTAOrInstructorOption"; -test("NOTE: Tests ?-? will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-9 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); @@ -311,37 +311,3 @@ test('AdminAddUser.test.js Test 9: HelperText error should show for the Email Ad }, 3000); }); }); - -test('AdminAddUser.test.js Test 10: Filling in valid input and clicking the Add User button should redirect you to the roster dashboard page, and should contain the new user you just added', async () => { - render(); - - await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(ct); - }); - - clickFirstElementWithAriaLabel(vcib); - - await waitFor(() => { - setTimeout(() => { - expectElementWithAriaLabelToBeInDocument(rt); - - var userFirstName = "Elliot"; - - changeElementWithAriaLabelWithInput(ufni,userFirstName); - - changeElementWithAriaLabelWithInput(ulni,"Anderson"); - - changeElementWithAriaLabelWithInput(ueai, "ebanderson@mail.lipscomb.edu"); - - clickElementWithAriaLabel(aub); - }, 3000); - }); - - await waitFor(() => { - setTimeout(() => { - expectElementWithAriaLabelToBeInDocument(auf); - - changeElementWithAriaLabelWithInput(ueai,"Please enter a valid email address"); - }, 3000); - }); -}); \ No newline at end of file From 62ef30216ca2cbb5efa562eeb193a1efd815fa64 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 13:50:49 -0500 Subject: [PATCH 027/160] Updated AdminAddUser.js by adding the FormHelperText below the Select component to have the helperText when an error is raised that the role is empty! --- .../src/View/Admin/Add/AddUsers/AdminAddUser.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index b2cdbb441..8075d0700 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -6,6 +6,7 @@ import ResponsiveDialog from '../../../Components/DropConfirmation.js'; import { genericResourcePOST, genericResourcePUT } from '../../../../utility.js'; import { Box, Button, FormControl, Typography, TextField, MenuItem, InputLabel, Select} from '@mui/material'; import Cookies from 'universal-cookie'; +import FormHelperText from '@mui/material/FormHelperText'; @@ -309,19 +310,19 @@ class AdminAddUser extends Component { label="Role" defaultValue="test" error={!!errors.role} - helperText={errors.role} onChange={this.handleSelect} required sx={{mb: 3}} aria-label="addUserRoleDropDown" > + Student - Student + TA/Instructor - TA/Instructor - - {/* Admin */} + {/* Admin */} + + {errors.role} } From 664498c2971831453494e2cfb84ec6e9a7ac44ad Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 13:53:03 -0500 Subject: [PATCH 028/160] Updated AdminAddUser.js by removing whitespace! --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 8075d0700..404cab64b 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -181,7 +181,6 @@ class AdminAddUser extends Component { } confirmCreateResource("User"); - } hasErrors = () => { From c281dd5866aad401f167e138d14900bbf29ba83d Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 13:54:59 -0500 Subject: [PATCH 029/160] Updated AdminAddUser.test.js by converting all single to double quotes! --- .../AddUsers/__tests__/AdminAddUser.test.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js index a79898335..ad2cd77b0 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminAddUser.test.js @@ -1,6 +1,6 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, @@ -8,17 +8,17 @@ import { changeElementWithAriaLabelWithInput, expectElementWithAriaLabelToHaveErrorMessage, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword -} from '../../../../../App.js'; +} from "../../../../../App.js"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; var aub = "addUserButton"; var aut = "addUserTitle"; var ct = "coursesTitle"; @@ -42,7 +42,7 @@ test("NOTE: Tests 1-9 will not pass if Demo Data is not loaded!", () => { }); -test('AdminAddUser.test.js Test 1: Should render the AdminAddUser component given the Add User button is clicked', async () => { +test("AdminAddUser.test.js Test 1: Should render the AdminAddUser component given the Add User button is clicked", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -69,7 +69,7 @@ test('AdminAddUser.test.js Test 1: Should render the AdminAddUser component give }); -test('AdminAddUser.test.js Test 2: Should render the roster dashboard if the back button on the Add User page is clicked', async () => { +test("AdminAddUser.test.js Test 2: Should render the roster dashboard if the back button on the Add User page is clicked", async () => { render(); await waitFor(() => { @@ -98,7 +98,7 @@ test('AdminAddUser.test.js Test 2: Should render the roster dashboard if the bac }); -test('AdminAddUser.test.js Test 3: Should render the roster dashboard if the cancel button on the Add User page is clicked', async () => { +test("AdminAddUser.test.js Test 3: Should render the roster dashboard if the cancel button on the Add User page is clicked", async () => { render(); await waitFor(() => { @@ -127,7 +127,7 @@ test('AdminAddUser.test.js Test 3: Should render the roster dashboard if the can }); -test('AdminAddUser.test.js Test 4: HelperText errors should show for each text field when no information is filled', async () => { +test("AdminAddUser.test.js Test 4: HelperText errors should show for each text field when no information is filled", async () => { render(); await waitFor(() => { @@ -159,7 +159,7 @@ test('AdminAddUser.test.js Test 4: HelperText errors should show for each text f }); }); -test('AdminAddUser.test.js Test 5: HelperText error should show for the firstName text field when it is left blank while all other information is filled', async () => { +test("AdminAddUser.test.js Test 5: HelperText error should show for the firstName text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -189,7 +189,7 @@ test('AdminAddUser.test.js Test 5: HelperText error should show for the firstNam }); }); -test('AdminAddUser.test.js Test 6: HelperText error should show for the LastName text field when it is left blank while all other information is filled', async () => { +test("AdminAddUser.test.js Test 6: HelperText error should show for the LastName text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -219,7 +219,7 @@ test('AdminAddUser.test.js Test 6: HelperText error should show for the LastName }); }); -test('AdminAddUser.test.js Test 7: HelperText error should show for the Email Address text field when it is left blank while all other information is filled', async () => { +test("AdminAddUser.test.js Test 7: HelperText error should show for the Email Address text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -249,7 +249,7 @@ test('AdminAddUser.test.js Test 7: HelperText error should show for the Email Ad }); }); -test('AdminAddUser.test.js Test 8: Should render all options for the dropdown menu when clicked', async () => { +test("AdminAddUser.test.js Test 8: Should render all options for the dropdown menu when clicked", async () => { render(); await waitFor(() => { @@ -280,7 +280,7 @@ test('AdminAddUser.test.js Test 8: Should render all options for the dropdown me }); }); -test('AdminAddUser.test.js Test 9: HelperText error should show for the Email Address text field when the input is invalid', async () => { +test("AdminAddUser.test.js Test 9: HelperText error should show for the Email Address text field when the input is invalid", async () => { render(); await waitFor(() => { From 4f0bf93e3f62aecddb03e2617b33725e6ad23f83 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 14:42:53 -0500 Subject: [PATCH 030/160] Updated AdminEditTeamMembers.js changing AdminEditTeam to AdminEditTeamMembers! Also added an aria label to the title! Updated AdminViewTeamMembers.js by changing AdminEditTeam to AdminEditTeamMembers! Also added aria labels! Created AdminViewTeamMembers.test.js and added 4 jest tests! Updated AppState.js by changing AdminEditTeam to AdminEditTeamMembers! --- ...minEditTeam.js => AdminEditTeamMembers.js} | 10 +- .../ViewTeamMembers/AdminViewTeamMembers.js | 6 +- .../__tests__/AdminViewTeamMembers.test.js | 191 ++++++++++++++++++ FrontEndReact/src/View/Navbar/AppState.js | 6 +- 4 files changed, 205 insertions(+), 8 deletions(-) rename FrontEndReact/src/View/Admin/Add/AddTeam/{AdminEditTeam.js => AdminEditTeamMembers.js} (95%) create mode 100644 FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeam.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js similarity index 95% rename from FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeam.js rename to FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index dc65fe492..41e94c082 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeam.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -9,7 +9,7 @@ import { IconButton, Typography } from "@mui/material"; -class AdminEditTeam extends Component { +class AdminEditTeamMembers extends Component { constructor(props) { super(props); @@ -184,7 +184,11 @@ class AdminEditTeam extends Component { return (
- + {this.props.addTeamAction} Members @@ -215,4 +219,4 @@ class AdminEditTeam extends Component { } } -export default AdminEditTeam; +export default AdminEditTeamMembers; diff --git a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js index da5a0770f..7dd927267 100644 --- a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js +++ b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js @@ -81,10 +81,11 @@ class AdminViewTeamMembers extends Component { [team], team["team_id"], parseUserNames(users), - "AdminEditTeam", + "AdminEditTeamMembers", "Add" ); }} + aria-label='addMemberButton' > Add Member @@ -100,10 +101,11 @@ class AdminViewTeamMembers extends Component { [team], team["team_id"], parseUserNames(users), - "AdminEditTeam", + "AdminEditTeamMembers", "Remove" ); }} + aria-label='removeMemberButton' > Remove Member diff --git a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js new file mode 100644 index 000000000..ddac9f64b --- /dev/null +++ b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js @@ -0,0 +1,191 @@ +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + clickFirstElementWithAriaLabel +} from "../../../../../testUtilities.js"; + +import { + demoAdminPassword +} from "../../../../../App.js"; + + + +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; +var vcib = "viewCourseIconButton"; +var vcmh = "viewCourseMainHeader"; +var mhbb = "mainHeaderBackButton"; +var tt = "teamsTab"; +var rt = "rosterTitle"; +var td = "teamDashboard"; +var abub = "adminBulkUploadButton"; +var abu = "adminBulkUpload"; +var aatb = "adminAddTeamButton"; +var aatt = "adminAddTeamTitle"; +var vtib = "viewTeamsIconButton"; +var avtmt = "adminViewTeamMembersTitle"; +var amb = "addMemberButton"; +var atmt = "addTeamMembersTitle"; +var rmb = "removeMemberButton"; +var rtmt = "removeTeamMembersTitle"; + + + +test("NOTE: Tests 1-4 will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test("AdminViewTeamMembers.test.js Test 1: Should render the TeamDashboard", async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + }, 3000); + }); +}); + + +test("AdminViewTeamMembers.test.js Test 2: Should render the View Team page if the adminViewTeam button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); + + +test("AdminViewTeamMembers.test.js Test 3: Should render the Add Team Members page if the add member button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(amb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(atmt); + }, 3000); + }); +}); + + +test("AdminViewTeamMembers.test.js Test 4: Should render the Remove Team Members page if the remove member button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(rmb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rtmt); + }, 3000); + }); +}); \ No newline at end of file diff --git a/FrontEndReact/src/View/Navbar/AppState.js b/FrontEndReact/src/View/Navbar/AppState.js index fa682e006..6b1a13870 100644 --- a/FrontEndReact/src/View/Navbar/AppState.js +++ b/FrontEndReact/src/View/Navbar/AppState.js @@ -12,7 +12,7 @@ import AdminViewTeamMembers from '../Admin/View/ViewTeamMembers/AdminViewTeamMem import AdminBulkUpload from '../Admin/Add/AddUsers/AdminBulkUpload.js'; import StudentDashboard from '../Student/StudentDashboard.js' import StudentTeamMembers from '../Student/View/Team/StudentTeamMembers.js'; -import AdminEditTeam from '../Admin/Add/AddTeam/AdminEditTeam.js' +import AdminEditTeamMembers from '../Admin/Add/AddTeam/AdminEditTeamMembers.js' import TeamDashboard from '../Admin/View/ViewDashboard/TeamDashboard.js'; import AdminAddTeam from '../Admin/Add/AddTeam/AdminAddTeam.js'; import AdminAddAssessmentTask from '../Admin/Add/AddTask/AdminAddAssessmentTask.js'; @@ -636,14 +636,14 @@ class AppState extends Component { } - {this.state.activeTab==="AdminEditTeam" && + {this.state.activeTab==="AdminEditTeamMembers" && - From 6eda5ad8789d821e28daf5de24b9943008db03fa Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 14:46:35 -0500 Subject: [PATCH 031/160] Updated AdminViewTeamMembers.test.js by removing unnecessary variables and added a new jest test for clicking the back button! --- .../__tests__/AdminViewTeamMembers.test.js | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js index ddac9f64b..fb5d3aef8 100644 --- a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js +++ b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/__tests__/AdminViewTeamMembers.test.js @@ -20,15 +20,10 @@ var ei = "emailInput"; var pi = "passwordInput"; var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; -var vcmh = "viewCourseMainHeader"; var mhbb = "mainHeaderBackButton"; var tt = "teamsTab"; var rt = "rosterTitle"; var td = "teamDashboard"; -var abub = "adminBulkUploadButton"; -var abu = "adminBulkUpload"; -var aatb = "adminAddTeamButton"; -var aatt = "adminAddTeamTitle"; var vtib = "viewTeamsIconButton"; var avtmt = "adminViewTeamMembersTitle"; var amb = "addMemberButton"; @@ -107,7 +102,49 @@ test("AdminViewTeamMembers.test.js Test 2: Should render the View Team page if t }); -test("AdminViewTeamMembers.test.js Test 3: Should render the Add Team Members page if the add member button is clicked", async () => { +test("AdminViewTeamMembers.test.js Test 3: Should render the View Team page if the back button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(mhbb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); + + +test("AdminViewTeamMembers.test.js Test 4: Should render the Add Team Members page if the add member button is clicked", async () => { render(); await waitFor(() => { @@ -149,7 +186,7 @@ test("AdminViewTeamMembers.test.js Test 3: Should render the Add Team Members pa }); -test("AdminViewTeamMembers.test.js Test 4: Should render the Remove Team Members page if the remove member button is clicked", async () => { +test("AdminViewTeamMembers.test.js Test 5: Should render the Remove Team Members page if the remove member button is clicked", async () => { render(); await waitFor(() => { From 1775c57f1912fe80c1611d01fb337135abf5c84f Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 15:40:01 -0500 Subject: [PATCH 032/160] Updated AdminEditTeamMembers.js by passing the props of addTeamAction to the label for the add or remove column! Also added an aria label! Created AdminEditTeamMembers.test.js and created 8 jest tests! Updated AppState.js by changing TeamMember to TeamMembers in the tabSelected props for the BackButtonResource component on the AdminEditTeamMembers page! --- .../Admin/Add/AddTeam/AdminEditTeamMembers.js | 4 +- .../__tests__/AdminEditTeamMembers.test.js | 387 ++++++++++++++++++ FrontEndReact/src/View/Navbar/AppState.js | 2 +- 3 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index 41e94c082..b1b6c8a49 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -124,7 +124,7 @@ class AdminEditTeamMembers extends Component { }, { name: "user_id", - label: "Add/Remove", + label: this.props.addTeamAction, options: { filter: true, sort: false, @@ -204,6 +204,8 @@ class AdminEditTeamMembers extends Component { onClick={() => { this.sendUsers(); }} + + aria-label="adminEditTeamMembersSaveTeamButton" > Save Team diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js new file mode 100644 index 000000000..8b649196a --- /dev/null +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js @@ -0,0 +1,387 @@ +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + clickFirstElementWithAriaLabel +} from "../../../../../testUtilities.js"; + +import { + demoAdminPassword +} from "../../../../../App.js"; + + + +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; +var vcib = "viewCourseIconButton"; +var mhbb = "mainHeaderBackButton"; +var tt = "teamsTab"; +var rt = "rosterTitle"; +var td = "teamDashboard"; +var vtib = "viewTeamsIconButton"; +var avtmt = "adminViewTeamMembersTitle"; +var amb = "addMemberButton"; +var atmt = "addTeamMembersTitle"; +var rmb = "removeMemberButton"; +var rtmt = "removeTeamMembersTitle"; +var aetmstb = "adminEditTeamMembersSaveTeamButton"; + + + +test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test("AdminEditTeamMembers.test.js Test 1: Should render the TeamDashboard", async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 2: Should render the View Team Members page if the adminViewTeam button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 3: Should render the Add Team Members page if the add member button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(amb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(atmt); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 4: Should render the View Team Members page if the back button is clicked on the add member page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(amb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(atmt); + + clickElementWithAriaLabel(mhbb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 5: Should render the View Team Members page if the save team button is clicked on the add member page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(amb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(atmt); + + clickElementWithAriaLabel(aetmstb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 6: Should render the Remove Team Members page if the remove member button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(rmb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rtmt); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 7: Should render the View Team Members page if the back button is clicked on the remove member page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(rmb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rtmt); + + clickElementWithAriaLabel(mhbb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); + + +test("AdminEditTeamMembers.test.js Test 8: Should render the View Team Members page if the save team button is clicked on the remove member page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(vtib); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + + clickElementWithAriaLabel(rmb); + }, 3000); + }); + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rtmt); + + clickElementWithAriaLabel(aetmstb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(avtmt); + }, 3000); + }); +}); \ No newline at end of file diff --git a/FrontEndReact/src/View/Navbar/AppState.js b/FrontEndReact/src/View/Navbar/AppState.js index 6b1a13870..0fc8e8182 100644 --- a/FrontEndReact/src/View/Navbar/AppState.js +++ b/FrontEndReact/src/View/Navbar/AppState.js @@ -640,7 +640,7 @@ class AppState extends Component { Date: Thu, 9 May 2024 15:43:45 -0500 Subject: [PATCH 033/160] Updated AdminEditTeamMembers.test.js by updating the comment in the jest test! --- .../Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js index 8b649196a..02b47bbeb 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminEditTeamMembers.test.js @@ -34,7 +34,7 @@ var aetmstb = "adminEditTeamMembersSaveTeamButton"; -test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-8 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); From bd4cf64d96b5ebe6d086f8e9e2e0dad1be96e329 Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 9 May 2024 15:30:21 -0500 Subject: [PATCH 034/160] Added 4 Jest test cases for AdminAddAssessmentTask.js --- .../Add/AddTask/AdminAddAssessmentTask.js | 27 +- .../__tests__/AdminAddAssessmentTask.test.js | 245 ++++++++++++++++++ 2 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js index a71a8d925..e2c3f80d9 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js +++ b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js @@ -194,7 +194,7 @@ class AdminAddAssessmentTask extends Component { Object.keys(roleNames).map((role) => { if (roleNames[role] === "TA/Instructor" || roleNames[role] === "Student") { - roleOptions = [...roleOptions, } label={roleNames[role]} key={role} />]; + roleOptions = [...roleOptions, } label={roleNames[role]} key={role} aria-label="addAssessmentRoleOption" />]; } return role; @@ -205,7 +205,7 @@ class AdminAddAssessmentTask extends Component { var rubricOptions = []; Object.keys(rubricNames).map((rubric) => { - rubricOptions = [...rubricOptions, {rubricNames[rubric]}]; + rubricOptions = [...rubricOptions, {rubricNames[rubric]}]; return rubric; }); @@ -258,9 +258,11 @@ class AdminAddAssessmentTask extends Component { label="Task Name" value={taskName} error={!!errors.taskName} + helperText={errors.taskName} onChange={this.handleChange} required sx={{ mb: 2 }} + aria-label="addAssessmentTaskName" />
@@ -275,6 +277,7 @@ class AdminAddAssessmentTask extends Component { error={!!errors.rubricId} onChange={(event) => this.handleSelect("rubricId", event)} required + aria-label="addAssessmentRubricDropdown" > {rubricOptions} @@ -293,9 +296,9 @@ class AdminAddAssessmentTask extends Component { sx={{ mb: 2 }} onChange={this.handleTeams} > - } label="Individual Assessment" /> + } label="Individual Assessment" aria-label="addAssessmentInvididualAssessmentRadioOption"/> - } label="Group Assessment" /> + } label="Group Assessment" aria-label="addAssessmentGroupAssessmentRadioOption" /> @@ -394,16 +397,17 @@ class AdminAddAssessmentTask extends Component { required sx={{ mb: 2 }} style={{width: "200px"}} + aria-label="addAssessmentTimezoneDropdown" > {timeZone ? {timeZone} : ''} - EST + EST - CST + CST - MST + MST - PST + PST
@@ -418,8 +422,10 @@ class AdminAddAssessmentTask extends Component { label="Password to switch teams" value={password} error={!!errors.password} + helperText={errors.password} onChange={this.handleChange} sx={{ mb: 2 }} + aria-label="addAssessmentTeamPassword" /> } @@ -437,10 +443,11 @@ class AdminAddAssessmentTask extends Component { minRows={2} maxRows={8} sx={{ mb: 2 }} + aria-label="addAssessmentNotes" /> - @@ -448,8 +455,8 @@ class AdminAddAssessmentTask extends Component { id="createAssessmentTask" className="primary-color" variant="contained" - onClick={this.handleSubmit} + aria-label="addAssessmentCreateOrUpdateButton" > {editAssessmentTask ? "Update Task" : "Create Task"} diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js b/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js new file mode 100644 index 000000000..d203fd0f6 --- /dev/null +++ b/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js @@ -0,0 +1,245 @@ +import { render, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import Login from '../../../../Login/Login.js'; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + clickFirstElementWithAriaLabel, + expectElementWithAriaLabelToHaveErrorMessage +} from '../../../../../testUtilities.js'; + +import { + demoAdminPassword, +} from '../../../../../App.js'; + + + +var lb = "loginButton"; +var ei = 'emailInput'; +var pi = 'passwordInput'; +var ct = 'coursesTitle'; +var vcib = "viewCourseIconButton"; +var rt = "rosterTitle"; +var at = "assessmentTab"; +var adt = "assessmentDashboardTitle"; +var atb = "addTaskButton"; +var aaatt = "adminAddAssessmentTaskTitle"; +var aaacb = "adminAddAssessmentCancelButton" +var aagaro = "addAssessmentGroupAssessmentRadioOption"; +var aatp = "addAssessmentTeamPassword"; +var aacoub = "addAssessmentCreateOrUpdateButton"; +var aatn = "addAssessmentTaskName"; +var aard = "addAssessmentRubricDropdown"; +var aaro = "addAssessmentRoleOption"; +var aatd = "addAssessmentTimezoneDropdown"; +var aaero = "addAssessmentEstRadioOption"; +var aan = "addAssessmentNotes"; +var aarubo = "addAssessmentRubricOption"; + + + +test("NOTE: Tests _-_ will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test('AdminAddAssessmentTask.test.js Test 1: Should render the AdminAddCourse component given the Add Course button is clicked', async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(adt); + + clickElementWithAriaLabel(atb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aaatt); + }, 3000); + }); +}); + + +test('AdminAddAssessmentTask.test.js Test 2: Should render the Assessment dashboard if the cancel button is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(adt); + + clickElementWithAriaLabel(atb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aaatt); + + clickElementWithAriaLabel(aaacb); + }, 3000); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(adt); + }); +}); + + +test('AdminAddAssessmentTask.test.js Test 3: Should render the Password text field if the Group Assessment radio option is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + + expectElementWithAriaLabelToBeInDocument(adt); + + clickElementWithAriaLabel(atb); + + }); + + await waitFor(() => { + clickElementWithAriaLabel(aagaro); + }); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aatp); + }); + +}); + + +test('AdminAddAssessmentTask.test.js Test 4: Should provide a HelperText error when Task Name is left empty', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + + expectElementWithAriaLabelToBeInDocument(adt); + + clickElementWithAriaLabel(atb); + + }); + + await waitFor(() => { + clickElementWithAriaLabel(aacoub); + }); + + expectElementWithAriaLabelToHaveErrorMessage(aatn, "Task Name cannot be empty"); + +}); + + +// TODO: +// test('AdminAddAssessmentTask.test.js Test 5: Should return back to the Assessment View page if all valid information is provided and the Add Assessment button is clicked', async () => { +// render(); + +// await waitFor(() => { +// expectElementWithAriaLabelToBeInDocument(ct); +// }); + +// clickFirstElementWithAriaLabel(vcib); + +// await waitFor(() => { +// setTimeout(() => { +// expectElementWithAriaLabelToBeInDocument(rt); +// }, 3000); +// }); + +// clickElementWithAriaLabel(at); + +// await waitFor(() => { + +// expectElementWithAriaLabelToBeInDocument(adt); + +// clickElementWithAriaLabel(atb); + +// }); + +// await waitFor(() => { +// changeElementWithAriaLabelWithInput(aatn, "Make a class"); + +// clickElementWithAriaLabel(aard); +// }); + +// await waitFor(() => { +// clickFirstElementWithAriaLabel(aarubo); + +// clickFirstElementWithAriaLabel(aaro); + +// clickElementWithAriaLabel(aatd); +// }); + +// await waitFor(() => { +// clickElementWithAriaLabel(aaero); + +// changeElementWithAriaLabelWithInput(aan, "Make a class"); +// }); + +// await waitFor(() => { +// expectElementWithAriaLabelToBeInDocument(adt); +// }); +// }); \ No newline at end of file From 12fb543eab954bc3e4d3b7de737dddd215aba8ee Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 9 May 2024 15:57:15 -0500 Subject: [PATCH 035/160] Added 5 test cases for the AdminAddAssessmentTask.js component --- .../__tests__/AdminAddAssessmentTask.test.js | 75 ++++++++++--------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js b/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js index d203fd0f6..c32888582 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js @@ -40,7 +40,7 @@ var aarubo = "addAssessmentRubricOption"; -test("NOTE: Tests _-_ will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); @@ -193,53 +193,58 @@ test('AdminAddAssessmentTask.test.js Test 4: Should provide a HelperText error w }); -// TODO: -// test('AdminAddAssessmentTask.test.js Test 5: Should return back to the Assessment View page if all valid information is provided and the Add Assessment button is clicked', async () => { -// render(); +test('AdminAddAssessmentTask.test.js Test 5: Should return back to the Assessment View page if all valid information is provided and the Add Assessment button is clicked', async () => { + render(); -// await waitFor(() => { -// expectElementWithAriaLabelToBeInDocument(ct); -// }); + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); -// clickFirstElementWithAriaLabel(vcib); + clickFirstElementWithAriaLabel(vcib); -// await waitFor(() => { -// setTimeout(() => { -// expectElementWithAriaLabelToBeInDocument(rt); -// }, 3000); -// }); + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); -// clickElementWithAriaLabel(at); + clickElementWithAriaLabel(at); -// await waitFor(() => { + await waitFor(() => { -// expectElementWithAriaLabelToBeInDocument(adt); + expectElementWithAriaLabelToBeInDocument(adt); -// clickElementWithAriaLabel(atb); + clickElementWithAriaLabel(atb); -// }); + }); -// await waitFor(() => { -// changeElementWithAriaLabelWithInput(aatn, "Make a class"); + await waitFor(() => { + changeElementWithAriaLabelWithInput(aatn, "Make a class"); -// clickElementWithAriaLabel(aard); -// }); + clickElementWithAriaLabel(aard); + }); -// await waitFor(() => { -// clickFirstElementWithAriaLabel(aarubo); + await waitFor(() => { + setTimeout(() => { + clickFirstElementWithAriaLabel(aarubo); -// clickFirstElementWithAriaLabel(aaro); + clickFirstElementWithAriaLabel(aaro); -// clickElementWithAriaLabel(aatd); -// }); + clickElementWithAriaLabel(aatd); + }, 3000); + }); -// await waitFor(() => { -// clickElementWithAriaLabel(aaero); + await waitFor(() => { + setTimeout(() => { + clickElementWithAriaLabel(aaero); -// changeElementWithAriaLabelWithInput(aan, "Make a class"); -// }); + changeElementWithAriaLabelWithInput(aan, "Make a class"); + }, 3000); + }); -// await waitFor(() => { -// expectElementWithAriaLabelToBeInDocument(adt); -// }); -// }); \ No newline at end of file + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(adt); + }, 3000) + }); +}); \ No newline at end of file From 3e5ce710c52c80cb8770f6919ae378de7e555193 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 16:09:05 -0500 Subject: [PATCH 036/160] Updated AdminAddAssessmentTask.test.js by changing single to double quotes! --- .../__tests__/AdminAddAssessmentTask.test.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js b/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js index c32888582..a45d8f935 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTask/__tests__/AdminAddAssessmentTask.test.js @@ -1,6 +1,6 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, @@ -8,18 +8,18 @@ import { changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel, expectElementWithAriaLabelToHaveErrorMessage -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword, -} from '../../../../../App.js'; +} from "../../../../../App.js"; var lb = "loginButton"; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var rt = "rosterTitle"; var at = "assessmentTab"; @@ -45,7 +45,7 @@ test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { }); -test('AdminAddAssessmentTask.test.js Test 1: Should render the AdminAddCourse component given the Add Course button is clicked', async () => { +test("AdminAddAssessmentTask.test.js Test 1: Should render the AdminAddCourse component given the Add Course button is clicked", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -84,7 +84,7 @@ test('AdminAddAssessmentTask.test.js Test 1: Should render the AdminAddCourse co }); -test('AdminAddAssessmentTask.test.js Test 2: Should render the Assessment dashboard if the cancel button is clicked', async () => { +test("AdminAddAssessmentTask.test.js Test 2: Should render the Assessment dashboard if the cancel button is clicked", async () => { render(); await waitFor(() => { @@ -123,7 +123,7 @@ test('AdminAddAssessmentTask.test.js Test 2: Should render the Assessment dashbo }); -test('AdminAddAssessmentTask.test.js Test 3: Should render the Password text field if the Group Assessment radio option is clicked', async () => { +test("AdminAddAssessmentTask.test.js Test 3: Should render the Password text field if the Group Assessment radio option is clicked", async () => { render(); await waitFor(() => { @@ -159,7 +159,7 @@ test('AdminAddAssessmentTask.test.js Test 3: Should render the Password text fie }); -test('AdminAddAssessmentTask.test.js Test 4: Should provide a HelperText error when Task Name is left empty', async () => { +test("AdminAddAssessmentTask.test.js Test 4: Should provide a HelperText error when Task Name is left empty", async () => { render(); await waitFor(() => { @@ -193,7 +193,7 @@ test('AdminAddAssessmentTask.test.js Test 4: Should provide a HelperText error w }); -test('AdminAddAssessmentTask.test.js Test 5: Should return back to the Assessment View page if all valid information is provided and the Add Assessment button is clicked', async () => { +test("AdminAddAssessmentTask.test.js Test 5: Should return back to the Assessment View page if all valid information is provided and the Add Assessment button is clicked", async () => { render(); await waitFor(() => { From 4a8c2884fb2647bdf4ce08a2e90463f46799b971 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 13:24:46 -0500 Subject: [PATCH 037/160] Created AdminAddTeam.test.js and added 3 tests! --- .../AddTeam/__tests__/AdminAddTeam.test.js | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js new file mode 100644 index 000000000..2524f43b0 --- /dev/null +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js @@ -0,0 +1,112 @@ +import { render, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import Login from '../../../../Login/Login.js'; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + clickFirstElementWithAriaLabel +} from '../../../../../testUtilities.js'; + +import { + demoAdminPassword +} from '../../../../../App.js'; + + + +var lb = 'loginButton'; +var ei = 'emailInput'; +var pi = 'passwordInput'; +var ct = 'coursesTitle'; +var vcib = "viewCourseIconButton"; +var vcmh = "viewCourseMainHeader"; +var mhbb = "mainHeaderBackButton"; +var tt = "teamsTab"; +var rt = "rosterTitle"; +var td = "teamDashboard"; +var abub = "adminBulkUploadButton"; +var abu = "adminBulkUpload"; +var aatb = "adminAddTeamButton"; +var aatt = "adminAddTeamTitle"; +var vtib = "viewTeamsIconButton"; +var avtmt = "adminViewTeamMembersTitle"; + + + +test("NOTE: Tests 1-? will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test('AdminAddTeam.test.js Test 1: Should render the TeamDashboard', async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + }, 3000); + }); +}); + + +test('AdminAddTeam.test.js Test 2: Should render the Add Team page if the adminAddTeam button is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(aatb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aatt); + }, 3000); + }); +}); + + +test('', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(aatt); + }); +}); \ No newline at end of file From 5197b874604ec229be714147f010562845781a97 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Thu, 9 May 2024 16:28:49 -0500 Subject: [PATCH 038/160] Added test cases for AdminAddTeam --- .../View/Admin/Add/AddTeam/AdminAddTeam.js | 6 +- .../AddTeam/__tests__/AdminAddTeam.test.js | 169 +++++++++++++++++- 2 files changed, 171 insertions(+), 4 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminAddTeam.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminAddTeam.js index 2317579d9..6dbd40e06 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminAddTeam.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminAddTeam.js @@ -192,7 +192,7 @@ class AdminAddTeam extends Component { - + {this.state.editTeam ? "Edit Team" : "Add Team"} @@ -210,6 +210,7 @@ class AdminAddTeam extends Component { onChange={this.handleChange} required sx={{ mb: 3 }} + aria-label="userTeamNameInput" /> @@ -246,6 +247,7 @@ class AdminAddTeam extends Component { addTeam: null, }); }} + aria-label="cancelAddTeamButton" > Cancel @@ -255,6 +257,8 @@ class AdminAddTeam extends Component { variant="contained" onClick={this.handleSubmit} + + aria-label="addOrSaveAddTeamButton" > {this.state.editTeam ? "Save" : "Add Team"} diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js index 2524f43b0..4dc431e41 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js @@ -6,7 +6,8 @@ import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, - clickFirstElementWithAriaLabel + clickFirstElementWithAriaLabel, + expectElementWithAriaLabelToHaveErrorMessage } from '../../../../../testUtilities.js'; import { @@ -31,6 +32,10 @@ var aatb = "adminAddTeamButton"; var aatt = "adminAddTeamTitle"; var vtib = "viewTeamsIconButton"; var avtmt = "adminViewTeamMembersTitle"; +var catb = "cancelAddTeamButton"; +var aosatb = "addOrSaveAddTeamButton"; +var atf = "addTeamForm"; +var utni = "userTeamNameInput"; @@ -103,10 +108,168 @@ test('AdminAddTeam.test.js Test 2: Should render the Add Team page if the adminA }); -test('', async () => { +test('AdminAddTeam.test.js Test 3: Should render the teams dashboard if the back button on the Add Team page is clicked ', async () => { render(); await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(aatt); + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(aatb); + }, 3000); + }); + + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aatt); + }, 3000); + }); + + clickElementWithAriaLabel(mhbb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + }, 3000); + }); +}); + +test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the cancel button on the Add Team page is clicked ', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(aatb); + }, 3000); + }); + + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aatt); + + clickElementWithAriaLabel(catb); + }, 3000); + }); + + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + }, 3000); + }); +}); + +test('AdminAddTeam.test.js Test 5: HelperText errors should show for each text field when no information is filled', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(aatb); + }, 3000); + }); + + + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(aatt); + + clickElementWithAriaLabel(aosatb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(atf); + + expectElementWithAriaLabelToHaveErrorMessage(utni,"Team name cannot be empty"); + }, 3000); + + }); +}); + +test('AdminAddTeam.test.js Test 6: HelperText error should show for the teamName text field when it is left blank while all other information is filled', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(aatb); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(atf); + + expectElementWithAriaLabelToHaveErrorMessage(utni,"Team name cannot be empty"); + }, 3000); + }); }); \ No newline at end of file From e3cf6224820362e262f0a6d9df1877fb0089727d Mon Sep 17 00:00:00 2001 From: aparriaran Date: Thu, 9 May 2024 16:40:19 -0500 Subject: [PATCH 039/160] Removed unecessary whitespace and updated the range of test cases in NOTE. --- .../Add/AddTeam/__tests__/AdminAddTeam.test.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js index 4dc431e41..95b77f5c2 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js @@ -21,17 +21,12 @@ var ei = 'emailInput'; var pi = 'passwordInput'; var ct = 'coursesTitle'; var vcib = "viewCourseIconButton"; -var vcmh = "viewCourseMainHeader"; var mhbb = "mainHeaderBackButton"; var tt = "teamsTab"; var rt = "rosterTitle"; var td = "teamDashboard"; -var abub = "adminBulkUploadButton"; -var abu = "adminBulkUpload"; var aatb = "adminAddTeamButton"; var aatt = "adminAddTeamTitle"; -var vtib = "viewTeamsIconButton"; -var avtmt = "adminViewTeamMembersTitle"; var catb = "cancelAddTeamButton"; var aosatb = "addOrSaveAddTeamButton"; var atf = "addTeamForm"; @@ -39,7 +34,7 @@ var utni = "userTeamNameInput"; -test("NOTE: Tests 1-? will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-6 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); @@ -150,6 +145,7 @@ test('AdminAddTeam.test.js Test 3: Should render the teams dashboard if the back }); }); + test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the cancel button on the Add Team page is clicked ', async () => { render(); @@ -175,8 +171,6 @@ test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the canc }, 3000); }); - - await waitFor(() => { setTimeout(() => { expectElementWithAriaLabelToBeInDocument(aatt); @@ -185,8 +179,6 @@ test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the canc }, 3000); }); - - await waitFor(() => { setTimeout(() => { expectElementWithAriaLabelToBeInDocument(td); @@ -194,6 +186,7 @@ test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the canc }); }); + test('AdminAddTeam.test.js Test 5: HelperText errors should show for each text field when no information is filled', async () => { render(); @@ -219,8 +212,6 @@ test('AdminAddTeam.test.js Test 5: HelperText errors should show for each text f }, 3000); }); - - await waitFor(() => { setTimeout(() => { expectElementWithAriaLabelToBeInDocument(aatt); @@ -239,6 +230,7 @@ test('AdminAddTeam.test.js Test 5: HelperText errors should show for each text f }); }); + test('AdminAddTeam.test.js Test 6: HelperText error should show for the teamName text field when it is left blank while all other information is filled', async () => { render(); @@ -270,6 +262,5 @@ test('AdminAddTeam.test.js Test 6: HelperText error should show for the teamName expectElementWithAriaLabelToHaveErrorMessage(utni,"Team name cannot be empty"); }, 3000); - }); }); \ No newline at end of file From 3e080ba53bcee333f6c1433b4b06840fb5db1be7 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 16:41:10 -0500 Subject: [PATCH 040/160] Updated AdminAddTeam.test.js by changing all single to double quotes! --- .../AddTeam/__tests__/AdminAddTeam.test.js | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js index 95b77f5c2..17ab0b1f9 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js @@ -1,6 +1,6 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, @@ -8,18 +8,18 @@ import { changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel, expectElementWithAriaLabelToHaveErrorMessage -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword -} from '../../../../../App.js'; +} from "../../../../../App.js"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var mhbb = "mainHeaderBackButton"; var tt = "teamsTab"; @@ -39,7 +39,7 @@ test("NOTE: Tests 1-6 will not pass if Demo Data is not loaded!", () => { }); -test('AdminAddTeam.test.js Test 1: Should render the TeamDashboard', async () => { +test("AdminAddTeam.test.js Test 1: Should render the TeamDashboard", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -70,7 +70,7 @@ test('AdminAddTeam.test.js Test 1: Should render the TeamDashboard', async () => }); -test('AdminAddTeam.test.js Test 2: Should render the Add Team page if the adminAddTeam button is clicked', async () => { +test("AdminAddTeam.test.js Test 2: Should render the Add Team page if the adminAddTeam button is clicked", async () => { render(); await waitFor(() => { @@ -103,7 +103,7 @@ test('AdminAddTeam.test.js Test 2: Should render the Add Team page if the adminA }); -test('AdminAddTeam.test.js Test 3: Should render the teams dashboard if the back button on the Add Team page is clicked ', async () => { +test("AdminAddTeam.test.js Test 3: Should render the teams dashboard if the back button on the Add Team page is clicked ", async () => { render(); await waitFor(() => { @@ -146,7 +146,7 @@ test('AdminAddTeam.test.js Test 3: Should render the teams dashboard if the back }); -test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the cancel button on the Add Team page is clicked ', async () => { +test("AdminAddTeam.test.js Test 4: Should render the teams dashboard if the cancel button on the Add Team page is clicked ", async () => { render(); await waitFor(() => { @@ -187,7 +187,7 @@ test('AdminAddTeam.test.js Test 4: Should render the teams dashboard if the canc }); -test('AdminAddTeam.test.js Test 5: HelperText errors should show for each text field when no information is filled', async () => { +test("AdminAddTeam.test.js Test 5: HelperText errors should show for each text field when no information is filled", async () => { render(); await waitFor(() => { @@ -231,7 +231,7 @@ test('AdminAddTeam.test.js Test 5: HelperText errors should show for each text f }); -test('AdminAddTeam.test.js Test 6: HelperText error should show for the teamName text field when it is left blank while all other information is filled', async () => { +test("AdminAddTeam.test.js Test 6: HelperText error should show for the teamName text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { From 8cee8e82e3270d88bf1739ef441d7c0ace5292f1 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Thu, 9 May 2024 16:42:59 -0500 Subject: [PATCH 041/160] Updated AdminAddTeam.test.js by removing whitespace! --- .../src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js index 17ab0b1f9..c1734efbf 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/__tests__/AdminAddTeam.test.js @@ -128,8 +128,6 @@ test("AdminAddTeam.test.js Test 3: Should render the teams dashboard if the back }, 3000); }); - - await waitFor(() => { setTimeout(() => { expectElementWithAriaLabelToBeInDocument(aatt); @@ -226,7 +224,6 @@ test("AdminAddTeam.test.js Test 5: HelperText errors should show for each text f expectElementWithAriaLabelToHaveErrorMessage(utni,"Team name cannot be empty"); }, 3000); - }); }); From 5fa6b8795d25c8ce75b30eaa4982c82610895a3f Mon Sep 17 00:00:00 2001 From: aparriaran Date: Fri, 10 May 2024 16:25:38 -0500 Subject: [PATCH 042/160] Created test cases for AdminBulkUpload and added the cancel button for Student Bulk Upload and Team Bulk Upload --- .../Admin/Add/AddUsers/AdminBulkUpload.js | 16 +- .../__tests__/AdminBulkUpload.test.js | 363 ++++++++++++++++++ 2 files changed, 377 insertions(+), 2 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminBulkUpload.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminBulkUpload.js index 6715b95af..4e69283e1 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminBulkUpload.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminBulkUpload.js @@ -113,12 +113,16 @@ class AdminBulkUpload extends Component { } render() { + var navbar = this.props.navbar; + var confirmCreateResource = navbar.confirmCreateResource; + return ( {this.state.errorMessage && } @@ -219,7 +223,7 @@ class AdminBulkUpload extends Component { className='rounded form-control' type="file" name="file" - + aria-label="adminBulkUploadChooseFileButton" onChange={(e) => { this.setState({ selectedFile: e.target.files[0] @@ -227,7 +231,15 @@ class AdminBulkUpload extends Component { }} /> - + + + {this.props.tab === "AdminTeamBulkUpload" && diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js new file mode 100644 index 000000000..b9d4c045a --- /dev/null +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js @@ -0,0 +1,363 @@ +import { getByLabelText, render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + clickFirstElementWithAriaLabel, + expectElementWithAriaLabelToHaveErrorMessage +} from "../../../../../testUtilities.js"; + +import { + demoAdminPassword +} from "../../../../../App.js"; + + + +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; +var vcib = "viewCourseIconButton"; +var rt = "rosterTitle"; +var sbub = "studentBulkUploadButton"; +var abut = "adminBulkUploadTitle"; +var mhbb = "mainHeaderBackButton"; +var abucfb = "adminBulkUploadChooseFileButton"; +var abuufb = "adminBulkUploadUploadFileButton"; +var abuem = "adminBulkUploadErrorMessage"; +var cabub = "cancelAdminBulkUploadButton"; +var tt = "teamsTab"; +var td = "teamDashboard"; +var tbub = "teamBulkUploadButton"; + + + +test("NOTE: Tests 1-??? will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test("AdminBulkUpload.test.js Test 1: Should render the AdminBulkUpload component given the Student Bulk Upload button is clicked", async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(sbub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(abut); + }); +}); + + +test("AdminBulkUpload.test.js Test 2: Should render the roster dashboard if the back button on the Student Bulk Upload page is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(sbub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(abut); + }); + + clickElementWithAriaLabel(mhbb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 3: Should render your files when the Choose File button is clicked on Student Bulk Upload page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(sbub); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(abut); + }, 3000); + }); + + clickElementWithAriaLabel(abucfb); +}); + + +test("AdminBulkUpload.test.js Test 4: Should render an error message when no file is uploaded on Student Bulk Upload page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(sbub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(abut); + }); + + clickElementWithAriaLabel(abuufb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(abuem); + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 5: Should render the roster dashboard if the cancel button on the Student Bulk Upload page is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); + + clickElementWithAriaLabel(sbub); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(abut); + }); + + clickElementWithAriaLabel(cabub); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 6: Should render the AdminBulkUpload component given the Team Bulk Upload button is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(tbub); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(abut); + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 7: Should render the roster dashboard if the back button on the Team Bulk Upload page is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(tbub); + + expectElementWithAriaLabelToBeInDocument(abut); + }, 3000); + }); + + clickElementWithAriaLabel(mhbb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 8: Should render your files when the Choose File button is clicked on Team Bulk Upload page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(tbub); + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(abut); + + clickElementWithAriaLabel(abuufb); + + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 9: Should render an error message when no file is uploaded on Team Bulk Upload page", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(tbub); + + expectElementWithAriaLabelToBeInDocument(abut); + }, 3000); + }); + + clickElementWithAriaLabel(abuufb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(abuem); + }, 3000); + }); +}); + + +test("AdminBulkUpload.test.js Test 10: Should render the roster dashboard if the cancel button on the Team Bulk Upload page is clicked", async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(tt); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(td); + + clickElementWithAriaLabel(tbub); + + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(abut); + + clickElementWithAriaLabel(cabub) + + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); \ No newline at end of file From 3ca33db95a05dfb455c7de592d819c6657f01ec9 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Fri, 10 May 2024 16:43:02 -0500 Subject: [PATCH 043/160] updated range for NOTE and removed varaibles that were not used --- .../Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js index b9d4c045a..230647a9a 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js @@ -1,4 +1,4 @@ -import { getByLabelText, render, waitFor } from "@testing-library/react"; +import { render, waitFor } from "@testing-library/react"; import "@testing-library/jest-dom"; import Login from "../../../../Login/Login.js"; @@ -7,7 +7,6 @@ import { expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel, - expectElementWithAriaLabelToHaveErrorMessage } from "../../../../../testUtilities.js"; import { @@ -35,7 +34,7 @@ var tbub = "teamBulkUploadButton"; -test("NOTE: Tests 1-??? will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-10 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); From 21eba8e7660e1a03a50291312deffe8a3cb17fc7 Mon Sep 17 00:00:00 2001 From: rtkells Date: Fri, 10 May 2024 12:21:10 -0500 Subject: [PATCH 044/160] Added 4 Jest Test cases for the AdminImportAssessmentTasks.js component --- .../ImportTasks/AdminImportAssessmentTasks.js | 6 +- .../Admin/Add/ImportTasks/CourseDropdown.js | 3 +- .../AdminImportAssessmentTasks.test.js | 175 ++++++++++++++++++ 3 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/AdminImportAssessmentTasks.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/AdminImportAssessmentTasks.js index eff1d1618..f2d496631 100644 --- a/FrontEndReact/src/View/Admin/Add/ImportTasks/AdminImportAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/AdminImportAssessmentTasks.js @@ -113,6 +113,7 @@ class AdminImportAssessmentTask extends Component { @@ -122,7 +123,9 @@ class AdminImportAssessmentTask extends Component { onClick={() => { confirmCreateResource("AssessmentTask") }} - id="" className=""> + id="" className="" + aria-label="adminImportAssessmentTaskCancelButton" + > Cancel @@ -132,6 +135,7 @@ class AdminImportAssessmentTask extends Component { }} id="importAssessmentTasks" className="primary-color" variant="contained" + aria-label="adminImportAssessmentTasksSubmitButton" > Import Tasks diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/CourseDropdown.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/CourseDropdown.js index 0a05adb35..5554d6939 100644 --- a/FrontEndReact/src/View/Admin/Add/ImportTasks/CourseDropdown.js +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/CourseDropdown.js @@ -39,7 +39,7 @@ class CourseDropdown extends Component { this.state.courses && this.state.courses.map((course, index) => { return( courseChoices = [...courseChoices, - + {course["course_name"]} ] @@ -56,6 +56,7 @@ class CourseDropdown extends Component { label='Select a Course' value={this.state.selectedCourse} onChange={this.handleCourseChange} + aria-label="adminImportAssessmentCourseDropdown" > {courseChoices} diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js new file mode 100644 index 000000000..f002723b3 --- /dev/null +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js @@ -0,0 +1,175 @@ +import { render, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import Login from '../../../../Login/Login.js'; + +import { + clickElementWithAriaLabel, + expectElementWithAriaLabelToBeInDocument, + changeElementWithAriaLabelWithInput, + expectElementWithAriaLabelToHaveErrorMessage, + clickFirstElementWithAriaLabel +} from '../../../../../testUtilities.js'; + +import { + demoAdminPassword +} from '../../../../../App.js'; + + + +var lb = 'loginButton'; +var ei = 'emailInput'; +var pi = 'passwordInput'; +var ct = 'coursesTitle'; +var iab = "importAssessmentButton" +var aiatt = "adminImportAssessmentTasksTitle"; +var vcib = "viewCourseIconButton"; +var rt = "rosterTitle"; +var at = "assessmentTab"; +var aiatcb = "adminImportAssessmentTaskCancelButton" +var aiatsb = "adminImportAssessmentTasksSubmitButton"; +var aiacs = "adminImportAssessmentTasksCourseSelect"; +var aiacc = "adminImportAssessmentCourseChoice"; +var aiacd = "adminImportAssessmentCourseDropdown" + + + +test("NOTE: Tests 1-_ will not pass if Demo Data is not loaded!", () => { + expect(true).toBe(true); +}); + + +test('AdminImportAssessmentTasks.test.js Test 1: Should render the AdminImportAssessmentTasks component given the Import Assessments button is clicked', async () => { + render(); + + changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); + + changeElementWithAriaLabelWithInput(pi, demoAdminPassword); + + clickElementWithAriaLabel(lb); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + clickElementWithAriaLabel(iab); + + expectElementWithAriaLabelToBeInDocument(aiatt); + }); +}); + + +test('AdminImportAssessmentTasks.test.js Test 3: Should render the page that came before given that the Cancel button is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + clickElementWithAriaLabel(iab); + + expectElementWithAriaLabelToBeInDocument(aiatt); + }); + + clickElementWithAriaLabel(aiatcb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); + + +test('AdminImportAssessmentTasks.test.js Test 4: Should render an error message on the page when no input is given', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + clickElementWithAriaLabel(iab); + + expectElementWithAriaLabelToBeInDocument(aiatt); + }); + + await waitFor(() => { + setTimeout(() => { + clickElementWithAriaLabel(aiatsb); + + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToHaveErrorMessage(aiacs, "Invalid Form: Missing Course!"); + }, 3000); + }); + + +}); + + +test('AdminImportAssessmentTasks.test.js Test 5: Should refresh and return back to Assessment Dashboard page when valid information is input and submit button is clicked', async() => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + clickElementWithAriaLabel(iab); + + expectElementWithAriaLabelToBeInDocument(aiatt); + }); + + await waitFor(() => { + setTimeout(() => { + clickElementWithAriaLabel(aiacd) + + clickFirstElementWithAriaLabel(aiacc); + + clickElementWithAriaLabel(aiasb) + }, 3000); + }); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); \ No newline at end of file From 6c53de17d500028f2df7687dc9ea73d5863e9b80 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Wed, 3 Apr 2024 22:41:13 -0500 Subject: [PATCH 045/160] Potentialy fixed bug where students were showing up as candidates to be added to multiple teams --- BackEndFlask/controller/Routes/User_routes.py | 4 ++-- BackEndFlask/models/queries.py | 6 +++--- FrontEndReact/src/View/Admin/View/ViewTeams/ViewTeams.js | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index b23520abf..8590b1378 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -43,7 +43,7 @@ get_users_by_course_id, get_users_by_course_id_and_role_id, get_users_by_team_id, - get_users_not_in_team_id, + get_users_not_in_any_team, add_user_to_team, remove_user_from_team, get_team_members @@ -88,7 +88,7 @@ def get_all_users(): else: # We are going to add users! # return users that are not in the team! - team_users = get_users_not_in_team_id(team) + team_users = get_users_not_in_any_team(team) return create_good_response(users_schema.dump(team_users), 200, "users") diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 28ef27f41..30e0b3d4e 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -233,10 +233,10 @@ def get_users_by_team_id(team): @error_log -def get_users_not_in_team_id(team): +def get_users_not_in_any_team(team): """ Description: - Gets all of the users not assigned to the given team. + Gets all of the users not assigned to any team in the course. Ensures that users are enrolled in the same course as the given team. @@ -259,7 +259,7 @@ def get_users_not_in_team_id(team): and_( or_( TeamUser.team_id == None, - TeamUser.team_id != team.team_id + # TeamUser.team_id != team.team_id ), UserCourse.role_id == 5 ), diff --git a/FrontEndReact/src/View/Admin/View/ViewTeams/ViewTeams.js b/FrontEndReact/src/View/Admin/View/ViewTeams/ViewTeams.js index 07bf45859..2f610811a 100644 --- a/FrontEndReact/src/View/Admin/View/ViewTeams/ViewTeams.js +++ b/FrontEndReact/src/View/Admin/View/ViewTeams/ViewTeams.js @@ -29,7 +29,7 @@ class ViewTeams extends Component{ }, { name: "observer_id", - label: chosenCourse["use_tas"] ? "TA Name" : "Instructor Name", + label: "Observer Name", options: { filter: true, setCellHeaderProps: () => { return { width:"165px"}}, From cd212718c2415c060a3992337921f4d51c7a86a4 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Thu, 4 Apr 2024 14:05:01 -0500 Subject: [PATCH 046/160] Revert functionality to previous implementation --- BackEndFlask/models/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 30e0b3d4e..342e6b929 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -259,7 +259,7 @@ def get_users_not_in_any_team(team): and_( or_( TeamUser.team_id == None, - # TeamUser.team_id != team.team_id + TeamUser.team_id != team.team_id ), UserCourse.role_id == 5 ), From d0221c5084955c6471a3cc665fb87b4b3dbdfba2 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Thu, 4 Apr 2024 14:18:57 -0500 Subject: [PATCH 047/160] Fix --- BackEndFlask/models/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 342e6b929..e47c8c4af 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -236,7 +236,7 @@ def get_users_by_team_id(team): def get_users_not_in_any_team(team): """ Description: - Gets all of the users not assigned to any team in the course. + Gets all of the users not assigned to the given team. Ensures that users are enrolled in the same course as the given team. From d8730a6596d0f1af19ca34055f444c98b4502bc8 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Thu, 4 Apr 2024 14:21:35 -0500 Subject: [PATCH 048/160] Fix 2 --- BackEndFlask/controller/Routes/User_routes.py | 4 ++-- BackEndFlask/models/queries.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 8590b1378..b23520abf 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -43,7 +43,7 @@ get_users_by_course_id, get_users_by_course_id_and_role_id, get_users_by_team_id, - get_users_not_in_any_team, + get_users_not_in_team_id, add_user_to_team, remove_user_from_team, get_team_members @@ -88,7 +88,7 @@ def get_all_users(): else: # We are going to add users! # return users that are not in the team! - team_users = get_users_not_in_any_team(team) + team_users = get_users_not_in_team_id(team) return create_good_response(users_schema.dump(team_users), 200, "users") diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index e47c8c4af..28ef27f41 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -233,7 +233,7 @@ def get_users_by_team_id(team): @error_log -def get_users_not_in_any_team(team): +def get_users_not_in_team_id(team): """ Description: Gets all of the users not assigned to the given team. From 193acdd569deaae8b33f598ff895b81c8db4f844 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Sat, 6 Apr 2024 19:17:59 -0500 Subject: [PATCH 049/160] Added experimental test query --- BackEndFlask/controller/Routes/User_routes.py | 30 +++++++++++++++-- BackEndFlask/models/queries.py | 33 +++++++++++++++++++ BackEndFlask/models/schemas.py | 3 +- .../Admin/Add/AddTeam/AdminEditTeamMembers.js | 26 +++++++++++++-- 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index b23520abf..7843af7e6 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -46,7 +46,8 @@ get_users_not_in_team_id, add_user_to_team, remove_user_from_team, - get_team_members + get_team_members, + test_replacement_team_query, ) @@ -76,10 +77,35 @@ def get_all_users(): if(request.args and request.args.get("isAdmin")): return create_good_response(users_schema.dump(get_user_admins()), 200, "users") + if ((request.args) and (request.args.get("team_id")) and (request.args.get("course_id"))): + course_id = int(request.args.get("course_id")) + team_id = int(request.args.get("team_id")) + get_members_not_in_team = request.args.get("assign") == 'true' + + all_queried_members = test_replacement_team_query(course_id, team_id, get_members_not_in_team) + + result = [] + for i in all_queried_members: + data = {} + + data['team_user_id'] = i[0] + data['team_id'] = i[1] + data['user_id'] = i[2] + data['course_id'] = i[3] + data['role_id'] = i[4] + data['team_name'] = i[5] + data['first_name'] = i[6] + data['last_name'] = i[7] + data['email'] = i[8] + + result.append(data) + + return create_good_response(result, 200, "users") + if(request.args and request.args.get("team_id")): team_id = request.args.get("team_id") - team = get_team(team_id) # Trigger an error if not exists. + team = get_team(team_id) if request.args.get("assign"): # We are going to remove users! diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 28ef27f41..97626a27f 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -268,6 +268,39 @@ def get_users_not_in_team_id(team): ) ).all() +@error_log +def test_replacement_team_query(course_id: int, team_id: int, get_members_not_in_team: bool): + return db.session.query( + TeamUser.team_user_id, + TeamUser.team_id, + UserCourse.user_id, + UserCourse.course_id, + UserCourse.role_id, + Team.team_name, + User.first_name, + User.last_name, + User.email, + ).join( + TeamUser, + UserCourse.user_id == TeamUser.user_id, + isouter=True, + ).join( + Team, + Team.team_id == TeamUser.team_id, + isouter=True, + ).join( + User, + User.user_id == UserCourse.user_id, + ).filter( + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5, + (or_( + TeamUser.team_id == None, + TeamUser.team_id != team_id + )) if get_members_not_in_team else TeamUser.team_id == team_id + ) + ).all() @error_log def get_team_members(user_id: int, course_id: int): diff --git a/BackEndFlask/models/schemas.py b/BackEndFlask/models/schemas.py index 890af6b42..2a1f312ad 100644 --- a/BackEndFlask/models/schemas.py +++ b/BackEndFlask/models/schemas.py @@ -92,7 +92,8 @@ class Course(db.Model): class UserCourse(db.Model): __tablename__ = "UserCourse" - user_course_id = db.Column(db.Integer, primary_key=True, autoincrement=True) + __table_args__ = {'sqlite_autoincrement': True} + user_course_id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, ForeignKey(User.user_id), nullable=False) course_id = db.Column(db.Integer, ForeignKey(Course.course_id), nullable=False) active = db.Column(db.Boolean) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index b1b6c8a49..1b0f6457d 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -75,10 +75,17 @@ class AdminEditTeamMembers extends Component { var team = state.team; + // genericResourceGET( + // `/user?team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), + // "users", this, + // ); + + // temp + var chosenCourse = state.chosenCourse.course_id; genericResourceGET( - `/user?team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), - "users", this, - ); + `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}&assign=${this.props.addTeamAction === "Add"}`, + "users", this, + ); } render() { @@ -109,6 +116,19 @@ class AdminEditTeamMembers extends Component { }, }, }, + { + name: "team_name", + label: "Current Team", + options: { + filter: true, + setCellHeaderProps: () => { + return { width: "300px" }; + }, + setCellProps: () => { + return { width: "300px" }; + }, + }, + }, { name: "email", label: "Email", From 8de721b206dda209a47a1d90cad3a96c4acec467 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Sat, 6 Apr 2024 20:33:30 -0500 Subject: [PATCH 050/160] Changed get_users_by_team_id() and get_users_not_in_team_id() to include team name; changed structure of /user GET route to reflect the changes --- .../Routes/Assessment_task_routes.py | 1 + BackEndFlask/controller/Routes/User_routes.py | 37 +++----- BackEndFlask/models/queries.py | 91 +++++++++---------- .../Admin/Add/AddTeam/AdminEditTeamMembers.js | 11 +-- .../ViewTeamMembers/AdminViewTeamMembers.js | 3 +- .../Student/View/BuildTeam/ShowTeamMembers.js | 3 +- .../Student/View/Team/StudentTeamMembers.js | 3 +- 7 files changed, 69 insertions(+), 80 deletions(-) diff --git a/BackEndFlask/controller/Routes/Assessment_task_routes.py b/BackEndFlask/controller/Routes/Assessment_task_routes.py index 3f53e42dc..a483b93a2 100644 --- a/BackEndFlask/controller/Routes/Assessment_task_routes.py +++ b/BackEndFlask/controller/Routes/Assessment_task_routes.py @@ -192,6 +192,7 @@ def update_assessment_task(): if completed.team_id is not None: email_students_feedback_is_ready_to_view( get_users_by_team_id( + one_assessment_task.course_id, get_team(completed.team_id) ), diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 7843af7e6..b3942eba0 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -76,14 +76,23 @@ def get_all_users(): if(request.args and request.args.get("isAdmin")): return create_good_response(users_schema.dump(get_user_admins()), 200, "users") + + if(request.args and request.args.get("team_id")): + team_id = request.args.get("team_id") + if request.args.get("course_id"): + course_id = request.args.get("course_id") + else: + course_id = get_team(team_id).course_id - if ((request.args) and (request.args.get("team_id")) and (request.args.get("course_id"))): - course_id = int(request.args.get("course_id")) - team_id = int(request.args.get("team_id")) - get_members_not_in_team = request.args.get("assign") == 'true' + if request.args.get("assign") == 'true': + # We are going to remove users! + # return users that are in the team! + all_queried_members = get_users_by_team_id(course_id, team_id) + else: + # We are going to add users! + # return users that are not in the team! + all_queried_members = get_users_not_in_team_id(course_id, team_id) - all_queried_members = test_replacement_team_query(course_id, team_id, get_members_not_in_team) - result = [] for i in all_queried_members: data = {} @@ -102,22 +111,6 @@ def get_all_users(): return create_good_response(result, 200, "users") - if(request.args and request.args.get("team_id")): - team_id = request.args.get("team_id") - - team = get_team(team_id) - - if request.args.get("assign"): - # We are going to remove users! - # return users that are in the team! - team_users = get_users_by_team_id(team) - else: - # We are going to add users! - # return users that are not in the team! - team_users = get_users_not_in_team_id(team) - - return create_good_response(users_schema.dump(team_users), 200, "users") - if(request.args and request.args.get("course_id")): course_id = request.args.get("course_id") diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 97626a27f..c10ffb9c6 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -18,6 +18,10 @@ get_completed_assessments_by_assessment_task_id ) +from models.assessment_task import ( + get_assessment_task +) + from models.team import ( get_team ) @@ -204,9 +208,8 @@ def get_team_by_course_id_and_user_id(course_id, user_id): return teams - @error_log -def get_users_by_team_id(team): +def get_users_by_team_id(course_id: int, team_id: int): """ Description: Gets all of the users assigned to the given team. @@ -214,26 +217,41 @@ def get_users_by_team_id(team): course as the given team. Parameters: - team: Team SQLAlchemy Object (The object of a team) + course_id: int (The id of a course) + team_id: int (The id of a team) """ return db.session.query( - User - ).join( - TeamUser, - User.user_id == TeamUser.user_id - ).join( - UserCourse, - User.user_id == UserCourse.user_id - ).filter( - TeamUser.team_id == team.team_id, - UserCourse.course_id == team.course_id, - UserCourse.role_id == 5, - UserCourse.active == True - ).all() - + TeamUser.team_user_id, + TeamUser.team_id, + UserCourse.user_id, + UserCourse.course_id, + UserCourse.role_id, + Team.team_name, + User.first_name, + User.last_name, + User.email, + ).join( + TeamUser, + UserCourse.user_id == TeamUser.user_id, + isouter=True, + ).join( + Team, + Team.team_id == TeamUser.team_id, + isouter=True, + ).join( + User, + User.user_id == UserCourse.user_id, + ).filter( + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5, + UserCourse.active == True, + TeamUser.team_id == team_id, + ) + ).all() @error_log -def get_users_not_in_team_id(team): +def get_users_not_in_team_id(course_id: int, team_id: int): """ Description: Gets all of the users not assigned to the given team. @@ -241,35 +259,9 @@ def get_users_not_in_team_id(team): as the given team. Parameters: - team: Team SQLAlchemy Object (The object of a team) + course_id: int (The id of a course) + team_id: int (The id of a team) """ - return db.session.query( - User - ).join( - UserCourse, - User.user_id == UserCourse.user_id - ).join( - TeamUser, - User.user_id == TeamUser.user_id, - isouter=True - ).filter( - and_( - UserCourse.course_id == team.course_id, - and_( - and_( - or_( - TeamUser.team_id == None, - TeamUser.team_id != team.team_id - ), - UserCourse.role_id == 5 - ), - UserCourse.active == True - ) - ) - ).all() - -@error_log -def test_replacement_team_query(course_id: int, team_id: int, get_members_not_in_team: bool): return db.session.query( TeamUser.team_user_id, TeamUser.team_id, @@ -295,10 +287,11 @@ def test_replacement_team_query(course_id: int, team_id: int, get_members_not_in and_( UserCourse.course_id == course_id, UserCourse.role_id == 5, - (or_( + UserCourse.active == True, + or_( TeamUser.team_id == None, TeamUser.team_id != team_id - )) if get_members_not_in_team else TeamUser.team_id == team_id + ) ) ).all() @@ -500,12 +493,14 @@ def send_teams_and_students_email_to_view_completed_assessment_feedback(assessme Parameters: assessment_task_id: int (The id of an assessment task) """ + one_assessment_task = get_assessment_task(assessment_task_id) all_completed=get_completed_assessments_by_assessment_task_id(assessment_task_id) for completed in all_completed: if completed.team_id is not None and completed.done: email_students_feedback_is_ready_to_view( get_users_by_team_id( + one_assessment_task.course_id, get_team(completed.team_id) ) ) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index 1b0f6457d..c961e38d0 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -54,6 +54,8 @@ class AdminEditTeamMembers extends Component { var team = state.team; + var chosenCourse = state.chosenCourse.course_id; + var url = `/user?team_id=${team["team_id"]}&user_ids=${users}`; if (this.props.addTeamAction === "Add") { @@ -75,15 +77,10 @@ class AdminEditTeamMembers extends Component { var team = state.team; - // genericResourceGET( - // `/user?team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), - // "users", this, - // ); - - // temp var chosenCourse = state.chosenCourse.course_id; + genericResourceGET( - `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}&assign=${this.props.addTeamAction === "Add"}`, + `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), "users", this, ); } diff --git a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js index 7dd927267..26595938a 100644 --- a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js +++ b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js @@ -22,9 +22,10 @@ class AdminViewTeamMembers extends Component { var navbar = this.props.navbar; var state = navbar.state; var team = state.team; + var chosenCourse = state.chosenCourse.course_id; genericResourceGET( - `/user?team_id=${team["team_id"]}&assign=${true}`, + `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}&assign=${true}`, 'users', this ); } diff --git a/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js b/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js index ae5f8ef88..3ab840d80 100644 --- a/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js +++ b/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js @@ -37,9 +37,10 @@ class ShowTeamMembers extends Component { componentDidUpdate() { var navbar = this.props.navbar; var teamId = navbar.buildTeam.selectedTeam; + var chosenCourse = navbar.state.chosenCourse.course_id; if (teamId !== null && teamId !== this.state.selectedTeam) { - genericResourceGET(`/user?team_id=${teamId}`, 'users', this); + genericResourceGET(`/user?course_id=${chosenCourse}&team_id=${teamId}`, 'users', this); } } diff --git a/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js b/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js index fda9f4185..818813273 100644 --- a/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js +++ b/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js @@ -21,9 +21,10 @@ class StudentTeamMembers extends Component { var navbar = this.props.navbar; var state = navbar.state; var team = state.team; + var chosenCourse = state.chosenCourse.course_id; genericResourceGET( - `/user?team_id=${team["team_id"]}&assign=${true}`, + `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}&assign=${true}`, "users", this ); } From 9c514f7e2081200130f2f8b5d3ab4621477f7287 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Sat, 6 Apr 2024 20:43:26 -0500 Subject: [PATCH 051/160] Removed unnecessary import --- BackEndFlask/controller/Routes/User_routes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index b3942eba0..054d51dce 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -47,7 +47,6 @@ add_user_to_team, remove_user_from_team, get_team_members, - test_replacement_team_query, ) From da6570c8dea32aeb486e00f7368eb1d10564da98 Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Wed, 10 Apr 2024 18:08:22 -0500 Subject: [PATCH 052/160] Reworked logic in get_users_by_team_id() and get_users_not_in_team_id() to account for the fact that the same user can be in multiple teams if they are in multiple different courses --- BackEndFlask/controller/Routes/User_routes.py | 18 +- BackEndFlask/models/queries.py | 158 +++++++++++------- BackEndFlask/models/user_course.py | 15 +- .../Admin/Add/AddTeam/AdminEditTeamMembers.js | 2 +- 4 files changed, 116 insertions(+), 77 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 054d51dce..c94581f86 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -95,16 +95,14 @@ def get_all_users(): result = [] for i in all_queried_members: data = {} - - data['team_user_id'] = i[0] - data['team_id'] = i[1] - data['user_id'] = i[2] - data['course_id'] = i[3] - data['role_id'] = i[4] - data['team_name'] = i[5] - data['first_name'] = i[6] - data['last_name'] = i[7] - data['email'] = i[8] + + data['user_id'] = i[0] + data['course_id'] = i[1] + data['team_id'] = i[2] + data['team_name'] = i[3] + data['first_name'] = i[4] + data['last_name'] = i[5] + data['email'] = i[6] result.append(data) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index c10ffb9c6..d879b541b 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -220,35 +220,55 @@ def get_users_by_team_id(course_id: int, team_id: int): course_id: int (The id of a course) team_id: int (The id of a team) """ + all_students_in_course = db.session.query( + UserCourse.user_id, + UserCourse.course_id, + ) \ + .filter( + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5, + UserCourse.active == True, + ) + ) \ + .subquery() + + all_team_users_in_course = db.session.query( + TeamUser.team_id, + TeamUser.user_id, + Team.team_name, + ) \ + .join( + Team, + TeamUser.team_id == Team.team_id + ) \ + .filter( + Team.course_id == course_id, + ) \ + .subquery() + return db.session.query( - TeamUser.team_user_id, - TeamUser.team_id, - UserCourse.user_id, - UserCourse.course_id, - UserCourse.role_id, - Team.team_name, - User.first_name, - User.last_name, - User.email, - ).join( - TeamUser, - UserCourse.user_id == TeamUser.user_id, - isouter=True, - ).join( - Team, - Team.team_id == TeamUser.team_id, - isouter=True, - ).join( - User, - User.user_id == UserCourse.user_id, - ).filter( - and_( - UserCourse.course_id == course_id, - UserCourse.role_id == 5, - UserCourse.active == True, - TeamUser.team_id == team_id, - ) - ).all() + all_students_in_course.c.user_id, + all_students_in_course.c.course_id, + all_team_users_in_course.c.team_id, + all_team_users_in_course.c.team_name, + User.first_name, + User.last_name, + User.email, + ) \ + .join( + all_team_users_in_course, + all_students_in_course.c.user_id == all_team_users_in_course.c.user_id, + isouter=True + ) \ + .join( + User, + User.user_id == all_students_in_course.c.user_id, + ) \ + .filter( + all_team_users_in_course.c.team_id == team_id, + ) \ + .all() @error_log def get_users_not_in_team_id(course_id: int, team_id: int): @@ -262,38 +282,58 @@ def get_users_not_in_team_id(course_id: int, team_id: int): course_id: int (The id of a course) team_id: int (The id of a team) """ + all_students_in_course = db.session.query( + UserCourse.user_id, + UserCourse.course_id, + ) \ + .filter( + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5, + UserCourse.active == True, + ) + ) \ + .subquery() + + all_team_users_in_course = db.session.query( + TeamUser.team_id, + TeamUser.user_id, + Team.team_name, + ) \ + .join( + Team, + TeamUser.team_id == Team.team_id + ) \ + .filter( + Team.course_id == course_id, + ) \ + .subquery() + return db.session.query( - TeamUser.team_user_id, - TeamUser.team_id, - UserCourse.user_id, - UserCourse.course_id, - UserCourse.role_id, - Team.team_name, - User.first_name, - User.last_name, - User.email, - ).join( - TeamUser, - UserCourse.user_id == TeamUser.user_id, - isouter=True, - ).join( - Team, - Team.team_id == TeamUser.team_id, - isouter=True, - ).join( - User, - User.user_id == UserCourse.user_id, - ).filter( - and_( - UserCourse.course_id == course_id, - UserCourse.role_id == 5, - UserCourse.active == True, - or_( - TeamUser.team_id == None, - TeamUser.team_id != team_id - ) - ) - ).all() + all_students_in_course.c.user_id, + all_students_in_course.c.course_id, + all_team_users_in_course.c.team_id, + all_team_users_in_course.c.team_name, + User.first_name, + User.last_name, + User.email, + ) \ + .join( + all_team_users_in_course, + all_students_in_course.c.user_id == all_team_users_in_course.c.user_id, + isouter=True + ) \ + .join( + User, + User.user_id == all_students_in_course.c.user_id, + ) \ + .filter( + or_( + all_team_users_in_course.c.team_id == None, + all_team_users_in_course.c.team_id != team_id, + ) + ) \ + .all() @error_log def get_team_members(user_id: int, course_id: int): diff --git a/BackEndFlask/models/user_course.py b/BackEndFlask/models/user_course.py index 7be3a59c3..1a42e2789 100644 --- a/BackEndFlask/models/user_course.py +++ b/BackEndFlask/models/user_course.py @@ -84,13 +84,14 @@ def load_demo_user_course_ta_instructor(): def load_demo_user_course_student(): - for user_id in range(4, 14): - create_user_course({ - "user_id": user_id, - "course_id": 1, - "role_id": 5, - "active": True - }) + for course_id in range(1, 5): + for user_id in range(4, 14): + create_user_course({ + "user_id": user_id, + "course_id": course_id, + "role_id": 5, + "active": True + }) @error_log diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index c961e38d0..dff9a5ba1 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -78,7 +78,7 @@ class AdminEditTeamMembers extends Component { var team = state.team; var chosenCourse = state.chosenCourse.course_id; - + genericResourceGET( `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), "users", this, From baa97a9ebe29561525f338e2d2b3ac408208ac1a Mon Sep 17 00:00:00 2001 From: nmullins417 Date: Tue, 23 Apr 2024 08:43:47 -0500 Subject: [PATCH 053/160] Fixed the issues --- BackEndFlask/controller/Routes/User_routes.py | 4 +- BackEndFlask/models/queries.py | 54 +++++++++++++------ 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index c94581f86..7f6c542f5 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -148,14 +148,14 @@ def add_user(): if(request.args and request.args.get("team_id")): team_id = request.args.get("team_id") - get_team(team_id) # Trigger an error if not exists. + course_id = get_team(team_id).course_id # Trigger an error if not exists. user_ids = request.args.get("user_ids").split(",") for user_id in user_ids: get_user(user_id) # Trigger an error if not exists. - add_user_to_team(user_id, team_id) + add_user_to_team(user_id, team_id, course_id) return create_good_response([], 201, "users") diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index d879b541b..40c54897d 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -383,7 +383,7 @@ def get_team_members(user_id: int, course_id: int): @error_log -def add_user_to_team(user_id, team_id): +def add_user_to_team(user_id: int, team_id: int, course_id: int): """ Description: Adds the given user to the given team. @@ -391,23 +391,45 @@ def add_user_to_team(user_id, team_id): Parameters: user_id: int (The id of a user) team_id: int (The id of a team) + course_id: int (The id of a course) """ - team_user = TeamUser.query.filter_by( - user_id=user_id - ).first() - - if team_user is None: - return create_team_user({ - "user_id": user_id, - "team_id": team_id - }) - else: - team_user.team_id = team_id - - db.session.commit() - - return team_user + all_team_users_in_course = db.session.query( + TeamUser.team_user_id, + TeamUser.team_id, + TeamUser.user_id, + # Team.team_name, #debug only + ) \ + .join( + Team, + TeamUser.team_id == Team.team_id + ) \ + .filter( + Team.course_id == course_id, + ) \ + .all() + # See if the user_id is already enrolled in a team for that course + for user in all_team_users_in_course: + # If they are, we don't need to create a new TeamUser; we just need to + # alter the existing TeamUser affiliation for that user + if (user[2] == int(user_id)): + team_user = TeamUser.query.filter_by( + team_id=user[1], + user_id=user[2] + ).first() + + team_user.team_id = team_id + + db.session.commit() + + return team_user + + # If the user_id is NOT already enrolled in a team for that course, we must create + # a new TeamUser + return create_team_user({ + "user_id": user_id, + "team_id": team_id + }) @error_log def remove_user_from_team(user_id, team_id): From 7b0b49700063867b9e621d19a352ace9a1cbf83e Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Mon, 6 May 2024 08:43:51 -0500 Subject: [PATCH 054/160] Updated Assessment_task_routes.py by removing whitespace! --- BackEndFlask/controller/Routes/Assessment_task_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/Assessment_task_routes.py b/BackEndFlask/controller/Routes/Assessment_task_routes.py index a483b93a2..525987e5c 100644 --- a/BackEndFlask/controller/Routes/Assessment_task_routes.py +++ b/BackEndFlask/controller/Routes/Assessment_task_routes.py @@ -192,7 +192,7 @@ def update_assessment_task(): if completed.team_id is not None: email_students_feedback_is_ready_to_view( get_users_by_team_id( - one_assessment_task.course_id, + one_assessment_task.course_id, get_team(completed.team_id) ), From cd422b30b42ccd1a8fe7348570a94e2b55c99f7f Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Mon, 6 May 2024 08:47:27 -0500 Subject: [PATCH 055/160] Updated User_routes.py by removing whitespace! --- BackEndFlask/controller/Routes/User_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 7f6c542f5..2b552c7fa 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -46,7 +46,7 @@ get_users_not_in_team_id, add_user_to_team, remove_user_from_team, - get_team_members, + get_team_members ) From 0e0f3d6b0c56b9276f96f36a27d0ee0b073a2975 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Mon, 6 May 2024 08:48:09 -0500 Subject: [PATCH 056/160] Updated User_routes.py by removing whitespace! --- BackEndFlask/controller/Routes/User_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 2b552c7fa..e498d909c 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -75,7 +75,7 @@ def get_all_users(): if(request.args and request.args.get("isAdmin")): return create_good_response(users_schema.dump(get_user_admins()), 200, "users") - + if(request.args and request.args.get("team_id")): team_id = request.args.get("team_id") if request.args.get("course_id"): From 38507fd49feafd266e4c17f63809cbe83de8786b Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Mon, 6 May 2024 09:00:15 -0500 Subject: [PATCH 057/160] Updated User_routes.py by simplifying the logic and used our marshmallow schema to convert the sqlalchemy result set to json! --- BackEndFlask/controller/Routes/User_routes.py | 36 ++++++------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index e498d909c..daabc4e10 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -78,35 +78,21 @@ def get_all_users(): if(request.args and request.args.get("team_id")): team_id = request.args.get("team_id") - if request.args.get("course_id"): - course_id = request.args.get("course_id") - else: - course_id = get_team(team_id).course_id + + get_team(team_id) # Trigger an error if not exists. + + course_id = get_team(team_id).course_id if not request.args.get("course_id") else request.args.get("course_id") + + # We are going to add users by default! + # Return users that are not in the team! + all_queried_members = get_users_not_in_team_id(course_id, team_id) if request.args.get("assign") == 'true': # We are going to remove users! - # return users that are in the team! + # Return users that are in the team! all_queried_members = get_users_by_team_id(course_id, team_id) - else: - # We are going to add users! - # return users that are not in the team! - all_queried_members = get_users_not_in_team_id(course_id, team_id) - - result = [] - for i in all_queried_members: - data = {} - - data['user_id'] = i[0] - data['course_id'] = i[1] - data['team_id'] = i[2] - data['team_name'] = i[3] - data['first_name'] = i[4] - data['last_name'] = i[5] - data['email'] = i[6] - - result.append(data) - - return create_good_response(result, 200, "users") + + return create_good_response(users_schema.dump(all_queried_members), 200, "users") if(request.args and request.args.get("course_id")): course_id = request.args.get("course_id") From 17eeab8cc53211948e9be79842b34e6157504f8c Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Mon, 6 May 2024 09:00:55 -0500 Subject: [PATCH 058/160] Updated User_routes.py by adding whitespace! --- BackEndFlask/controller/Routes/User_routes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index daabc4e10..b90b1245e 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -79,7 +79,7 @@ def get_all_users(): if(request.args and request.args.get("team_id")): team_id = request.args.get("team_id") - get_team(team_id) # Trigger an error if not exists. + get_team(team_id) # Trigger an error if not exists. course_id = get_team(team_id).course_id if not request.args.get("course_id") else request.args.get("course_id") From 363d5a6a138b9645a093bb29f3663873628b891c Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Tue, 7 May 2024 08:58:26 -0500 Subject: [PATCH 059/160] Updated queries.py by removing the escaped newlines! --- BackEndFlask/models/queries.py | 64 +++++++++++----------------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 40c54897d..5cc3f91e5 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -223,29 +223,24 @@ def get_users_by_team_id(course_id: int, team_id: int): all_students_in_course = db.session.query( UserCourse.user_id, UserCourse.course_id, - ) \ - .filter( + ).filter( and_( UserCourse.course_id == course_id, UserCourse.role_id == 5, UserCourse.active == True, ) - ) \ - .subquery() + ).subquery() all_team_users_in_course = db.session.query( TeamUser.team_id, TeamUser.user_id, Team.team_name, - ) \ - .join( + ).join( Team, TeamUser.team_id == Team.team_id - ) \ - .filter( + ).filter( Team.course_id == course_id, - ) \ - .subquery() + ).subquery() return db.session.query( all_students_in_course.c.user_id, @@ -255,20 +250,16 @@ def get_users_by_team_id(course_id: int, team_id: int): User.first_name, User.last_name, User.email, - ) \ - .join( + ).join( all_team_users_in_course, all_students_in_course.c.user_id == all_team_users_in_course.c.user_id, isouter=True - ) \ - .join( + ).join( User, User.user_id == all_students_in_course.c.user_id, - ) \ - .filter( + ).filter( all_team_users_in_course.c.team_id == team_id, - ) \ - .all() + ).all() @error_log def get_users_not_in_team_id(course_id: int, team_id: int): @@ -285,29 +276,24 @@ def get_users_not_in_team_id(course_id: int, team_id: int): all_students_in_course = db.session.query( UserCourse.user_id, UserCourse.course_id, - ) \ - .filter( + ).filter( and_( UserCourse.course_id == course_id, UserCourse.role_id == 5, UserCourse.active == True, ) - ) \ - .subquery() + ).subquery() all_team_users_in_course = db.session.query( TeamUser.team_id, TeamUser.user_id, Team.team_name, - ) \ - .join( + ).join( Team, TeamUser.team_id == Team.team_id - ) \ - .filter( + ).filter( Team.course_id == course_id, - ) \ - .subquery() + ).subquery() return db.session.query( all_students_in_course.c.user_id, @@ -317,23 +303,19 @@ def get_users_not_in_team_id(course_id: int, team_id: int): User.first_name, User.last_name, User.email, - ) \ - .join( + ).join( all_team_users_in_course, all_students_in_course.c.user_id == all_team_users_in_course.c.user_id, isouter=True - ) \ - .join( + ).join( User, User.user_id == all_students_in_course.c.user_id, - ) \ - .filter( + ).filter( or_( all_team_users_in_course.c.team_id == None, all_team_users_in_course.c.team_id != team_id, ) - ) \ - .all() + ).all() @error_log def get_team_members(user_id: int, course_id: int): @@ -397,16 +379,12 @@ def add_user_to_team(user_id: int, team_id: int, course_id: int): TeamUser.team_user_id, TeamUser.team_id, TeamUser.user_id, - # Team.team_name, #debug only - ) \ - .join( + ).join( Team, TeamUser.team_id == Team.team_id - ) \ - .filter( + ).filter( Team.course_id == course_id, - ) \ - .all() + ).all() # See if the user_id is already enrolled in a team for that course for user in all_team_users_in_course: From d251a1e89e8235dae1a437c45e340587956004ce Mon Sep 17 00:00:00 2001 From: aparriaran Date: Tue, 7 May 2024 09:29:47 -0500 Subject: [PATCH 060/160] We updated the README to include the instructions on how to set up the virtual environment --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index b238864d7..e4db62ad7 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,30 @@ NOTE: - Follow the link for instructions on downloading Node.js: https://nodejs.org/en/download + -Follow the instructions for setting up virtual environment: + + Step 1: + Ensure you are in the BackEndFlask direction by running + the command: + + cd BackEndFlask + + Step 2: + Create the virtual environment by reading the command: + python3 -m venv BackEndFlaskVenv + + Step 3: + Activate the virtual environment by running the command: + source BackEndFlaskVenv/bin/activate + To Deactivate the virtual environment, run the command: + + deactivate + + To Remove the virtual environment, run the command: + + rm -r BackEndFlaskVenv + - In order to install the required packages you WILL need to be in the directory `/rubricapp/FrontEndReact/`. From dc313b0b67cc8dc6b185fa4e7076103bc3e25262 Mon Sep 17 00:00:00 2001 From: aparriaran Date: Tue, 7 May 2024 09:40:38 -0500 Subject: [PATCH 061/160] Fixed typos and added whitespace --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e4db62ad7..a28447ef1 100644 --- a/README.md +++ b/README.md @@ -163,20 +163,22 @@ NOTE: - Follow the link for instructions on downloading Node.js: https://nodejs.org/en/download - -Follow the instructions for setting up virtual environment: - + -Follow the instructions for setting up a virtual environment: + Step 1: - Ensure you are in the BackEndFlask direction by running + Ensure you are in the BackEndFlask directory by running the command: cd BackEndFlask Step 2: - Create the virtual environment by reading the command: + Create the virtual environment by running the command: + python3 -m venv BackEndFlaskVenv Step 3: Activate the virtual environment by running the command: + source BackEndFlaskVenv/bin/activate To Deactivate the virtual environment, run the command: From a4f160146e7a2772e8476c04d32b03d1d21ac402 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Tue, 7 May 2024 10:04:59 -0500 Subject: [PATCH 062/160] Updated README.md by reorganizing the instructions and sections and fixed typoes! --- README.md | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/README.md b/README.md index a28447ef1..72e512715 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ access the database directly, run the following command: and then type the password. -## Setting up the Back End environment: ## +## Installing requirements ## - Follow the link for instructions on downloading Python: @@ -163,32 +163,7 @@ NOTE: - Follow the link for instructions on downloading Node.js: https://nodejs.org/en/download - -Follow the instructions for setting up a virtual environment: - - Step 1: - Ensure you are in the BackEndFlask directory by running - the command: - - cd BackEndFlask - Step 2: - Create the virtual environment by running the command: - - python3 -m venv BackEndFlaskVenv - - Step 3: - Activate the virtual environment by running the command: - - source BackEndFlaskVenv/bin/activate - - To Deactivate the virtual environment, run the command: - - deactivate - - To Remove the virtual environment, run the command: - - rm -r BackEndFlaskVenv - - In order to install the required packages you WILL need to be in the directory `/rubricapp/FrontEndReact/`. From 46449109493e9b1514058b7740fdd518e4a070cc Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 09:17:23 -0500 Subject: [PATCH 063/160] Updated AdminEditTeam.js by removing an unused variable chosenCourse! Also changed chosenCourse variable name to courseID! --- .../src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index dff9a5ba1..2f2302e64 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -54,8 +54,6 @@ class AdminEditTeamMembers extends Component { var team = state.team; - var chosenCourse = state.chosenCourse.course_id; - var url = `/user?team_id=${team["team_id"]}&user_ids=${users}`; if (this.props.addTeamAction === "Add") { @@ -77,10 +75,10 @@ class AdminEditTeamMembers extends Component { var team = state.team; - var chosenCourse = state.chosenCourse.course_id; + var courseID = state.chosenCourse.course_id; genericResourceGET( - `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), + `/user?course_id=${courseID}&team_id=${team["team_id"]}` + (this.props.addTeamAction === "Add" ? "" : `&assign=${true}`), "users", this, ); } From 3b1eb523be5b1f9829056d3ea0b435a01a6a5ddb Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 09:19:00 -0500 Subject: [PATCH 064/160] Updated AdminViewTeamMembers.js by changing chosenCourse variable name to courseID! --- .../View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js index 26595938a..25c4afc6c 100644 --- a/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js +++ b/FrontEndReact/src/View/Admin/View/ViewTeamMembers/AdminViewTeamMembers.js @@ -22,10 +22,10 @@ class AdminViewTeamMembers extends Component { var navbar = this.props.navbar; var state = navbar.state; var team = state.team; - var chosenCourse = state.chosenCourse.course_id; + var courseID = state.chosenCourse.course_id; genericResourceGET( - `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}&assign=${true}`, + `/user?course_id=${courseID}&team_id=${team["team_id"]}&assign=${true}`, 'users', this ); } From 36f88fa0cd0507bc319d9824820634973ce8a03e Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 09:20:36 -0500 Subject: [PATCH 065/160] Updated ShowTeamMembers.js by changing chosenCourse variable name to courseID! --- .../src/View/Student/View/BuildTeam/ShowTeamMembers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js b/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js index 3ab840d80..a43e9b712 100644 --- a/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js +++ b/FrontEndReact/src/View/Student/View/BuildTeam/ShowTeamMembers.js @@ -37,10 +37,10 @@ class ShowTeamMembers extends Component { componentDidUpdate() { var navbar = this.props.navbar; var teamId = navbar.buildTeam.selectedTeam; - var chosenCourse = navbar.state.chosenCourse.course_id; + var courseID = navbar.state.chosenCourse.course_id; if (teamId !== null && teamId !== this.state.selectedTeam) { - genericResourceGET(`/user?course_id=${chosenCourse}&team_id=${teamId}`, 'users', this); + genericResourceGET(`/user?course_id=${courseID}&team_id=${teamId}`, 'users', this); } } From f45a3517c4899785f7d8099e367d31bbb751e285 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Wed, 8 May 2024 09:21:48 -0500 Subject: [PATCH 066/160] Updated StudentTeamMembers.js by changing chosenCourse variable name to courseID! --- .../src/View/Student/View/Team/StudentTeamMembers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js b/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js index 818813273..e995d323d 100644 --- a/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js +++ b/FrontEndReact/src/View/Student/View/Team/StudentTeamMembers.js @@ -21,10 +21,10 @@ class StudentTeamMembers extends Component { var navbar = this.props.navbar; var state = navbar.state; var team = state.team; - var chosenCourse = state.chosenCourse.course_id; + var courseID = state.chosenCourse.course_id; genericResourceGET( - `/user?course_id=${chosenCourse}&team_id=${team["team_id"]}&assign=${true}`, + `/user?course_id=${courseID}&team_id=${team["team_id"]}&assign=${true}`, "users", this ); } From 6682e644033eb92423923a396916a2bff421aade Mon Sep 17 00:00:00 2001 From: Aldo Velarde Date: Thu, 9 May 2024 21:14:29 -0500 Subject: [PATCH 067/160] Fixed Current team column to display team --- BackEndFlask/controller/Routes/User_routes.py | 24 ++++++++++--------- .../Admin/Add/AddTeam/AdminEditTeamMembers.js | 6 ++++- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index b90b1245e..8a617a2d8 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -46,7 +46,6 @@ get_users_not_in_team_id, add_user_to_team, remove_user_from_team, - get_team_members ) @@ -75,31 +74,31 @@ def get_all_users(): if(request.args and request.args.get("isAdmin")): return create_good_response(users_schema.dump(get_user_admins()), 200, "users") - - if(request.args and request.args.get("team_id")): + + if(request.args and request.args.get("course_id") and request.args.get("team_id")): team_id = request.args.get("team_id") get_team(team_id) # Trigger an error if not exists. course_id = get_team(team_id).course_id if not request.args.get("course_id") else request.args.get("course_id") - + # We are going to add users by default! # Return users that are not in the team! - all_queried_members = get_users_not_in_team_id(course_id, team_id) + all_users = get_users_not_in_team_id(course_id, team_id) - if request.args.get("assign") == 'true': + if request.args.get("assign") == True: # We are going to remove users! # Return users that are in the team! - all_queried_members = get_users_by_team_id(course_id, team_id) - - return create_good_response(users_schema.dump(all_queried_members), 200, "users") - + all_users = get_users_by_team_id(course_id, team_id) + + return create_good_response(users_schema.dump(all_users), 200, "users") + if(request.args and request.args.get("course_id")): course_id = request.args.get("course_id") get_course(course_id) # Trigger an error if not exists. - if request.args.get("role_id"): + if(request.args.get("role_id")): role_id = request.args.get("role_id") get_role(role_id) # Trigger an error if not exists. @@ -186,6 +185,7 @@ def add_user(): return create_bad_response(f"An error occurred creating a user: {e}", "users", 400) + @bp.route('/user', methods = ['PUT']) @jwt_required() @bad_token_check() @@ -252,6 +252,8 @@ class Meta: 'first_name', 'last_name', 'email', + 'team_id', + 'team_name', 'lms_id', 'consent', 'owner_id', diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index 2f2302e64..7364b3d6e 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -84,6 +84,7 @@ class AdminEditTeamMembers extends Component { } render() { + const columns = [ { name: "first_name", @@ -122,8 +123,11 @@ class AdminEditTeamMembers extends Component { setCellProps: () => { return { width: "300px" }; }, + customBodyRender: (value) => { + return value ? value : "No team assigned"; + } }, - }, + }, { name: "email", label: "Email", From e334a44a48f4d918823b8915b8f2efef941abf43 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 09:58:51 -0500 Subject: [PATCH 068/160] Updated User_routes.py by removing whitespace! --- BackEndFlask/controller/Routes/User_routes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 8a617a2d8..eb7ddc513 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -49,6 +49,7 @@ ) + @bp.route('/user', methods = ['GET']) @jwt_required() @bad_token_check() @@ -74,7 +75,7 @@ def get_all_users(): if(request.args and request.args.get("isAdmin")): return create_good_response(users_schema.dump(get_user_admins()), 200, "users") - + if(request.args and request.args.get("course_id") and request.args.get("team_id")): team_id = request.args.get("team_id") @@ -92,7 +93,7 @@ def get_all_users(): all_users = get_users_by_team_id(course_id, team_id) return create_good_response(users_schema.dump(all_users), 200, "users") - + if(request.args and request.args.get("course_id")): course_id = request.args.get("course_id") @@ -185,7 +186,6 @@ def add_user(): return create_bad_response(f"An error occurred creating a user: {e}", "users", 400) - @bp.route('/user', methods = ['PUT']) @jwt_required() @bad_token_check() From a278be7342b820074ab582b85e02f4d0c5b9ac00 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 10:02:25 -0500 Subject: [PATCH 069/160] Updated User_routes.py by reorganizing some of the if statements in get_all_users()! --- BackEndFlask/controller/Routes/User_routes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index eb7ddc513..9d90b615f 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -56,6 +56,9 @@ @AuthCheck() def get_all_users(): try: + if(request.args and request.args.get("isAdmin")): + return create_good_response(users_schema.dump(get_user_admins()), 200, "users") + if(request.args and request.args.get("team_ids")): team_ids = request.args.get("team_ids").split(",") @@ -73,9 +76,6 @@ def get_all_users(): return create_good_response(teams_and_team_members, 200, "users") - if(request.args and request.args.get("isAdmin")): - return create_good_response(users_schema.dump(get_user_admins()), 200, "users") - if(request.args and request.args.get("course_id") and request.args.get("team_id")): team_id = request.args.get("team_id") @@ -105,6 +105,7 @@ def get_all_users(): get_role(role_id) # Trigger an error if not exists. all_users = get_users_by_course_id_and_role_id(course_id, role_id) + else: all_users = get_users_by_course_id(course_id) From 720484e3e9a8b80ad2856d2c47bbfa56fe188984 Mon Sep 17 00:00:00 2001 From: Aldo Velarde Date: Thu, 11 Apr 2024 12:29:35 -0500 Subject: [PATCH 070/160] Fixed bug encountered on Unit of Assesssment --- .../src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js index e2c3f80d9..aacf15ada 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js +++ b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js @@ -101,10 +101,10 @@ class AdminAddAssessmentTask extends Component { }; handleTeams = (event) => { - const test = event.target.value === 'true' ? true : false; + const unit_of_assessment = event.target.value === 'true' ? true : false; this.setState({ - usingTeams: test, + usingTeams: unit_of_assessment, }); }; @@ -268,7 +268,6 @@ class AdminAddAssessmentTask extends Component {
Rubric - Date: Fri, 10 May 2024 09:39:21 -0500 Subject: [PATCH 076/160] Updated ErrorMessage.js by removing whitespace! --- FrontEndReact/src/View/Error/ErrorMessage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/FrontEndReact/src/View/Error/ErrorMessage.js b/FrontEndReact/src/View/Error/ErrorMessage.js index bda5c5ca7..ba5eb5b78 100644 --- a/FrontEndReact/src/View/Error/ErrorMessage.js +++ b/FrontEndReact/src/View/Error/ErrorMessage.js @@ -6,7 +6,6 @@ import { Box, Alert } from '@mui/material'; class ErrorMessage extends Component { render() { - var displayedMessage = this.props.errorMessage; return( From b2b9b63dbd1cb90ab3c7ca17b8526a5f78fa711c Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 09:40:22 -0500 Subject: [PATCH 077/160] Updated StudentConfirmCurrentTeam.js by removing whitespace! --- .../Student/View/ConfirmCurrentTeam/StudentConfirmCurrentTeam.js | 1 - 1 file changed, 1 deletion(-) diff --git a/FrontEndReact/src/View/Student/View/ConfirmCurrentTeam/StudentConfirmCurrentTeam.js b/FrontEndReact/src/View/Student/View/ConfirmCurrentTeam/StudentConfirmCurrentTeam.js index 8cd91acaf..01d48f4e0 100644 --- a/FrontEndReact/src/View/Student/View/ConfirmCurrentTeam/StudentConfirmCurrentTeam.js +++ b/FrontEndReact/src/View/Student/View/ConfirmCurrentTeam/StudentConfirmCurrentTeam.js @@ -18,7 +18,6 @@ class StudentConfirmCurrentTeam extends Component { } componentDidMount() { - var courseId = this.props.navbar.state.chosenCourse["course_id"]; genericResourceGET( From 1a4ac255b9f98805e0b7c73ac5dc02a064591307 Mon Sep 17 00:00:00 2001 From: Aldo Velarde Date: Fri, 10 May 2024 00:15:14 -0500 Subject: [PATCH 078/160] Fixed CSS on Role Field! --- FrontEndReact/src/SBStyles.css | 6 +++++- .../src/View/Admin/Add/AddUsers/AdminAddUser.js | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/FrontEndReact/src/SBStyles.css b/FrontEndReact/src/SBStyles.css index 8a4c39995..7c4814656 100644 --- a/FrontEndReact/src/SBStyles.css +++ b/FrontEndReact/src/SBStyles.css @@ -13,7 +13,6 @@ } - /* Table aligment */ @media only screen and (max-width: 768px) { @@ -156,6 +155,11 @@ h4, h5 { gap: 15px; } +.errorSelect { + color: red; +} + + /* OLD CSS */ /*------------------------------------------------- diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 404cab64b..f48ff4c3b 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -299,8 +299,8 @@ class AdminAddUser extends Component { /> { !navbar.props.isSuperAdmin && - - Role + + Role - - {errors.role} + {errors.role ? "Role cannot be empty" : ""} } From fa8941991108ead0a2e7cce9c841904f34c008db Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 09:46:01 -0500 Subject: [PATCH 079/160] Updated SBStyles.css by removing whitespace! --- FrontEndReact/src/SBStyles.css | 41 ++++++++++++++-------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/FrontEndReact/src/SBStyles.css b/FrontEndReact/src/SBStyles.css index 7c4814656..926f3fcc2 100644 --- a/FrontEndReact/src/SBStyles.css +++ b/FrontEndReact/src/SBStyles.css @@ -3,6 +3,7 @@ min-height: 100vh; max-height: auto; } + /* Primary Color */ .primary-color { background-color: #2E8BEF !important; @@ -12,9 +13,7 @@ background-color: #E0E0E0 !important; } - /* Table aligment */ - @media only screen and (max-width: 768px) { .button-column-alignment { text-align: left !important; @@ -49,7 +48,6 @@ h4, h5 { } } - /* Secondary elements alignment */ .content-spacing { display: flex; @@ -111,7 +109,6 @@ h4, h5 { @media screen and (max-width: 600px) { width: 100%; } - } .assessment-title-spacing { @@ -159,14 +156,13 @@ h4, h5 { color: red; } - /* OLD CSS */ /*------------------------------------------------- Was from AddSyles.css -------------------------------------------------*/ label { - float: left; + float: left; text-align: left; } @@ -201,21 +197,21 @@ input { width: 25%; margin-top: 6px; } - + /* Floating column for inputs: 75% width */ .col-75 { float: left; width: 75%; margin-top: 6px; } - - /* Clear floats after the columns */ + +/* Clear floats after the columns */ .row:after { content: ""; display: table; clear: both; } - + /* Responsive layout - when the screen is less than 600px wide, make the two columns stack on top of each other instead of next to each other */ @media screen and (max-width: 600px) { .col-25, .col-75, input[type=submit] { @@ -230,7 +226,7 @@ input { .container { text-align: center; } - + .adminHeader { background-color: lightblue; display: flex; @@ -240,20 +236,20 @@ input { color: black; /* font-size: calc(10px + 2vmin); */ } - + * { box-sizing: border-box; } - + body { margin: 0; /*font-family: 'Cabin;*/ } - + .site-title { font-size: 2rem; } - + .navbar { background-color: #2E8BEF; color: white; @@ -263,7 +259,7 @@ input { gap: 2rem; padding: 0 1rem; } - + .navbar ul { padding: 0; margin: 0; @@ -273,7 +269,7 @@ input { align-items: center; gap: 1rem; } - + .navbar a { color: inherit; text-decoration: none; @@ -282,20 +278,20 @@ input { align-items: center; padding: .25rem; } - + .navbar ul button { height: fit-content; } - + .navbar ul button img { height: 2rem; margin-left: 10px; } - + /* .navbar li.active { background-color: #555; } */ - + /* .navbar li:hover { background-color: #777; } */ @@ -304,7 +300,6 @@ input { Was from index.css -------------------------------------------------*/ - #slider { height: 0.75rem; width: 100% !important; @@ -319,8 +314,6 @@ input { font-size: 1.25rem !important; } - - /*------------------------------------------------- Was from slider.css -------------------------------------------------*/ From 694492e691622304db59214b7c477427b1204421 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 09:51:19 -0500 Subject: [PATCH 080/160] Updated AdminAddUser.js by removing helperText on Select component! --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 1 - 1 file changed, 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index f48ff4c3b..339a9cb6e 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -309,7 +309,6 @@ class AdminAddUser extends Component { label="Role" defaultValue="test" error={!!errors.role} - helperText={errors.role} onChange={this.handleSelect} required aria-label="addUserRoleDropDown" From 6fe137b29568534181b990731447c98fcc89c5f7 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 10:04:16 -0500 Subject: [PATCH 081/160] Updated User_routes.py by adding back the needed import of get_team_members! --- BackEndFlask/controller/Routes/User_routes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 5923f99fa..13f0f55ba 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -46,6 +46,7 @@ get_users_not_in_team_id, add_user_to_team, remove_user_from_team, + get_team_members ) From 30e4fe5e2330a087913e99b33231b0a2841827be Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 10:08:13 -0500 Subject: [PATCH 082/160] Updated AdminEditTeamMembers.js by changing the variable name of value to teamName! --- .../src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js index 7364b3d6e..df70f6f3b 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js +++ b/FrontEndReact/src/View/Admin/Add/AddTeam/AdminEditTeamMembers.js @@ -123,8 +123,8 @@ class AdminEditTeamMembers extends Component { setCellProps: () => { return { width: "300px" }; }, - customBodyRender: (value) => { - return value ? value : "No team assigned"; + customBodyRender: (teamName) => { + return teamName ? teamName : "No team assigned"; } }, }, From 35d1c98a33647521d38f98cf8665c6c4bae247dc Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 11:47:23 -0500 Subject: [PATCH 083/160] Updated User_routes.py by removing whitespace and changing True to the string of true! --- BackEndFlask/controller/Routes/User_routes.py | 6 +- BackEndFlask/models/queries.py | 93 +++++-------------- 2 files changed, 28 insertions(+), 71 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 13f0f55ba..b0597647d 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -86,13 +86,13 @@ def get_all_users(): # We are going to add users by default! # Return users that are not in the team! - all_users = get_users_not_in_team_id(course_id, team_id) + all_users = get_users_not_in_team_id(course_id, team_id) - if request.args.get("assign") == True: + if request.args.get("assign") == 'true': # We are going to remove users! # Return users that are in the team! all_users = get_users_by_team_id(course_id, team_id) - + return create_good_response(users_schema.dump(all_users), 200, "users") if(request.args and request.args.get("course_id")): diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 5cc3f91e5..586c84dad 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -220,45 +220,28 @@ def get_users_by_team_id(course_id: int, team_id: int): course_id: int (The id of a course) team_id: int (The id of a team) """ - all_students_in_course = db.session.query( - UserCourse.user_id, - UserCourse.course_id, - ).filter( - and_( - UserCourse.course_id == course_id, - UserCourse.role_id == 5, - UserCourse.active == True, - ) - ).subquery() - - all_team_users_in_course = db.session.query( - TeamUser.team_id, - TeamUser.user_id, - Team.team_name, - ).join( - Team, - TeamUser.team_id == Team.team_id - ).filter( - Team.course_id == course_id, - ).subquery() - return db.session.query( - all_students_in_course.c.user_id, - all_students_in_course.c.course_id, - all_team_users_in_course.c.team_id, - all_team_users_in_course.c.team_name, + User.user_id, User.first_name, User.last_name, User.email, + Team.team_id, + Team.team_name, + ).join( + UserCourse, + User.user_id == UserCourse.user_id ).join( - all_team_users_in_course, - all_students_in_course.c.user_id == all_team_users_in_course.c.user_id, - isouter=True + TeamUser, + User.user_id == TeamUser.user_id ).join( - User, - User.user_id == all_students_in_course.c.user_id, + Team, + TeamUser.team_id == Team.team_id ).filter( - all_team_users_in_course.c.team_id == team_id, + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5, + TeamUser.team_id == team_id + ) ).all() @error_log @@ -273,49 +256,23 @@ def get_users_not_in_team_id(course_id: int, team_id: int): course_id: int (The id of a course) team_id: int (The id of a team) """ - all_students_in_course = db.session.query( - UserCourse.user_id, - UserCourse.course_id, - ).filter( - and_( - UserCourse.course_id == course_id, - UserCourse.role_id == 5, - UserCourse.active == True, - ) - ).subquery() - - all_team_users_in_course = db.session.query( - TeamUser.team_id, - TeamUser.user_id, - Team.team_name, - ).join( - Team, - TeamUser.team_id == Team.team_id - ).filter( - Team.course_id == course_id, - ).subquery() - return db.session.query( - all_students_in_course.c.user_id, - all_students_in_course.c.course_id, - all_team_users_in_course.c.team_id, - all_team_users_in_course.c.team_name, + User.user_id, User.first_name, User.last_name, - User.email, + User.email ).join( - all_team_users_in_course, - all_students_in_course.c.user_id == all_team_users_in_course.c.user_id, - isouter=True + UserCourse, + User.user_id == UserCourse.user_id ).join( - User, - User.user_id == all_students_in_course.c.user_id, + TeamUser, + User.user_id != TeamUser.user_id ).filter( - or_( - all_team_users_in_course.c.team_id == None, - all_team_users_in_course.c.team_id != team_id, + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5 ) - ).all() + ).distinct().all() @error_log def get_team_members(user_id: int, course_id: int): From 8f4f3500c20aa7e164c0c12f3dc3902b3f7bea47 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 11:56:06 -0500 Subject: [PATCH 084/160] Updated User_routes.py by updating the renaming of the function from get_users_not_in_team_id() to get_users_not_in_a_team()! Updated queries.py by changing the description of get_users_not_in_a_team()! --- BackEndFlask/controller/Routes/User_routes.py | 4 ++-- BackEndFlask/models/queries.py | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index b0597647d..759618a1b 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -43,7 +43,7 @@ get_users_by_course_id, get_users_by_course_id_and_role_id, get_users_by_team_id, - get_users_not_in_team_id, + get_users_not_in_a_team, add_user_to_team, remove_user_from_team, get_team_members @@ -86,7 +86,7 @@ def get_all_users(): # We are going to add users by default! # Return users that are not in the team! - all_users = get_users_not_in_team_id(course_id, team_id) + all_users = get_users_not_in_a_team(course_id, team_id) if request.args.get("assign") == 'true': # We are going to remove users! diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 586c84dad..73966e2b3 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -245,16 +245,14 @@ def get_users_by_team_id(course_id: int, team_id: int): ).all() @error_log -def get_users_not_in_team_id(course_id: int, team_id: int): +def get_users_not_in_a_team(course_id: int): """ Description: - Gets all of the users not assigned to the given team. - Ensures that users are enrolled in the same course - as the given team. + Gets all of the users not assigned to a team. + Ensures that users are enrolled in the given course. Parameters: course_id: int (The id of a course) - team_id: int (The id of a team) """ return db.session.query( User.user_id, From 7fc9ad9652d81cf1f416f7a74358371cd21c7496 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 11:58:16 -0500 Subject: [PATCH 085/160] Updated queries.py by adding whitespace! --- BackEndFlask/models/queries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 73966e2b3..26131e6f8 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -489,6 +489,7 @@ def send_teams_and_students_email_to_view_completed_assessment_feedback(assessme assessment_task_id: int (The id of an assessment task) """ one_assessment_task = get_assessment_task(assessment_task_id) + all_completed=get_completed_assessments_by_assessment_task_id(assessment_task_id) for completed in all_completed: From b013617a5925d078062bb4c0a9d340b351406bec Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 13:30:16 -0500 Subject: [PATCH 086/160] Updated queries.py by simplfying the logic in add_user_to_team()! --- BackEndFlask/models/queries.py | 56 +++++++++++++--------------------- 1 file changed, 22 insertions(+), 34 deletions(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 26131e6f8..acbe6fc39 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -3,7 +3,8 @@ from models.schemas import * from models.team_user import ( - create_team_user + create_team_user, + replace_team_user ) from models.user import ( @@ -320,7 +321,7 @@ def get_team_members(user_id: int, course_id: int): @error_log -def add_user_to_team(user_id: int, team_id: int, course_id: int): +def add_user_to_team(user_id: int, team_id: int): """ Description: Adds the given user to the given team. @@ -328,41 +329,28 @@ def add_user_to_team(user_id: int, team_id: int, course_id: int): Parameters: user_id: int (The id of a user) team_id: int (The id of a team) - course_id: int (The id of a course) """ - all_team_users_in_course = db.session.query( - TeamUser.team_user_id, - TeamUser.team_id, - TeamUser.user_id, - ).join( - Team, - TeamUser.team_id == Team.team_id + team_user = db.session.query( + TeamUser ).filter( - Team.course_id == course_id, - ).all() + and_( + TeamUser.team_id == team_id, + TeamUser.user_id == user_id + ) + ).first() + + team_user_json = { + "team_id": team_id, + "user_id": user_id + } + + if not team_user: + return create_team_user(team_user_json) - # See if the user_id is already enrolled in a team for that course - for user in all_team_users_in_course: - # If they are, we don't need to create a new TeamUser; we just need to - # alter the existing TeamUser affiliation for that user - if (user[2] == int(user_id)): - team_user = TeamUser.query.filter_by( - team_id=user[1], - user_id=user[2] - ).first() - - team_user.team_id = team_id - - db.session.commit() - - return team_user - - # If the user_id is NOT already enrolled in a team for that course, we must create - # a new TeamUser - return create_team_user({ - "user_id": user_id, - "team_id": team_id - }) + return replace_team_user( + team_user_json, + team_user.team_user_id + ) @error_log def remove_user_from_team(user_id, team_id): From 4bd4f184e00fa2d5911512171a93c1aec9136ab0 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 16:13:28 -0500 Subject: [PATCH 087/160] Updated User_routes.py by adding the decorators to the /team_members GET method! Also removed the course_id to add_user_to_team()! Also changed users to students in the functions get_users_by_team_id() and get_users_not_in_a_team()! Also changed users to students in comments! Updated queries.py by adding the team_id parameter to get_users_not_in_a_team(), updated the description, and added the code to query all of the students not in a team and all of the students in other teams to be returned and sorted by user_id! Also added whitespace! --- BackEndFlask/controller/Routes/User_routes.py | 23 ++++--- BackEndFlask/models/queries.py | 66 +++++++++++++++---- 2 files changed, 66 insertions(+), 23 deletions(-) diff --git a/BackEndFlask/controller/Routes/User_routes.py b/BackEndFlask/controller/Routes/User_routes.py index 759618a1b..50ddba0be 100644 --- a/BackEndFlask/controller/Routes/User_routes.py +++ b/BackEndFlask/controller/Routes/User_routes.py @@ -42,8 +42,8 @@ from models.queries import ( get_users_by_course_id, get_users_by_course_id_and_role_id, - get_users_by_team_id, - get_users_not_in_a_team, + get_students_by_team_id, + get_students_not_in_a_team, add_user_to_team, remove_user_from_team, get_team_members @@ -84,14 +84,14 @@ def get_all_users(): course_id = get_team(team_id).course_id if not request.args.get("course_id") else request.args.get("course_id") - # We are going to add users by default! - # Return users that are not in the team! - all_users = get_users_not_in_a_team(course_id, team_id) + # We are going to add students by default! + # Return students that are not in the team! + all_users = get_students_not_in_a_team(course_id, team_id) if request.args.get("assign") == 'true': - # We are going to remove users! - # Return users that are in the team! - all_users = get_users_by_team_id(course_id, team_id) + # We are going to remove students! + # Return students that are in the team! + all_users = get_students_by_team_id(course_id, team_id) return create_good_response(users_schema.dump(all_users), 200, "users") @@ -126,7 +126,11 @@ def get_all_users(): except Exception as e: return create_bad_response(f"An error occurred retrieving all users: {e}", "users", 400) + @bp.route('/team_members', methods=['GET']) +@jwt_required() +@bad_token_check() +@AuthCheck() def get_all_team_members(): try: if request.args and request.args.get("course_id") and request.args.get("user_id"): @@ -147,6 +151,7 @@ def get_all_team_members(): except Exception as e: return create_bad_response(f"An error occurred retrieving team members: {e}", "team_members", 400) + @bp.route('/user', methods = ['POST']) @jwt_required() @bad_token_check() @@ -163,7 +168,7 @@ def add_user(): for user_id in user_ids: get_user(user_id) # Trigger an error if not exists. - add_user_to_team(user_id, team_id, course_id) + add_user_to_team(user_id, team_id) return create_good_response([], 201, "users") diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index acbe6fc39..a77330be1 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -99,6 +99,7 @@ def get_users_by_course_id(course_id): return users_and_role_ids + @error_log def get_users_by_course_id_and_role_id(course_id, role_id): """ @@ -209,8 +210,9 @@ def get_team_by_course_id_and_user_id(course_id, user_id): return teams + @error_log -def get_users_by_team_id(course_id: int, team_id: int): +def get_students_by_team_id(course_id: int, team_id: int): """ Description: Gets all of the users assigned to the given team. @@ -245,34 +247,67 @@ def get_users_by_team_id(course_id: int, team_id: int): ) ).all() + @error_log -def get_users_not_in_a_team(course_id: int): +def get_students_not_in_a_team(course_id: int, team_id: int): """ Description: - Gets all of the users not assigned to a team. - Ensures that users are enrolled in the given course. + Gets all of the students not assigned to a team. + Ensures that students are enrolled in the given course. Parameters: course_id: int (The id of a course) + team_id: int (The id of a team) """ - return db.session.query( + all_students_not_in_a_team = db.session.query( User.user_id, User.first_name, User.last_name, - User.email + User.email, ).join( UserCourse, User.user_id == UserCourse.user_id + ).filter( + and_( + UserCourse.course_id == course_id, + UserCourse.role_id == 5, + UserCourse.user_id.notin_( + db.session.query( + TeamUser.user_id + ) + ), + ) + ).all() + + all_students_in_other_teams = db.session.query( + User.user_id, + User.first_name, + User.last_name, + User.email, + Team.team_id, + Team.team_name ).join( TeamUser, - User.user_id != TeamUser.user_id + TeamUser.user_id == User.user_id + ).join( + Team, + Team.team_id == TeamUser.team_id + ).join( + UserCourse, + UserCourse.user_id == User.user_id ).filter( and_( UserCourse.course_id == course_id, - UserCourse.role_id == 5 + UserCourse.role_id == 5, + TeamUser.team_id != team_id ) ).distinct().all() + return sorted( + all_students_not_in_a_team + all_students_in_other_teams + ) + + @error_log def get_team_members(user_id: int, course_id: int): """ @@ -325,6 +360,8 @@ def add_user_to_team(user_id: int, team_id: int): """ Description: Adds the given user to the given team. + Or updates the current team the user + is assigned to the new given team. Parameters: user_id: int (The id of a user) @@ -333,10 +370,7 @@ def add_user_to_team(user_id: int, team_id: int): team_user = db.session.query( TeamUser ).filter( - and_( - TeamUser.team_id == team_id, - TeamUser.user_id == user_id - ) + TeamUser.user_id == user_id ).first() team_user_json = { @@ -344,7 +378,7 @@ def add_user_to_team(user_id: int, team_id: int): "user_id": user_id } - if not team_user: + if team_user == None: return create_team_user(team_user_json) return replace_team_user( @@ -352,6 +386,7 @@ def add_user_to_team(user_id: int, team_id: int): team_user.team_user_id ) + @error_log def remove_user_from_team(user_id, team_id): """ @@ -465,6 +500,7 @@ def get_rubrics_and_total_categories(user_id): return all_rubrics_and_total_categories + @error_log def send_teams_and_students_email_to_view_completed_assessment_feedback(assessment_task_id): """ @@ -483,7 +519,7 @@ def send_teams_and_students_email_to_view_completed_assessment_feedback(assessme for completed in all_completed: if completed.team_id is not None and completed.done: email_students_feedback_is_ready_to_view( - get_users_by_team_id( + get_students_by_team_id( one_assessment_task.course_id, get_team(completed.team_id) ) @@ -508,6 +544,7 @@ def get_all_checkins_for_assessment(assessment_task_id): return checkins + @error_log def get_completed_assessment_with_team_name(assessment_task_id): """ @@ -536,6 +573,7 @@ def get_completed_assessment_with_team_name(assessment_task_id): return complete_assessments + @error_log def get_completed_assessment_by_user_id(course_id, user_id): From a34fceac2e836aa9c62c2f4e615494f529b93574 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 16:17:03 -0500 Subject: [PATCH 088/160] Updated Assessment_task_routes.py by changing users to students for get_users_by_team_id()! Updated queries.py by removing whitespace! --- BackEndFlask/controller/Routes/Assessment_task_routes.py | 4 ++-- BackEndFlask/models/queries.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/controller/Routes/Assessment_task_routes.py b/BackEndFlask/controller/Routes/Assessment_task_routes.py index 525987e5c..15b7a9de8 100644 --- a/BackEndFlask/controller/Routes/Assessment_task_routes.py +++ b/BackEndFlask/controller/Routes/Assessment_task_routes.py @@ -30,7 +30,7 @@ ) from models.queries import ( - get_users_by_team_id + get_students_by_team_id ) @@ -191,7 +191,7 @@ def update_assessment_task(): for completed in list_of_completed_assessments: if completed.team_id is not None: email_students_feedback_is_ready_to_view( - get_users_by_team_id( + get_students_by_team_id( one_assessment_task.course_id, get_team(completed.team_id) ), diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index a77330be1..3e88c7028 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -217,7 +217,7 @@ def get_students_by_team_id(course_id: int, team_id: int): Description: Gets all of the users assigned to the given team. Ensures that users are enrolled in the same - course as the given team. + course as the given team. Parameters: course_id: int (The id of a course) From 24eaaf94dcef04e12c2d5f8c976828086bea9b0a Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 16:30:04 -0500 Subject: [PATCH 089/160] Updated AdminImportAssessmentTasks.test.js by removing whitespace and adding a missing t to aiasb! --- .../ImportTasks/__tests__/AdminImportAssessmentTasks.test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js index f002723b3..4438c8773 100644 --- a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js @@ -131,8 +131,6 @@ test('AdminImportAssessmentTasks.test.js Test 4: Should render an error message expectElementWithAriaLabelToHaveErrorMessage(aiacs, "Invalid Form: Missing Course!"); }, 3000); }); - - }); @@ -163,7 +161,7 @@ test('AdminImportAssessmentTasks.test.js Test 5: Should refresh and return back clickFirstElementWithAriaLabel(aiacc); - clickElementWithAriaLabel(aiasb) + clickElementWithAriaLabel(aiatsb) }, 3000); }); From e8a6bdfd52f40325fb178b65f99c22d3cba9e439 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 16:34:27 -0500 Subject: [PATCH 090/160] Updated AdminImportAssessmentTasks.test.js by adding missing semi-colons! --- .../__tests__/AdminImportAssessmentTasks.test.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js index 4438c8773..abaa3099e 100644 --- a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js @@ -20,16 +20,16 @@ var lb = 'loginButton'; var ei = 'emailInput'; var pi = 'passwordInput'; var ct = 'coursesTitle'; -var iab = "importAssessmentButton" +var iab = "importAssessmentButton"; var aiatt = "adminImportAssessmentTasksTitle"; var vcib = "viewCourseIconButton"; var rt = "rosterTitle"; var at = "assessmentTab"; -var aiatcb = "adminImportAssessmentTaskCancelButton" +var aiatcb = "adminImportAssessmentTaskCancelButton"; var aiatsb = "adminImportAssessmentTasksSubmitButton"; var aiacs = "adminImportAssessmentTasksCourseSelect"; var aiacc = "adminImportAssessmentCourseChoice"; -var aiacd = "adminImportAssessmentCourseDropdown" +var aiacd = "adminImportAssessmentCourseDropdown"; @@ -122,7 +122,6 @@ test('AdminImportAssessmentTasks.test.js Test 4: Should render an error message await waitFor(() => { setTimeout(() => { clickElementWithAriaLabel(aiatsb); - }, 3000); }); @@ -157,11 +156,11 @@ test('AdminImportAssessmentTasks.test.js Test 5: Should refresh and return back await waitFor(() => { setTimeout(() => { - clickElementWithAriaLabel(aiacd) + clickElementWithAriaLabel(aiacd); clickFirstElementWithAriaLabel(aiacc); - clickElementWithAriaLabel(aiatsb) + clickElementWithAriaLabel(aiatsb); }, 3000); }); From 1eca486728a0d0ad7d228a365e1394f2e46fd1ac Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 16:40:43 -0500 Subject: [PATCH 091/160] Updated AdminImportAssesmsentTasks.test.js by adding a jest test for the back button! --- .../AdminImportAssessmentTasks.test.js | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js index abaa3099e..9d5dbf0c8 100644 --- a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js @@ -20,11 +20,12 @@ var lb = 'loginButton'; var ei = 'emailInput'; var pi = 'passwordInput'; var ct = 'coursesTitle'; +var rt = "rosterTitle"; +var at = "assessmentTab"; var iab = "importAssessmentButton"; var aiatt = "adminImportAssessmentTasksTitle"; +var mhbb = "mainHeaderBackButton"; var vcib = "viewCourseIconButton"; -var rt = "rosterTitle"; -var at = "assessmentTab"; var aiatcb = "adminImportAssessmentTaskCancelButton"; var aiatsb = "adminImportAssessmentTasksSubmitButton"; var aiacs = "adminImportAssessmentTasksCourseSelect"; @@ -33,7 +34,7 @@ var aiacd = "adminImportAssessmentCourseDropdown"; -test("NOTE: Tests 1-_ will not pass if Demo Data is not loaded!", () => { +test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { expect(true).toBe(true); }); @@ -67,7 +68,7 @@ test('AdminImportAssessmentTasks.test.js Test 1: Should render the AdminImportAs }); -test('AdminImportAssessmentTasks.test.js Test 3: Should render the page that came before given that the Cancel button is clicked', async () => { +test('AdminImportAssessmentTasks.test.js Test 2: Should render the page that came before given that the Cancel button is clicked', async () => { render(); await waitFor(() => { @@ -98,6 +99,37 @@ test('AdminImportAssessmentTasks.test.js Test 3: Should render the page that cam }); +test('AdminImportAssessmentTasks.test.js Test 3: Should render the page that came before given that the back button is clicked', async () => { + render(); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(ct); + }); + + clickFirstElementWithAriaLabel(vcib); + + await waitFor(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }); + + clickElementWithAriaLabel(at); + + await waitFor(() => { + clickElementWithAriaLabel(iab); + + expectElementWithAriaLabelToBeInDocument(aiatt); + }); + + clickElementWithAriaLabel(mhbb); + + await waitFor(() => { + setTimeout(() => { + expectElementWithAriaLabelToBeInDocument(rt); + }, 3000); + }); +}); + + test('AdminImportAssessmentTasks.test.js Test 4: Should render an error message on the page when no input is given', async () => { render(); From 469e677104998fb39252b88b8b5026a897f97f9d Mon Sep 17 00:00:00 2001 From: aparriaran Date: Fri, 10 May 2024 16:49:20 -0500 Subject: [PATCH 092/160] updated test 9 --- .../View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js index 230647a9a..8c4988229 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/__tests__/AdminBulkUpload.test.js @@ -311,10 +311,11 @@ test("AdminBulkUpload.test.js Test 9: Should render an error message when no fil }, 3000); }); - clickElementWithAriaLabel(abuufb); await waitFor(() => { setTimeout(() => { + clickElementWithAriaLabel(abuufb); + expectElementWithAriaLabelToBeInDocument(abuem); }, 3000); }); From 35ad48dbe00d1f403796b8b2fb8d8876385d51e9 Mon Sep 17 00:00:00 2001 From: mr21Awesomeness <78995257+brianlugo1@users.noreply.github.com> Date: Fri, 10 May 2024 16:55:42 -0500 Subject: [PATCH 093/160] Updated all test files by replacing single with double quotes! --- .../__tests__/AdminAddCourse.test.js | 66 +++++++++---------- .../AdminImportAssessmentTasks.test.js | 28 ++++---- .../__tests__/AdminViewCourses.test.js | 40 +++++------ .../__tests__/AssessmentDashboard.test.js | 16 ++--- .../__tests__/RosterDashboard.test.js | 16 ++--- .../__tests__/TeamDashboard.test.js | 28 ++++---- .../Components/__tests__/MainHeader.test.js | 36 +++++----- .../src/View/Login/__tests__/Login.test.js | 52 +++++++-------- .../Login/__tests__/SetNewPassword.test.js | 42 ++++++------ .../Login/__tests__/ValidateReset.test.js | 50 +++++++------- 10 files changed, 187 insertions(+), 187 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js b/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js index 49c231168..7707092ba 100644 --- a/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js @@ -1,33 +1,33 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, expectElementWithAriaLabelToHaveErrorMessage -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword -} from '../../../../../App.js'; - - - -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; -var ac = 'addCourse'; -var act = 'addCourseTitle'; -var cacb = 'cancelAddCourseButton'; -var aosacb = 'addOrSaveAddCourseButton'; -var acf = 'addCourseForm'; -var cnami = 'courseNameInput'; -var cnumi = 'courseNumberInput'; -var cti = 'courseTermInput'; -var cyi = 'courseYearInput'; +} from "../../../../../App.js"; + + + +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; +var ac = "addCourse"; +var act = "addCourseTitle"; +var cacb = "cancelAddCourseButton"; +var aosacb = "addOrSaveAddCourseButton"; +var acf = "addCourseForm"; +var cnami = "courseNameInput"; +var cnumi = "courseNumberInput"; +var cti = "courseTermInput"; +var cyi = "courseYearInput"; var vcd = "viewCourseDiv"; @@ -37,7 +37,7 @@ test("NOTE: Tests 1-11 will not pass if Demo Data is not loaded!", () => { }); -test('AdminAddCourse.test.js Test 1: Should render the AdminAddCourse component given the Add Course button is clicked', async () => { +test("AdminAddCourse.test.js Test 1: Should render the AdminAddCourse component given the Add Course button is clicked", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -59,7 +59,7 @@ test('AdminAddCourse.test.js Test 1: Should render the AdminAddCourse component }); -test('AdminAddCourse.test.js Test 2: Should render the course table if the cancel button on the Add Course page is clicked', async () => { +test("AdminAddCourse.test.js Test 2: Should render the course table if the cancel button on the Add Course page is clicked", async () => { render(); await waitFor(() => { @@ -80,7 +80,7 @@ test('AdminAddCourse.test.js Test 2: Should render the course table if the cance }); -test('AdminAddCourse.test.js Test 3: HelperText errors should show for each text field when no information is filled', async () => { +test("AdminAddCourse.test.js Test 3: HelperText errors should show for each text field when no information is filled", async () => { render(); await waitFor(() => { @@ -109,7 +109,7 @@ test('AdminAddCourse.test.js Test 3: HelperText errors should show for each text }); -test('AdminAddCourse.test.js Test 4: HelperText error should show for the addCourseName text field when it is left blank while all other information is filled', async () => { +test("AdminAddCourse.test.js Test 4: HelperText error should show for the addCourseName text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -138,7 +138,7 @@ test('AdminAddCourse.test.js Test 4: HelperText error should show for the addCou }); -test('AdminAddCourse.test.js Test 5: HelperText error should show for the addCourseNumber text field when it is left blank while all other information is filled', async () => { +test("AdminAddCourse.test.js Test 5: HelperText error should show for the addCourseNumber text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -167,7 +167,7 @@ test('AdminAddCourse.test.js Test 5: HelperText error should show for the addCou }); -test('AdminAddCourse.test.js Test 6: HelperText error should show for the addCourseTerm text field when it is left blank while all other information is filled', async () => { +test("AdminAddCourse.test.js Test 6: HelperText error should show for the addCourseTerm text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -196,7 +196,7 @@ test('AdminAddCourse.test.js Test 6: HelperText error should show for the addCou }); -test('AdminAddCourse.test.js Test 7: HelperText error should show for the addCourseYear text field when it is left blank while all other information is filled', async () => { +test("AdminAddCourse.test.js Test 7: HelperText error should show for the addCourseYear text field when it is left blank while all other information is filled", async () => { render(); await waitFor(() => { @@ -225,7 +225,7 @@ test('AdminAddCourse.test.js Test 7: HelperText error should show for the addCou }); -test('AdminAddCourse.test.js Test 8: HelperText error should show for the addCourseTerm text field when input is not "Fall", "Spring" or "Summer"', async () => { +test("AdminAddCourse.test.js Test 8: HelperText error should show for the addCourseTerm text field when input is not 'Fall', 'Spring' or 'Summer'", async () => { render(); await waitFor(() => { @@ -256,7 +256,7 @@ test('AdminAddCourse.test.js Test 8: HelperText error should show for the addCou }); -test('AdminAddCourse.test.js Test 9: HelperText error should show for the addCourseYear text field when input is less than 2023', async () => { +test("AdminAddCourse.test.js Test 9: HelperText error should show for the addCourseYear text field when input is less than 2023", async () => { render(); await waitFor(() => { @@ -287,7 +287,7 @@ test('AdminAddCourse.test.js Test 9: HelperText error should show for the addCou }); -test('AdminAddCourse.test.js Test 10: HelperText error should show for the addCourseYear text field when input is not a numeric value', async () => { +test("AdminAddCourse.test.js Test 10: HelperText error should show for the addCourseYear text field when input is not a numeric value", async () => { render(); await waitFor(() => { @@ -318,7 +318,7 @@ test('AdminAddCourse.test.js Test 10: HelperText error should show for the addCo }); -test('AdminAddCourse.test.js Test 11: Filling in valid input and clicking the Add Course button should redirect you to course view page, and should contain the new course you just added', async () => { +test("AdminAddCourse.test.js Test 11: Filling in valid input and clicking the Add Course button should redirect you to course view page, and should contain the new course you just added", async () => { render(); await waitFor(() => { @@ -354,7 +354,7 @@ test('AdminAddCourse.test.js Test 11: Filling in valid input and clicking the Ad }); }); -test('AdminAddCourse.test.js Test 12: HelperText errors should show for the addCourseYear and addCourseTerm text fields when the input year is not numeric and the term is not "Spring", "Fall", or "Summer"', async () => { +test("AdminAddCourse.test.js Test 12: HelperText errors should show for the addCourseYear and addCourseTerm text fields when the input year is not numeric and the term is not 'Spring', 'Fall', or 'Summer'", async () => { render(); await waitFor(() => { diff --git a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js index 9d5dbf0c8..99e280a8e 100644 --- a/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js +++ b/FrontEndReact/src/View/Admin/Add/ImportTasks/__tests__/AdminImportAssessmentTasks.test.js @@ -1,6 +1,6 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, @@ -8,18 +8,18 @@ import { changeElementWithAriaLabelWithInput, expectElementWithAriaLabelToHaveErrorMessage, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword -} from '../../../../../App.js'; +} from "../../../../../App.js"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var rt = "rosterTitle"; var at = "assessmentTab"; var iab = "importAssessmentButton"; @@ -39,7 +39,7 @@ test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { }); -test('AdminImportAssessmentTasks.test.js Test 1: Should render the AdminImportAssessmentTasks component given the Import Assessments button is clicked', async () => { +test("AdminImportAssessmentTasks.test.js Test 1: Should render the AdminImportAssessmentTasks component given the Import Assessments button is clicked", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -68,7 +68,7 @@ test('AdminImportAssessmentTasks.test.js Test 1: Should render the AdminImportAs }); -test('AdminImportAssessmentTasks.test.js Test 2: Should render the page that came before given that the Cancel button is clicked', async () => { +test("AdminImportAssessmentTasks.test.js Test 2: Should render the page that came before given that the Cancel button is clicked", async () => { render(); await waitFor(() => { @@ -99,7 +99,7 @@ test('AdminImportAssessmentTasks.test.js Test 2: Should render the page that cam }); -test('AdminImportAssessmentTasks.test.js Test 3: Should render the page that came before given that the back button is clicked', async () => { +test("AdminImportAssessmentTasks.test.js Test 3: Should render the page that came before given that the back button is clicked", async () => { render(); await waitFor(() => { @@ -130,7 +130,7 @@ test('AdminImportAssessmentTasks.test.js Test 3: Should render the page that cam }); -test('AdminImportAssessmentTasks.test.js Test 4: Should render an error message on the page when no input is given', async () => { +test("AdminImportAssessmentTasks.test.js Test 4: Should render an error message on the page when no input is given", async () => { render(); await waitFor(() => { @@ -165,7 +165,7 @@ test('AdminImportAssessmentTasks.test.js Test 4: Should render an error message }); -test('AdminImportAssessmentTasks.test.js Test 5: Should refresh and return back to Assessment Dashboard page when valid information is input and submit button is clicked', async() => { +test("AdminImportAssessmentTasks.test.js Test 5: Should refresh and return back to Assessment Dashboard page when valid information is input and submit button is clicked", async() => { render(); await waitFor(() => { diff --git a/FrontEndReact/src/View/Admin/View/ViewCourses/__tests__/AdminViewCourses.test.js b/FrontEndReact/src/View/Admin/View/ViewCourses/__tests__/AdminViewCourses.test.js index 8527b5116..e52ddaeb4 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCourses/__tests__/AdminViewCourses.test.js +++ b/FrontEndReact/src/View/Admin/View/ViewCourses/__tests__/AdminViewCourses.test.js @@ -1,34 +1,34 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword, demoTaInstructorPassword, demoStudentPassword -} from '../../../../../App.js'; +} from "../../../../../App.js"; var lf = "loginForm"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ad = 'accountDropdown'; -var lob = 'logoutButton'; -var ct = 'coursesTitle'; -var ac = 'addCourse'; -var act = 'addCourseTitle'; -var ecib = 'editCourseIconButton'; -var vcib = 'viewCourseIconButton'; -var rt = 'rosterTitle'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ad = "accountDropdown"; +var lob = "logoutButton"; +var ct = "coursesTitle"; +var ac = "addCourse"; +var act = "addCourseTitle"; +var ecib = "editCourseIconButton"; +var vcib = "viewCourseIconButton"; +var rt = "rosterTitle"; @@ -37,14 +37,14 @@ test("NOTE: Tests 2-7 will not pass if Demo Data is not loaded!", () => { }); -test('AdminViewCourses.test.js Test 1: should render Login Form component', () => { +test("AdminViewCourses.test.js Test 1: should render Login Form component", () => { render(); expectElementWithAriaLabelToBeInDocument(lf); }); -test('AdminViewCourses.test.js Test 2: Should show courses page for admin view using demo admin credentials', async () => { +test("AdminViewCourses.test.js Test 2: Should show courses page for admin view using demo admin credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -67,7 +67,7 @@ test('AdminViewCourses.test.js Test 2: Should show courses page for admin view u }); -test('AdminViewCourses.test.js Test 3: Should show courses page for ta/instructor view using demo ta/instructor credentials', async () => { +test("AdminViewCourses.test.js Test 3: Should show courses page for ta/instructor view using demo ta/instructor credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demotainstructor03@skillbuilder.edu"); @@ -90,7 +90,7 @@ test('AdminViewCourses.test.js Test 3: Should show courses page for ta/instructo }); -test('AdminViewCourses.test.js Test 4: Should show courses page for student view using demo student credentials', async () => { +test("AdminViewCourses.test.js Test 4: Should show courses page for student view using demo student credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demostudent4@skillbuilder.edu"); diff --git a/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/AssessmentDashboard.test.js b/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/AssessmentDashboard.test.js index 1ea1c099f..44e90f5a2 100644 --- a/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/AssessmentDashboard.test.js +++ b/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/AssessmentDashboard.test.js @@ -1,25 +1,25 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword, -} from '../../../../../App.js'; +} from "../../../../../App.js"; var lf = "loginForm"; var lb = "loginButton"; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var rt = "rosterTitle"; var at = "assessmentTab"; diff --git a/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/RosterDashboard.test.js b/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/RosterDashboard.test.js index bed482c77..ef8db3d29 100644 --- a/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/RosterDashboard.test.js +++ b/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/RosterDashboard.test.js @@ -1,24 +1,24 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword, -} from '../../../../../App.js'; +} from "../../../../../App.js"; var lf = "loginForm"; var lb = "loginButton"; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var rt = "rosterTitle"; var sbub = "studentBulkUploadButton"; diff --git a/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/TeamDashboard.test.js b/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/TeamDashboard.test.js index d32050c14..8a576e71f 100644 --- a/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/TeamDashboard.test.js +++ b/FrontEndReact/src/View/Admin/View/ViewDashboard/__tests__/TeamDashboard.test.js @@ -1,24 +1,24 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel -} from '../../../../../testUtilities.js'; +} from "../../../../../testUtilities.js"; import { demoAdminPassword -} from '../../../../../App.js'; +} from "../../../../../App.js"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var vcmh = "viewCourseMainHeader"; var mhbb = "mainHeaderBackButton"; @@ -39,7 +39,7 @@ test("NOTE: Tests 1-5 will not pass if Demo Data is not loaded!", () => { }); -test('TeamDashboard.test.js Test 1: Should render the TeamDashboard', async () => { +test("TeamDashboard.test.js Test 1: Should render the TeamDashboard", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -70,7 +70,7 @@ test('TeamDashboard.test.js Test 1: Should render the TeamDashboard', async () = }); -test('TeamDashboard.test.js Test 2: Should render the Admin Bulk Upload page if the adminBulkUpload button is clicked', async () => { +test("TeamDashboard.test.js Test 2: Should render the Admin Bulk Upload page if the adminBulkUpload button is clicked", async () => { render(); await waitFor(() => { @@ -103,7 +103,7 @@ test('TeamDashboard.test.js Test 2: Should render the Admin Bulk Upload page if }); -test('TeamDashboard.test.js Test 3: Should render the Add Team page if the adminAddTeam button is clicked', async () => { +test("TeamDashboard.test.js Test 3: Should render the Add Team page if the adminAddTeam button is clicked", async () => { render(); await waitFor(() => { @@ -136,7 +136,7 @@ test('TeamDashboard.test.js Test 3: Should render the Add Team page if the admin }); -test('TeamDashboard.test.js Test 4: Should render the View Team page if the adminViewTeam button is clicked', async () => { +test("TeamDashboard.test.js Test 4: Should render the View Team page if the adminViewTeam button is clicked", async () => { render(); await waitFor(() => { @@ -169,7 +169,7 @@ test('TeamDashboard.test.js Test 4: Should render the View Team page if the admi }); -test('MainHeader.test.js Test 5: Clicking the back button on the page should go to the page that came before the current (ViewCourseAdmin)', async () => { +test("MainHeader.test.js Test 5: Clicking the back button on the page should go to the page that came before the current (ViewCourseAdmin)", async () => { render(); await waitFor(() => { diff --git a/FrontEndReact/src/View/Components/__tests__/MainHeader.test.js b/FrontEndReact/src/View/Components/__tests__/MainHeader.test.js index 349f84d05..80c0133c7 100644 --- a/FrontEndReact/src/View/Components/__tests__/MainHeader.test.js +++ b/FrontEndReact/src/View/Components/__tests__/MainHeader.test.js @@ -1,24 +1,24 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../../Login/Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../../Login/Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, changeElementWithAriaLabelWithInput, clickFirstElementWithAriaLabel -} from '../../../testUtilities.js'; +} from "../../../testUtilities.js"; import { demoAdminPassword -} from '../../../App.js'; +} from "../../../App.js"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ct = 'coursesTitle'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ct = "coursesTitle"; var vcib = "viewCourseIconButton"; var vcmh = "viewCourseMainHeader"; var mhbb = "mainHeaderBackButton"; @@ -38,7 +38,7 @@ test("NOTE: Tests 1-9 will not pass if Demo Data is not loaded!", () => { }); -test('Header.test.js Test 1: Should render the MainHeader component given the View Course button is clicked', async () => { +test("Header.test.js Test 1: Should render the MainHeader component given the View Course button is clicked", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -61,7 +61,7 @@ test('Header.test.js Test 1: Should render the MainHeader component given the Vi }); -test('MainHeader.test.js Test 2: Clicking the back button on the MainHeader component should go to the page that came before the current (ViewCourseAdmin)', async () => { +test("MainHeader.test.js Test 2: Clicking the back button on the MainHeader component should go to the page that came before the current (ViewCourseAdmin)", async () => { render(); await waitFor(() => { @@ -88,7 +88,7 @@ test('MainHeader.test.js Test 2: Clicking the back button on the MainHeader comp }); -test('MainHeader.test.js Test 3: Clicking the view button for a given course provides the correct course title', async () => { +test("MainHeader.test.js Test 3: Clicking the view button for a given course provides the correct course title", async () => { var className = "Operating Systems"; render(); @@ -107,7 +107,7 @@ test('MainHeader.test.js Test 3: Clicking the view button for a given course pro }); -test('MainHeader.test.js Test 4: Clicking a View Course button on the main page should render all four tabs', async () => { +test("MainHeader.test.js Test 4: Clicking a View Course button on the main page should render all four tabs", async () => { render(); await waitFor(() => { @@ -130,7 +130,7 @@ test('MainHeader.test.js Test 4: Clicking a View Course button on the main page }); -test('MainHeader.test.js Test 5: Ensure that clicking the view button for a given course will render the rosterDashboard by default', async () => { +test("MainHeader.test.js Test 5: Ensure that clicking the view button for a given course will render the rosterDashboard by default", async () => { render(); await waitFor(() => { @@ -147,7 +147,7 @@ test('MainHeader.test.js Test 5: Ensure that clicking the view button for a give }); -test('MainHeader.test.js Test 6: Ensure that clicking the rosterTab will render the rosterDashboard', async () => { +test("MainHeader.test.js Test 6: Ensure that clicking the rosterTab will render the rosterDashboard", async () => { render(); await waitFor(() => { @@ -172,7 +172,7 @@ test('MainHeader.test.js Test 6: Ensure that clicking the rosterTab will render }); -test('MainHeader.test.js Test 7: Ensure that clicking the teamTab will render the teamDashboard', async () => { +test("MainHeader.test.js Test 7: Ensure that clicking the teamTab will render the teamDashboard", async () => { render(); await waitFor(() => { @@ -197,7 +197,7 @@ test('MainHeader.test.js Test 7: Ensure that clicking the teamTab will render th }); -test('MainHeader.test.js Test 8: Ensure that clicking the assessmentTab will render the assessmentDashboard', async () => { +test("MainHeader.test.js Test 8: Ensure that clicking the assessmentTab will render the assessmentDashboard", async () => { render(); await waitFor(() => { @@ -222,7 +222,7 @@ test('MainHeader.test.js Test 8: Ensure that clicking the assessmentTab will ren }); -test('MainHeader.test.js Test 9: Ensure that clicking the reportingTab will render the reportingDashboard', async () => { +test("MainHeader.test.js Test 9: Ensure that clicking the reportingTab will render the reportingDashboard", async () => { render(); await waitFor(() => { diff --git a/FrontEndReact/src/View/Login/__tests__/Login.test.js b/FrontEndReact/src/View/Login/__tests__/Login.test.js index 12117aeff..411d1ac9f 100644 --- a/FrontEndReact/src/View/Login/__tests__/Login.test.js +++ b/FrontEndReact/src/View/Login/__tests__/Login.test.js @@ -1,34 +1,34 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../Login.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, expectElementWithAriaLabelToHaveErrorMessage, changeElementWithAriaLabelWithInput -} from '../../../testUtilities.js'; +} from "../../../testUtilities.js"; import { superAdminPassword, demoAdminPassword, demoTaInstructorPassword, demoStudentPassword -} from '../../../App.js'; +} from "../../../App.js"; var lf = "loginForm"; -var lb = 'loginButton'; -var ei = 'emailInput'; -var pi = 'passwordInput'; -var ema = 'errorMessageAlert'; -var sat = 'superAdminTitle'; -var ad = 'accountDropdown'; -var lob = 'logoutButton'; -var ct = 'coursesTitle'; -var fpb = 'resetPasswordButton'; -var vrt = 'validateResetTitle'; +var lb = "loginButton"; +var ei = "emailInput"; +var pi = "passwordInput"; +var ema = "errorMessageAlert"; +var sat = "superAdminTitle"; +var ad = "accountDropdown"; +var lob = "logoutButton"; +var ct = "coursesTitle"; +var fpb = "resetPasswordButton"; +var vrt = "validateResetTitle"; @@ -37,14 +37,14 @@ test("NOTE: Tests 7-10 will not pass if Demo Data is not loaded!", () => { }); -test('Login.test.js Test 1: should render Login Form component', () => { +test("Login.test.js Test 1: should render Login Form component", () => { render(); expectElementWithAriaLabelToBeInDocument(lf); }); -test('Login.test.js Test 2: HelperText should show Email cannot be empty with Email and Password not filled.', async () => { +test("Login.test.js Test 2: HelperText should show Email cannot be empty with Email and Password not filled.", async () => { render(); clickElementWithAriaLabel(lb); @@ -57,7 +57,7 @@ test('Login.test.js Test 2: HelperText should show Email cannot be empty with Em }); -test('Login.test.js Test 3: HelperText should show Email cannot be empty with Password filled, but not Email.', async () => { +test("Login.test.js Test 3: HelperText should show Email cannot be empty with Password filled, but not Email.", async () => { render(); changeElementWithAriaLabelWithInput(pi, "passwordTest123"); @@ -72,7 +72,7 @@ test('Login.test.js Test 3: HelperText should show Email cannot be empty with Pa }); -test('Login.test.js Test 4: HelperText should show Password cannot be empty with Email filled, but not Password.', async () => { +test("Login.test.js Test 4: HelperText should show Password cannot be empty with Email filled, but not Password.", async () => { render(); changeElementWithAriaLabelWithInput(ei, "test21@test.com"); @@ -87,7 +87,7 @@ test('Login.test.js Test 4: HelperText should show Password cannot be empty with }); -test('Login.test.js Test 5: Error Message Component show error invalid credentials when email is invalid and password is not missing.', async () => { +test("Login.test.js Test 5: Error Message Component show error invalid credentials when email is invalid and password is not missing.", async () => { render(); changeElementWithAriaLabelWithInput(ei, "invalidEmail1@test.com"); @@ -106,7 +106,7 @@ test('Login.test.js Test 5: Error Message Component show error invalid credentia }); -test('Login.test.js Test 6: Error Message Component should show error unable to verify when email is valid but password is invalid.', async () => { +test("Login.test.js Test 6: Error Message Component should show error unable to verify when email is valid but password is invalid.", async () => { render(); changeElementWithAriaLabelWithInput(ei, "superadminuser01@skillbuilder.edu"); @@ -125,7 +125,7 @@ test('Login.test.js Test 6: Error Message Component should show error unable to }); -test('Login.test.js Test 7: Should show users page for super admin view using super admin credentials', async () => { +test("Login.test.js Test 7: Should show users page for super admin view using super admin credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "superadminuser01@skillbuilder.edu"); @@ -148,7 +148,7 @@ test('Login.test.js Test 7: Should show users page for super admin view using su }); -test('Login.test.js Test 8: Should show courses page for admin view using demo admin credentials', async () => { +test("Login.test.js Test 8: Should show courses page for admin view using demo admin credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demoadmin02@skillbuilder.edu"); @@ -171,7 +171,7 @@ test('Login.test.js Test 8: Should show courses page for admin view using demo a }); -test('Login.test.js Test 9: Should show courses page for ta/instructor view using demo ta/instructor credentials', async () => { +test("Login.test.js Test 9: Should show courses page for ta/instructor view using demo ta/instructor credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demotainstructor03@skillbuilder.edu"); @@ -194,7 +194,7 @@ test('Login.test.js Test 9: Should show courses page for ta/instructor view usin }); -test('Login.test.js Test 10: Should show courses page for student view using demo student credentials', async () => { +test("Login.test.js Test 10: Should show courses page for student view using demo student credentials", async () => { render(); changeElementWithAriaLabelWithInput(ei, "demostudent4@skillbuilder.edu"); @@ -217,7 +217,7 @@ test('Login.test.js Test 10: Should show courses page for student view using dem }); -test('Login.test.js Test 11: Should show Set New Password page when clicking Forgot Password Link.', async () => { +test("Login.test.js Test 11: Should show Set New Password page when clicking Forgot Password Link.", async () => { render(); clickElementWithAriaLabel(fpb); diff --git a/FrontEndReact/src/View/Login/__tests__/SetNewPassword.test.js b/FrontEndReact/src/View/Login/__tests__/SetNewPassword.test.js index 454aebe71..ff07fa662 100644 --- a/FrontEndReact/src/View/Login/__tests__/SetNewPassword.test.js +++ b/FrontEndReact/src/View/Login/__tests__/SetNewPassword.test.js @@ -1,22 +1,22 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import SetNewPassword from '../SetNewPassword.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import SetNewPassword from "../SetNewPassword.js"; import { clickElementWithAriaLabel, expectElementWithAriaLabelToBeInDocument, expectElementWithAriaLabelToHaveErrorMessage, changeElementWithAriaLabelWithInput -} from '../../../testUtilities.js'; +} from "../../../testUtilities.js"; -var snpfl = 'setNewPasswordFormLabel'; -var snpb = 'setNewPasswordButton'; -var ema = 'errorMessageAlert'; -var snpi = 'setNewPasswordInput'; -var sncpi = 'setNewPasswordConfirmInput'; -var lf = 'loginForm'; +var snpfl = "setNewPasswordFormLabel"; +var snpb = "setNewPasswordButton"; +var ema = "errorMessageAlert"; +var snpi = "setNewPasswordInput"; +var sncpi = "setNewPasswordConfirmInput"; +var lf = "loginForm"; @@ -25,14 +25,14 @@ test("NOTE: Test 11 will not pass if Demo Data is not loaded!", () => { }); -test('SetNewPassword.test.js Test 1: should render SetNewPassword Form component', () => { +test("SetNewPassword.test.js Test 1: should render SetNewPassword Form component", () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); }); -test('SetNewPassword.test.js Test 2: should display error password cannot be empty when no password or confirm password are entered', async () => { +test("SetNewPassword.test.js Test 2: should display error password cannot be empty when no password or confirm password are entered", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -45,7 +45,7 @@ test('SetNewPassword.test.js Test 2: should display error password cannot be emp }); -test('SetNewPassword.test.js Test 3: should display error confirm password cannot be empty when password is filled but not confirm password', async () => { +test("SetNewPassword.test.js Test 3: should display error confirm password cannot be empty when password is filled but not confirm password", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -60,7 +60,7 @@ test('SetNewPassword.test.js Test 3: should display error confirm password canno }); -test('SetNewPassword.test.js Test 4: should display error passwords to not match', async () => { +test("SetNewPassword.test.js Test 4: should display error passwords to not match", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -77,7 +77,7 @@ test('SetNewPassword.test.js Test 4: should display error passwords to not match }); -test('SetNewPassword.test.js Test 5: should display error check password strength when password is less than 7 characters long', async () => { +test("SetNewPassword.test.js Test 5: should display error check password strength when password is less than 7 characters long", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -94,7 +94,7 @@ test('SetNewPassword.test.js Test 5: should display error check password strengt }); -test('SetNewPassword.test.js Test 6: should display error check password strength when password is 7 long and has one uppercase letter but not one lowercase letter', async () => { +test("SetNewPassword.test.js Test 6: should display error check password strength when password is 7 long and has one uppercase letter but not one lowercase letter", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -111,7 +111,7 @@ test('SetNewPassword.test.js Test 6: should display error check password strengt }); -test('SetNewPassword.test.js Test 7: should display error check password strength when password is 7 long and has one lowercase letter but not one uppercase letter', async () => { +test("SetNewPassword.test.js Test 7: should display error check password strength when password is 7 long and has one lowercase letter but not one uppercase letter", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -128,7 +128,7 @@ test('SetNewPassword.test.js Test 7: should display error check password strengt }); -test('SetNewPassword.test.js Test 8: should display error check password strength when password is 7 long, has one uppercase, and one lowercase letter but not one number', async () => { +test("SetNewPassword.test.js Test 8: should display error check password strength when password is 7 long, has one uppercase, and one lowercase letter but not one number", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -145,7 +145,7 @@ test('SetNewPassword.test.js Test 8: should display error check password strengt }); -test('SetNewPassword.test.js Test 9: should display error check password strength when password is 7 long, has one uppercase, one lowercase, and one number but not a special symbol', async () => { +test("SetNewPassword.test.js Test 9: should display error check password strength when password is 7 long, has one uppercase, one lowercase, and one number but not a special symbol", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -162,7 +162,7 @@ test('SetNewPassword.test.js Test 9: should display error check password strengt }); -test('SetNewPassword.test.js Test 10: should display error missing email or password when email is missing but check password strength is strong because the password is 7 long, has one uppercase, one lowercase, one number, and one special symbol', async () => { +test("SetNewPassword.test.js Test 10: should display error missing email or password when email is missing but check password strength is strong because the password is 7 long, has one uppercase, one lowercase, one number, and one special symbol", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); @@ -179,7 +179,7 @@ test('SetNewPassword.test.js Test 10: should display error missing email or pass }); -test('SetNewPassword.test.js Test 11: should display login page when email is valid and check password strength is strong because the password is 7 long, has one uppercase, one lowercase, one number, and one special symbol', async () => { +test("SetNewPassword.test.js Test 11: should display login page when email is valid and check password strength is strong because the password is 7 long, has one uppercase, one lowercase, one number, and one special symbol", async () => { render(); expectElementWithAriaLabelToBeInDocument(snpfl); diff --git a/FrontEndReact/src/View/Login/__tests__/ValidateReset.test.js b/FrontEndReact/src/View/Login/__tests__/ValidateReset.test.js index 5aa50f390..6ac64f123 100644 --- a/FrontEndReact/src/View/Login/__tests__/ValidateReset.test.js +++ b/FrontEndReact/src/View/Login/__tests__/ValidateReset.test.js @@ -1,6 +1,6 @@ -import { render, waitFor } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import Login from '../Login.js'; +import { render, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import Login from "../Login.js"; import { clickElementWithAriaLabel, @@ -8,33 +8,33 @@ import { expectElementWithAriaLabelToHaveErrorMessage, changeElementWithAriaLabelWithInput, changeElementWithAriaLabelWithCode -} from '../../../testUtilities.js'; +} from "../../../testUtilities.js"; -var lf = 'loginForm'; -var fpb = 'resetPasswordButton'; -var vrt = 'validateResetTitle'; -var vrbb = 'validateResetBackButton'; -var vrcb = 'validateResetConfirmButton'; -var vrf = 'validateResetForm'; -var vrei = 'validateResetEmailInput'; -var ecf = 'enterCodeForm'; -var vcb = 'verifyCodeButton'; -var scbb = 'sendCodeBackButton'; -var ema = 'errorMessageAlert'; -var sci = 'sendCodeInput'; +var lf = "loginForm"; +var fpb = "resetPasswordButton"; +var vrt = "validateResetTitle"; +var vrbb = "validateResetBackButton"; +var vrcb = "validateResetConfirmButton"; +var vrf = "validateResetForm"; +var vrei = "validateResetEmailInput"; +var ecf = "enterCodeForm"; +var vcb = "verifyCodeButton"; +var scbb = "sendCodeBackButton"; +var ema = "errorMessageAlert"; +var sci = "sendCodeInput"; -test('ValidateReset.test.js Test 1: should render Login Form component', () => { +test("ValidateReset.test.js Test 1: should render Login Form component", () => { render(); expectElementWithAriaLabelToBeInDocument(lf); }); -test('ValidateReset.test.js Test 2: Should show Set New Password page when clicking Forgot Password Link.', async () => { +test("ValidateReset.test.js Test 2: Should show Set New Password page when clicking Forgot Password Link.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -45,7 +45,7 @@ test('ValidateReset.test.js Test 2: Should show Set New Password page when click }); -test('ValidateReset.test.js Test 3: Should show Login page when clicking Back button.', async () => { +test("ValidateReset.test.js Test 3: Should show Login page when clicking Back button.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -62,7 +62,7 @@ test('ValidateReset.test.js Test 3: Should show Login page when clicking Back bu }); -test('ValidateReset.test.js Test 4: Should show email cannot be empty when email is not passed in.', async () => { +test("ValidateReset.test.js Test 4: Should show email cannot be empty when email is not passed in.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -81,7 +81,7 @@ test('ValidateReset.test.js Test 4: Should show email cannot be empty when email }); -test('ValidateReset.test.js Test 5: Should show SetNewPassword page when email is invalid.', async () => { +test("ValidateReset.test.js Test 5: Should show SetNewPassword page when email is invalid.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -96,7 +96,7 @@ test('ValidateReset.test.js Test 5: Should show SetNewPassword page when email i }); -test('ValidateReset.test.js Test 6: Should show SetNewPassword page when email is valid.', async () => { +test("ValidateReset.test.js Test 6: Should show SetNewPassword page when email is valid.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -111,7 +111,7 @@ test('ValidateReset.test.js Test 6: Should show SetNewPassword page when email i }); -test('ValidateReset.test.js Test 7: Should show Validate Reset page when clicking Back button on Code Required page.', async () => { +test("ValidateReset.test.js Test 7: Should show Validate Reset page when clicking Back button on Code Required page.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -128,7 +128,7 @@ test('ValidateReset.test.js Test 7: Should show Validate Reset page when clickin }); -test('ValidateReset.test.js Test 8: Should show make sure your code is correct when no code is entered.', async () => { +test("ValidateReset.test.js Test 8: Should show make sure your code is correct when no code is entered.", async () => { render(); clickElementWithAriaLabel(fpb); @@ -147,7 +147,7 @@ test('ValidateReset.test.js Test 8: Should show make sure your code is correct w }); -test('ValidateReset.test.js Test 9: Should show an error occurred please verify your code when an incorrect code is entered.', async () => { +test("ValidateReset.test.js Test 9: Should show an error occurred please verify your code when an incorrect code is entered.", async () => { render(); clickElementWithAriaLabel(fpb); From 55fc4d321c28141eac2bf83c92e23ec877e10021 Mon Sep 17 00:00:00 2001 From: Aldo Velarde Date: Fri, 10 May 2024 11:15:29 -0500 Subject: [PATCH 094/160] Modified componentDidUpdate call! --- .../CompleteAssessmentTask.js | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/CompleteAssessmentTask/CompleteAssessmentTask.js b/FrontEndReact/src/View/Admin/View/CompleteAssessmentTask/CompleteAssessmentTask.js index 4592fe61f..f0596bc39 100644 --- a/FrontEndReact/src/View/Admin/View/CompleteAssessmentTask/CompleteAssessmentTask.js +++ b/FrontEndReact/src/View/Admin/View/CompleteAssessmentTask/CompleteAssessmentTask.js @@ -74,18 +74,16 @@ class CompleteAssessmentTask extends Component { } } - componentDidUpdate() { - if (this.state.rubrics && this.state.teams && this.state.users === null) { - var teamIds = []; - - for (var index = 0; index < this.state.teams.length; index++) { - teamIds = [...teamIds, this.state.teams[index]["team_id"]]; + componentDidUpdate(prevProps, prevState) { + if (prevState.rubrics === null && prevState.teams === null && prevState.users === null) { + if (this.state.teams && this.state.teams.length > 0) { + var teamIds = this.state.teams.map(team => team.team_id); + + genericResourceGET( + `/user?team_ids=${teamIds}`, + "users", this + ); } - - genericResourceGET( - `/user?team_ids=${teamIds}`, - "users", this - ); } } From 7a158ba02132aa6d63b42111008a67eb18eefcdf Mon Sep 17 00:00:00 2001 From: ebanderson3 Date: Sat, 14 Sep 2024 13:57:47 -0500 Subject: [PATCH 095/160] Reordered to account for foreign key dependencies --- BackEndFlask/dbcreate.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/BackEndFlask/dbcreate.py b/BackEndFlask/dbcreate.py index 0de355bf5..6cb73134b 100644 --- a/BackEndFlask/dbcreate.py +++ b/BackEndFlask/dbcreate.py @@ -37,6 +37,18 @@ os.abort() print("[dbcreate] successfully created new db") time.sleep(sleep_time) + if (get_roles().__len__() == 0): + print("[dbcreate] attempting to load existing roles...") + time.sleep(sleep_time) + load_existing_roles() + print("[dbcreate] successfully loaded existing roles") + time.sleep(sleep_time) + if(get_users().__len__()==0): + print("[dbcreate] attempting to load SuperAdminUser...") + time.sleep(sleep_time) + load_SuperAdminUser() + print("[dbcreate] successfully loaded SuperAdminUser") + time.sleep(sleep_time) if(get_rubrics().__len__()==0): print("[dbcreate] attempting to load existing rubrics...") time.sleep(sleep_time) @@ -60,18 +72,6 @@ time.sleep(sleep_time) load_existing_suggestions() print("[dbcreate] successfully loaded existing suggestions") - if (get_roles().__len__() == 0): - print("[dbcreate] attempting to load existing roles...") - time.sleep(sleep_time) - load_existing_roles() - print("[dbcreate] successfully loaded existing roles") - time.sleep(sleep_time) - if(get_users().__len__()==0): - print("[dbcreate] attempting to load SuperAdminUser...") - time.sleep(sleep_time) - load_SuperAdminUser() - print("[dbcreate] successfully loaded SuperAdminUser") - time.sleep(sleep_time) if len(sys.argv) == 2 and sys.argv[1]=="demo": if(get_users().__len__()==1): print("[dbcreate] attempting to load demo Admin...") @@ -129,17 +129,17 @@ load_demo_admin_assessment_task() print("[dbcreate] successfully loaded demo AssessmentTask") time.sleep(sleep_time) - if(get_feedback().__len__()==0): - print("[dbcreate] attempting to load demo Feedback...") - time.sleep(sleep_time) - load_demo_feedback() - print("[dbcreate] successfully loaded demo Feedback") - time.sleep(sleep_time) if (get_completed_assessments().__len__() == 0): print("[dbcreate] attempting to load demo completed assessments...") time.sleep(sleep_time) load_demo_completed_assessment() print("[dbcreate] successfully loaded demo completed assessments") time.sleep(sleep_time) + if(get_feedback().__len__()==0): + print("[dbcreate] attempting to load demo Feedback...") + time.sleep(sleep_time) + load_demo_feedback() + print("[dbcreate] successfully loaded demo Feedback") + time.sleep(sleep_time) print("[dbcreate] exiting...") From a0de76bcfae84a8720cb28176ec282d92412c4c1 Mon Sep 17 00:00:00 2001 From: ebanderson3 Date: Sat, 14 Sep 2024 16:45:57 -0500 Subject: [PATCH 096/160] Added database creation to README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 72e512715..504881d33 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,10 @@ terminal: sudo mysql -u root - Next use these commands to create an account -named skillbuilder and set the passowrd to +named skillbuilder and set the password to "WasPogil1#" + CREATE DATABASE account; CREATE USER 'skillbuilder'@'localhost' IDENTIFIED BY 'WasPogil1#'; GRANT ALL PRIVILEGES ON *.* TO 'skillbuilder'@'localhost'; FLUSH PRIVILEGES; From 6966a3ad8064e158d143a0bc9709f142b313e1b2 Mon Sep 17 00:00:00 2001 From: Brian Lugo <78995257+brianlugo1@users.noreply.github.com> Date: Sat, 14 Sep 2024 23:20:02 -0500 Subject: [PATCH 097/160] Updated requirements.txt by adding new dependency for mysql on mac! Updated README.md by adding information for how to install mysql on MacOS and start it! --- BackEndFlask/requirements.txt | 3 ++- README.md | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/requirements.txt b/BackEndFlask/requirements.txt index 653d710fd..423cf8d5b 100644 --- a/BackEndFlask/requirements.txt +++ b/BackEndFlask/requirements.txt @@ -15,4 +15,5 @@ Werkzeug >= 2.0.2 redis >= 4.5.5 python-dotenv >= 1.0.0 yagmail >= 0.15.293 -openpyxl >= 3.1.2 \ No newline at end of file +openpyxl >= 3.1.2 +cryptography >= 43.0.1 \ No newline at end of file diff --git a/README.md b/README.md index 504881d33..bca889ac8 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,18 @@ on Linux: sudo apt install mysql-server -- Run the following command to open MySQL in the -terminal: +- Run the following command to install MySQL-Server +on MacOS: + + brew install mysql + +- Run the following command to start MySQL-Server +on MacOS: + + brew services start mysql + +- Run the following command to start MySQL-Server +in a new terminal: sudo mysql -u root From 9464accc2c72796f9bcfd97bb2a26dbca108f0bd Mon Sep 17 00:00:00 2001 From: Brian Lugo <78995257+brianlugo1@users.noreply.github.com> Date: Sun, 15 Sep 2024 00:37:04 -0500 Subject: [PATCH 098/160] Updated .env by adding new environment variables for the new mysql db uri! Updated __init__.py by adding logic to use the new environment variables to create the correct mysql db uri! Updated dbcreate.py by removing no longer needed db.drop_all() and replaced os.abort() with raise e to provide a way to debug the errors better in docker compose logs! Updated run.py by removing no longer needed comments! Updated setupEnv.py by adding the error log of the exception raised by trying to run a command in cmd()! Updated compose.yml by adding logic for creating a new mysql docker image and container! Also added logic to persist mysql data even when running docker compose down! Also added logic so that the backend service will only run if mysql is ready to accept connections to allow the backend service to connect and initialize the db! Updated Dockerfile.backend by removing the logic to run the -d flag while building the backend docker image and instead run it when the docker image is executed to create a docker container! --- BackEndFlask/.env | 4 ++++ BackEndFlask/core/__init__.py | 16 ++++++++++++++-- BackEndFlask/dbcreate.py | 3 +-- BackEndFlask/run.py | 8 -------- BackEndFlask/setupEnv.py | 2 ++ Dockerfile.backend | 5 +---- compose.yml | 32 +++++++++++++++++++++++++++++++- 7 files changed, 53 insertions(+), 17 deletions(-) diff --git a/BackEndFlask/.env b/BackEndFlask/.env index 70fad1b9e..47023316d 100644 --- a/BackEndFlask/.env +++ b/BackEndFlask/.env @@ -4,3 +4,7 @@ DEMO_ADMIN_PASSWORD=demo_admin DEMO_TA_INSTRUCTOR_PASSWORD=demo_ta DEMO_STUDENT_PASSWORD=demo_student SECRET_KEY=Thisissupposedtobesecret! +MYSQL_HOST=localhost +MYSQL_USER=skillbuilder +MYSQL_PASSWORD=WasPogil1# +MYSQL_DATABASE=account diff --git a/BackEndFlask/core/__init__.py b/BackEndFlask/core/__init__.py index 501344950..c4b7d3321 100644 --- a/BackEndFlask/core/__init__.py +++ b/BackEndFlask/core/__init__.py @@ -2,11 +2,13 @@ from flask_marshmallow import Marshmallow from flask_sqlalchemy import SQLAlchemy from models.tests import testing +from dotenv import load_dotenv from flask import Flask from flask_cors import CORS +import subprocess +load_dotenv() import sys import os -import subprocess import re def setup_cron_jobs(): @@ -62,7 +64,17 @@ def setup_cron_jobs(): jwt = JWTManager(app) account_db_path = os.getcwd() + os.path.join(os.path.sep, "core") + os.path.join(os.path.sep, "account.db") -app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://skillbuilder:WasPogil1#@localhost/account' +MYSQL_HOST=os.getenv('MYSQL_HOST') + +MYSQL_USER=os.getenv('MYSQL_USER') + +MYSQL_PASSWORD=os.getenv('MYSQL_PASSWORD') + +MYSQL_DATABASE=os.getenv('MYSQL_DATABASE') + +db_uri = (f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}/{MYSQL_DATABASE}") + +app.config['SQLALCHEMY_DATABASE_URI'] = db_uri db = SQLAlchemy(app) ma = Marshmallow(app) diff --git a/BackEndFlask/dbcreate.py b/BackEndFlask/dbcreate.py index e347d7900..ebed9fc9e 100644 --- a/BackEndFlask/dbcreate.py +++ b/BackEndFlask/dbcreate.py @@ -28,7 +28,6 @@ print("[dbcreate] attempting to create new db...") time.sleep(sleep_time) try: - db.drop_all() db.create_all() is_docker = os.getenv('DOCKER_BUILD', 'false').lower() == 'true' if not is_docker: @@ -36,7 +35,7 @@ except Exception as e: print(f"[dbcreate] an error ({e}) occured with db.create_all()") print("[dbcreate] exiting...") - os.abort() + raise e print("[dbcreate] successfully created new db") time.sleep(sleep_time) if (get_roles().__len__() == 0): diff --git a/BackEndFlask/run.py b/BackEndFlask/run.py index f1bba48e6..58643d83c 100644 --- a/BackEndFlask/run.py +++ b/BackEndFlask/run.py @@ -1,12 +1,4 @@ from core import app -# this variable is expected by the wsgi server -# application = app if __name__ == '__main__': - #The app.run(debug = True) line is needed if we are working on our local machine - # app.run(debug=True) - - #the app.run(host="0.0.0.0") line is currently commented out and if and only when we are seting up an EC2 instance app.run(host="0.0.0.0") - - # token: MFFt4RjpXNMh1c_T1AQj diff --git a/BackEndFlask/setupEnv.py b/BackEndFlask/setupEnv.py index 9b01a1b30..0effeb690 100755 --- a/BackEndFlask/setupEnv.py +++ b/BackEndFlask/setupEnv.py @@ -41,6 +41,8 @@ def cmd(command, parent_function): err(f"Error running command: {command} in function: {parent_function}") + err(f"Exception: {e}") + err(f"Return code: {e.returncode}") err(f"Output: {e.output}") diff --git a/Dockerfile.backend b/Dockerfile.backend index c43103ed1..386c23bc2 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -24,11 +24,8 @@ RUN pip install --no-cache-dir --upgrade pip \ # Copy the rest of the backend code COPY BackEndFlask/ /app/ -# Initialize the Backend environment -RUN python setupEnv.py -d - # Expose the Backend port EXPOSE 5000 # Start the Flask server -CMD ["python", "setupEnv.py", "-s"] +CMD ["python", "setupEnv.py", "-ds"] diff --git a/compose.yml b/compose.yml index 691c8ec9f..62131d6d7 100644 --- a/compose.yml +++ b/compose.yml @@ -6,11 +6,18 @@ services: ports: - "5000:5000" depends_on: - - redis + redis: + condition: service_started + mysql: + condition: service_healthy networks: - app-network environment: - REDIS_HOST=redis + - MYSQL_HOST=mysql + - MYSQL_USER=skillbuilder + - MYSQL_PASSWORD=WasPogil1# + - MYSQL_DATABASE=account redis: image: redis:7.2.4 @@ -30,6 +37,29 @@ services: networks: - app-network + mysql: + image: mysql:8.0 + restart: always + environment: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: account + MYSQL_USER: skillbuilder + MYSQL_PASSWORD: WasPogil1# + ports: + - "3306:3306" + volumes: + - mysql-data:/var/lib/mysql + networks: + - app-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] + interval: 10s + timeout: 5s + retries: 5 + networks: app-network: driver: bridge + +volumes: + mysql-data: From 6dcfc047a446c21d37a32f468ebdbdcb1b44da7c Mon Sep 17 00:00:00 2001 From: ebanderson3 Date: Tue, 17 Sep 2024 23:24:43 -0500 Subject: [PATCH 099/160] Changed MySQL port on localhost due to wslrelay.exe conflict (presumably) --- compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose.yml b/compose.yml index eea7bbe48..2e4bec9b8 100644 --- a/compose.yml +++ b/compose.yml @@ -50,7 +50,7 @@ services: MYSQL_USER: skillbuilder MYSQL_PASSWORD: WasPogil1# ports: - - "3306:3306" + - "5551:3306" volumes: - mysql-data:/var/lib/mysql networks: From 7dede3787bcbbe3a7fa6d78e644ce5ff64a8e66c Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Tue, 19 Nov 2024 15:36:16 -0600 Subject: [PATCH 100/160] created Send Message button for Super Admin dashboard --- .../src/View/Components/SendMessageModal.js | 83 +++++++++++++++++++ FrontEndReact/src/View/Navbar/AppState.js | 25 +++++- 2 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 FrontEndReact/src/View/Components/SendMessageModal.js diff --git a/FrontEndReact/src/View/Components/SendMessageModal.js b/FrontEndReact/src/View/Components/SendMessageModal.js new file mode 100644 index 000000000..8a2672bfa --- /dev/null +++ b/FrontEndReact/src/View/Components/SendMessageModal.js @@ -0,0 +1,83 @@ +import React from "react"; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import { TextField } from "@mui/material"; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +export default function SendMessageModal ( props ) { + return ( + + + + {"Send Notification to Admins"} + + + + + Use this form to send a message to all admin users. This notification will be delivered to their registered email addresses. + + + + + + + + + + + + + + + + + + ); +} diff --git a/FrontEndReact/src/View/Navbar/AppState.js b/FrontEndReact/src/View/Navbar/AppState.js index df9ce9095..ee7ba8325 100644 --- a/FrontEndReact/src/View/Navbar/AppState.js +++ b/FrontEndReact/src/View/Navbar/AppState.js @@ -30,6 +30,7 @@ import ReportingDashboard from '../Admin/View/Reporting/ReportingDashboard.js'; import AdminAddCustomRubric from '../Admin/Add/AddCustomRubric/AdminAddCustomRubric.js'; import AdminViewCustomRubrics from '../Admin/View/ViewCustomRubrics/AdminViewCustomRubrics.js'; import UserAccount from './UserAccount.js' +import SendMessageModal from '../Components/SendMessageModal.js'; class AppState extends Component { @@ -465,7 +466,7 @@ class AppState extends Component { {this.state.activeTab==="SuperAdminUsers" && -
+
Users @@ -483,6 +484,18 @@ class AppState extends Component { > Add User +
} + + {this.state.activeTab==="SendMessageModal" && + + + + } ) } From a37d1a6f1849daadb64865124eaf99d17dbcceb4 Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Tue, 19 Nov 2024 15:51:08 -0600 Subject: [PATCH 101/160] edited parts of SendMessageModal --- .../src/View/Components/SendMessageModal.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/FrontEndReact/src/View/Components/SendMessageModal.js b/FrontEndReact/src/View/Components/SendMessageModal.js index 8a2672bfa..3cd444912 100644 --- a/FrontEndReact/src/View/Components/SendMessageModal.js +++ b/FrontEndReact/src/View/Components/SendMessageModal.js @@ -30,10 +30,10 @@ export default function SendMessageModal ( props ) { id="emailSubject" name="emailSubject" variant='outlined' - label="Subject" - value={props.subject} - error={!!props.error.subject} - helperText={props.error.subject} + label="Add Subject" + value={props.emailSubject} + error={!!props.error.emailSubject} + helperText={props.error.emailSubject} onChange={props.handleChange} required fullWidth @@ -47,10 +47,10 @@ export default function SendMessageModal ( props ) { id="emailMessage" name="emailMessage" variant='outlined' - label="Message" - value={props.message} - error={!!props.error.message} - helperText={props.error.message} + label="Add Message" + value={props.emailMessage} + error={!!props.error.emailMessage} + helperText={props.error.emailMessage} onChange={props.handleChange} required multiline From 320e2a1084d05e979ee550fd3d1b3ffbf4838ed9 Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Tue, 19 Nov 2024 15:56:57 -0600 Subject: [PATCH 102/160] error is gone but the page linked shows nothing --- .../src/View/Components/SendMessageModal.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/FrontEndReact/src/View/Components/SendMessageModal.js b/FrontEndReact/src/View/Components/SendMessageModal.js index 3cd444912..4ceb6e458 100644 --- a/FrontEndReact/src/View/Components/SendMessageModal.js +++ b/FrontEndReact/src/View/Components/SendMessageModal.js @@ -31,9 +31,9 @@ export default function SendMessageModal ( props ) { name="emailSubject" variant='outlined' label="Add Subject" - value={props.emailSubject} - error={!!props.error.emailSubject} - helperText={props.error.emailSubject} + value={props.emailSubject || ''} + error={!!(props.error && props.error.emailSubject)} + helperText={props.error?.emailSubject || ''} onChange={props.handleChange} required fullWidth @@ -48,9 +48,9 @@ export default function SendMessageModal ( props ) { name="emailMessage" variant='outlined' label="Add Message" - value={props.emailMessage} - error={!!props.error.emailMessage} - helperText={props.error.emailMessage} + value={props.emailMessage || ''} + error={!!(props.error && props.error.emailMessage)} + helperText={props.error?.emailMessage || ''} onChange={props.handleChange} required multiline From 5996a36e5574f4b8167d8d782026335f17e6ec98 Mon Sep 17 00:00:00 2001 From: aldo Date: Wed, 20 Nov 2024 12:16:55 -0600 Subject: [PATCH 103/160] fixed team oc and sfi duplication --- BackEndFlask/models/queries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 0c245848a..f66838a7b 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1051,8 +1051,8 @@ def get_csv_categories(rubric_id: int, user_id: int, team_id: int, at_id: int, c if team_id is not None : ocs_sfis_query[i].filter(CompletedAssessment.team_id == team_id) # Executing the query - ocs = ocs_sfis_query[0].all() - sfis = ocs_sfis_query[1].all() + ocs = ocs_sfis_query[0].distinct(ObservableCharacteristic.observable_characteristics_id).all() + sfis = ocs_sfis_query[1].distinct(SuggestionsForImprovement.suggestion_id).all() return ocs,sfis From a5b5a60dbd838021782d2fdcc421ccf16b7ec294 Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Wed, 20 Nov 2024 15:09:00 -0600 Subject: [PATCH 104/160] added back button in send message page (super admin) --- FrontEndReact/src/View/Navbar/AppState.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FrontEndReact/src/View/Navbar/AppState.js b/FrontEndReact/src/View/Navbar/AppState.js index ee7ba8325..216efa937 100644 --- a/FrontEndReact/src/View/Navbar/AppState.js +++ b/FrontEndReact/src/View/Navbar/AppState.js @@ -856,6 +856,10 @@ class AppState extends Component { tabSelected={"SendMessageModal"} aria-label="SendMessageModal" /> + } From ce7de7c5e03813182da8a4987f8922f647f56c92 Mon Sep 17 00:00:00 2001 From: aldo Date: Wed, 20 Nov 2024 15:39:41 -0600 Subject: [PATCH 105/160] backend is now logging the feedback; a bit of work is need on the front end to ensure the write at is selected --- BackEndFlask/controller/Routes/Rating_routes.py | 6 ++++-- .../ViewCompletedAssessmentTasks.js | 12 ++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/BackEndFlask/controller/Routes/Rating_routes.py b/BackEndFlask/controller/Routes/Rating_routes.py index aebe91c32..29aeb40cf 100644 --- a/BackEndFlask/controller/Routes/Rating_routes.py +++ b/BackEndFlask/controller/Routes/Rating_routes.py @@ -61,8 +61,10 @@ def student_view_feedback(): used to calculate lag time. """ try: - user_id = request.json["user_id"] - completed_assessment_id = request.json["completed_assessment_id"] + with open("zfile.txt", 'w') as out: + print(request.json, file=out) + user_id = request.json.get("user_id") + completed_assessment_id = request.json.get("completed_assessment_id") exists = check_feedback_exists(user_id, completed_assessment_id) if exists: diff --git a/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js b/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js index 9728b156f..3d3f63625 100644 --- a/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js +++ b/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js @@ -3,7 +3,7 @@ import 'bootstrap/dist/css/bootstrap.css'; import CustomDataTable from "../../../Components/CustomDataTable"; import { IconButton } from "@mui/material"; import VisibilityIcon from '@mui/icons-material/Visibility'; -import { getHumanReadableDueDate } from "../../../../utility"; +import { genericResourcePOST, getHumanReadableDueDate } from "../../../../utility"; @@ -95,7 +95,15 @@ class ViewCompletedAssessmentTasks extends Component { { navbar.setAssessmentTaskInstructions(assessmentTasks, atId, completedAssessments, { readOnly: true }); - }} + genericResourcePOST( + `/rating`, + this, + JSON.stringify({ + "user_id" : this.userId, + "completed_assessment_id": 1, + }), + ); + }} aria-label="completedAssessmentTasksViewIconButton" > From c4a24c31c523685b3e75dbaf3801bcd08d4c033e Mon Sep 17 00:00:00 2001 From: aldo Date: Thu, 21 Nov 2024 13:53:23 -0600 Subject: [PATCH 106/160] Seems to now be saving and displaying correctly. I need to do some more testing to ensure info is being fully saved --- BackEndFlask/controller/Routes/Rating_routes.py | 4 ++-- .../ViewCompletedAssessmentTasks.js | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/BackEndFlask/controller/Routes/Rating_routes.py b/BackEndFlask/controller/Routes/Rating_routes.py index 29aeb40cf..688ec4d6a 100644 --- a/BackEndFlask/controller/Routes/Rating_routes.py +++ b/BackEndFlask/controller/Routes/Rating_routes.py @@ -71,8 +71,8 @@ def student_view_feedback(): return create_bad_response(f"Feedback already exists", "feedbacks", 409) feedback_data = request.json - feedback_time = datetime.now() - feedback_data["feedback_time"] = feedback_time.strftime('%Y-%m-%dT%H:%M:%S') + string_format ='%Y-%m-%dT%H:%M:%S.%fZ' + feedback_data["feedback_time"] = datetime.now().strftime(string_format) feedback = create_feedback(feedback_data) diff --git a/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js b/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js index 3d3f63625..fbce13e9b 100644 --- a/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js +++ b/FrontEndReact/src/View/Student/View/CompletedAssessmentTask/ViewCompletedAssessmentTasks.js @@ -95,12 +95,16 @@ class ViewCompletedAssessmentTasks extends Component { { navbar.setAssessmentTaskInstructions(assessmentTasks, atId, completedAssessments, { readOnly: true }); + var singluarCompletedAssessment = null; + if (completedAssessments) { + singluarCompletedAssessment = completedAssessments.find(completedAssessment => completedAssessment.assessment_task_id === atId) ?? null; + } genericResourcePOST( `/rating`, this, JSON.stringify({ - "user_id" : this.userId, - "completed_assessment_id": 1, + "user_id" : singluarCompletedAssessment.user_id, + "completed_assessment_id": singluarCompletedAssessment.completed_assessment_id, }), ); }} From 46375731935a55cc81cf40767d3b062ce09665c7 Mon Sep 17 00:00:00 2001 From: aldo Date: Thu, 21 Nov 2024 14:03:11 -0600 Subject: [PATCH 107/160] removing debugging --- BackEndFlask/controller/Routes/Rating_routes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/BackEndFlask/controller/Routes/Rating_routes.py b/BackEndFlask/controller/Routes/Rating_routes.py index 5a65a3088..0b62faa22 100644 --- a/BackEndFlask/controller/Routes/Rating_routes.py +++ b/BackEndFlask/controller/Routes/Rating_routes.py @@ -67,8 +67,6 @@ def student_view_feedback(): used to calculate lag time. """ try: - with open("zfile.txt", 'w') as out: - print(request.json, file=out) user_id = request.json.get("user_id") completed_assessment_id = request.json.get("completed_assessment_id") From 4664a30924e40eb11e3f3e68b66e47413756a0c8 Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 21 Nov 2024 15:37:10 -0600 Subject: [PATCH 108/160] Had to do the same thing again because git is really great :-) --- .../Routes/Completed_assessment_routes.py | 4 ++++ BackEndFlask/models/queries.py | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/BackEndFlask/controller/Routes/Completed_assessment_routes.py b/BackEndFlask/controller/Routes/Completed_assessment_routes.py index 7013c362d..eadf8c013 100644 --- a/BackEndFlask/controller/Routes/Completed_assessment_routes.py +++ b/BackEndFlask/controller/Routes/Completed_assessment_routes.py @@ -109,6 +109,10 @@ def get_all_completed_assessments(): get_assessment_task(assessment_task_id) # Trigger an error if not exists. completed_assessments = get_completed_assessment_with_team_name(assessment_task_id) + + if not completed_assessments: + completed_assessments = get_completed_assessment_with_user_name(assessment_task_id) + completed_count = get_completed_assessment_count(assessment_task_id) result = [ {**completed_assessment_schema.dump(assessment), 'completed_count': completed_count} diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index f66838a7b..2064bbb61 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1082,9 +1082,6 @@ def get_course_name_by_at_id(at_id:int) -> str : return course_name[0][0] - - - def get_completed_assessment_ratio(course_id: int, assessment_task_id: int) -> int: """ Description: @@ -1096,14 +1093,25 @@ def get_completed_assessment_ratio(course_id: int, assessment_task_id: int) -> i Return: int (Ratio of users who have completed an assessment task rounded to the nearest whole number) """ + ratio = 0 + all_usernames_for_completed_task = get_completed_assessment_with_user_name(assessment_task_id) - all_students_in_course = get_users_by_course_id_and_role_id(course_id, 5) - ratio = (len(all_usernames_for_completed_task) / len(all_students_in_course)) * 100 + + if all_usernames_for_completed_task: + all_students_in_course = get_users_by_course_id_and_role_id(course_id, 5) + + ratio = len(all_usernames_for_completed_task) / len(all_students_in_course) * 100 + else: + all_teams_in_course = get_team_members_in_course(course_id) + all_teams_for_completed_task = get_completed_assessment_with_team_name(assessment_task_id) + + ratio = len(all_teams_for_completed_task) / len(all_teams_in_course) * 100 ratio_rounded = round(ratio) return ratio_rounded + def is_admin_by_user_id(user_id: int) -> bool: """ Description: From 1b0dad0476a5dfe1d0db61e45e1b9a3c1a461a82 Mon Sep 17 00:00:00 2001 From: rtkells Date: Thu, 21 Nov 2024 15:38:12 -0600 Subject: [PATCH 109/160] Removed blank line in queries.py --- BackEndFlask/models/queries.py | 1 - 1 file changed, 1 deletion(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 2064bbb61..3429077ac 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1111,7 +1111,6 @@ def get_completed_assessment_ratio(course_id: int, assessment_task_id: int) -> i return ratio_rounded - def is_admin_by_user_id(user_id: int) -> bool: """ Description: From b8d2e5a14bfa05a7a6b46702e8e7ea54f237ac08 Mon Sep 17 00:00:00 2001 From: aldo Date: Thu, 21 Nov 2024 17:05:01 -0600 Subject: [PATCH 110/160] changes nothing fixed --- .../controller/Routes/Assessment_task_routes.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/BackEndFlask/controller/Routes/Assessment_task_routes.py b/BackEndFlask/controller/Routes/Assessment_task_routes.py index 814b6a313..2953f751f 100644 --- a/BackEndFlask/controller/Routes/Assessment_task_routes.py +++ b/BackEndFlask/controller/Routes/Assessment_task_routes.py @@ -209,6 +209,7 @@ def update_assessment_task(): one_assessment_task = get_assessment_task(assessment_task_id) # Trigger an error if not exists + if one_assessment_task.notification_sent == None: list_of_completed_assessments = get_completed_assessments_by_assessment_task_id(assessment_task_id) @@ -230,11 +231,11 @@ def update_assessment_task(): notification_date ) - return create_good_response( - assessment_task_schema.dump(one_assessment_task), - 201, - "assessment_tasks" - ) + return create_good_response( + assessment_task_schema.dump(one_assessment_task), + 201, + "assessment_tasks" + ) assessment_task_id = request.args.get("assessment_task_id") From 562a70fe7962c0a1de17ee1a55f9f8506fb00a79 Mon Sep 17 00:00:00 2001 From: aldo Date: Thu, 21 Nov 2024 17:08:05 -0600 Subject: [PATCH 111/160] I made the mistake of lengthening only the freshness last time. This now does extend the life time of an access token. --- BackEndFlask/controller/Routes/Refresh_route.py | 3 ++- BackEndFlask/controller/security/utility.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/controller/Routes/Refresh_route.py b/BackEndFlask/controller/Routes/Refresh_route.py index ce31aad9b..ebfacb068 100644 --- a/BackEndFlask/controller/Routes/Refresh_route.py +++ b/BackEndFlask/controller/Routes/Refresh_route.py @@ -5,6 +5,7 @@ from controller.Route_response import * from flask_jwt_extended import jwt_required, create_access_token from controller.security.CustomDecorators import AuthCheck, bad_token_check +import datetime @bp.route('/refresh', methods=['POST']) @jwt_required(refresh=True) @@ -14,7 +15,7 @@ def refresh_token(): try: user_id = int(request.args.get('user_id')) user = user_schema.dump(get_user(user_id)) - jwt = create_access_token([user_id]) + jwt = create_access_token([user_id], fresh=datetime.timedelta(minutes=60), expires_delta=datetime.timedelta(minutes=60)) return create_good_response(user, 200, "user", jwt) except: return create_bad_response("Bad request: user_id must be provided", "user", 400) diff --git a/BackEndFlask/controller/security/utility.py b/BackEndFlask/controller/security/utility.py index 5825203e9..f849ccaef 100644 --- a/BackEndFlask/controller/security/utility.py +++ b/BackEndFlask/controller/security/utility.py @@ -20,7 +20,7 @@ # jwt expires in 15mins; refresh token expires in 30days def create_tokens(user_i_d: any) -> 'tuple[str, str]': with app.app_context(): - jwt = create_access_token(str(user_i_d), fresh=datetime.timedelta(minutes=60)) + jwt = create_access_token(str(user_i_d), fresh=datetime.timedelta(minutes=60), expires_delta=datetime.timedelta(minutes=60)) refresh = request.args.get('refresh_token') if not refresh: refresh = create_refresh_token(str(user_i_d)) From 3ac429d2d89c3462de7fd325717bf3479cc90088 Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Thu, 21 Nov 2024 17:08:09 -0600 Subject: [PATCH 112/160] Added new Metacognition rubric and added 'None' to all OC's, and 'Nothing specific at this time' to all SFI's --- BackEndFlask/models/loadExistingRubrics.py | 141 ++++++++++++++++++++- 1 file changed, 138 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/models/loadExistingRubrics.py b/BackEndFlask/models/loadExistingRubrics.py index 686afc0fd..4ddbe904d 100644 --- a/BackEndFlask/models/loadExistingRubrics.py +++ b/BackEndFlask/models/loadExistingRubrics.py @@ -20,7 +20,9 @@ def load_existing_rubrics(): # (Latest update is September 16, 2022) Problem Solving ["Problem Solving", "Analyzing a complex problem or situation, developing a viable strategy to address it, and executing that strategy (when appropriate)."], # (Latest update is July 19, 2022) Teamwork - ["Teamwork", "Interacting with others and buliding on each other's individual strengths and skills, working toward a common goal."] + ["Teamwork", "Interacting with others and buliding on each other's individual strengths and skills, working toward a common goal."], + # (Latest update is November 21, 2024) Metacognition + ["Metacognition", "Being able to regulate one's thinking and learning through planning, monitoring, and evaluating one's efforts."] ] for rubric in rubrics: r = {} @@ -71,6 +73,11 @@ def load_existing_categories(): [7, "Contributing", "Considered the contributions, strengths and skills of all team members", consistently], [7, "Progressing", "Moved forward towards a common goal", consistently], [7, "Building Community", "Acted as a cohesive unit that supported and included all team members.", consistently], + # (Latest update is November 21, 2024) Metacognition Categories 1-4 + [8, "Planning", "Set learning goals and made plans for achieving them", completely], + [8, "Monitoring", "Paid attention to progress on learning and understanding", completely], + [8, "Evaluating", "Reviewed learning gains and/or performance and determined strengths and areas to improve", completely], + [8, "Realistic Self-assessment", "Produced a self-assessment based on previous and current behaviors and circumstances, using reasonable judgment in future planning", completely], ] for category in categories: c = {} @@ -91,60 +98,73 @@ def load_existing_observable_characteristics(): [1, "Identified the question that needed to be answered or the situation that needed to be addressed"], [1, "Identified any situational factors that may be important to addressing the question or situation"], [1, "Identified the general types of data or information needed to address the question"], + [1, "None"], # Evaluating Observable Characteristics 1-3 [2, "Indicated what information is likely to be most relevant"], [2, "Determined the reliability of the source of information"], [2, "Determined the quality and accuracy of the information itself"], + [2, "None"], # Analyzing Observable Characteristics 1-3 [3, "Discussed information and explored possible meanings"], [3, "Identified general trends or patterns in the data/information that could be used as evidence"], [3, "Processed and/or transformed data/information to put it in forms that could be used as evidence"], + [3, "None"], # Synthesizing Observable Characteristics 1-3 [4, "Identified the relationships between different pieces of information or concepts"], [4, "Compared or contrasted what could be determined from different pieces of information"], [4, "Combined multiple pieces of information or ideas in valid ways to generate a new insight in conclusion"], + [4, "None"], # Forming Arguments Structure Observable Characteristics 1-4 [5, "Stated the conclusion or the claim of the argument"], [5, "Listed the evidence used to support the argument"], [5, "Linked the claim/conclusion to the evidence with focused and organized reasoning"], [5, "Stated any qualifiers that limit the conditions for which the argument is true"], + [5, "None"], # Forming Arguments Validity Observable Characteristics 1-5 [6, "The most relevant evidence was used appropriately to support the claim"], [6, "Reasoning was logical and effectively connected the data to the claim"], [6, "The argument was aligned with disciplinary/community concepts or practices"], [6, "Considered alternative or counter claims"], [6, "Considered evidence that could be used to refute or challenge the claim"], + [6, "None"], # (Latest update is November, 2022) Formal Communication # Intent Observable Characteristics 1-3 [7, "Clearly stated what the audience should gain from the communication"], [7, "Used each part of the communication to convey or support the main message"], [7, "Concluded by summarizing what was to be learned"], + [7, "None"], # Audience Observable Characteristic 1-3 [8, "Communicated to the full range of the audience, including novices and those with expertise"], [8, "Aligned the communication with the interests and background of the particular audience"], [8, "Used vocabulary that aligned with the discipline and was understood by the audience"], + [8, "None"], # Organization Observable Characteristics 1-3 [9, "There was a clear story arc that moved the communication forward"], [9, "Organizational cues and transitions clearly indicated the structure of the communication"], [9, "Sequence of ideas flowed in an order that was easy to follow"], + [9, "None"], # Visual Representations Observable Characteristics 1-3 [10, "Each figure conveyed a clear message"], [10, "Details of the visual representation were easily interpreted by the audience"], [10, "The use of the visual enhanced understanding by the audience"], + [10, "None"], # Format and Style Observable Characteristics 1-3 [11, "Stylistic elements were aesthetically pleasing and did not distract from the message"], [11, "Stylistic elements were designed to make the communication accessbile to the audience (size, colors, contrasts, etc.)"], [11, "The level of formality of the communication aligns with the setting, context, and purpose"], + [11, "None"], # Mechanics Written Word Observable Characteristics 1-4 [12, "Writing contained correct spelling, word choice, punctuation, and capitalization"], [12, "All phrases and sentences were grammatically correct"], [12, "All paragraphs (or slides) were well constructed around a central idea"], [12, "All figures and tables were called out in the narrative, and sources were correctly cited"], + [12, "None"], # Delivery Oral Observable Characteristics 1-4 [13, "Spoke loudly and clearly with a tone that indicated confidence and interest in the subject"], [13, "Vocal tone and pacing helped maintain audience interest"], [13, "Gestures and visual cues further oriented the audience to focus on particular items or messages"], [13, "Body language directed the delivery toward the audience and indicated the speaker was open to engagement"], + [13, "None"], # (Latest update is December 29, 2021) Information Processing # Evaluating Observable Characteristics 1-5 [14, "Established what needs to be accomplished with this information"], @@ -152,101 +172,144 @@ def load_existing_observable_characteristics(): [14, "Indicated what information is relevant"], [14, "Indicated what information is NOT relevant"], [14, "Indicated why certain information is relevant or not"], + [14, "None"], # Interpreting Observable Characteristics 1-4 [15, "Labeled or assigned correct meaning to information (text, tables, symbols, diagrams)"], [15, "Extracted specific details from information"], [15, "Rephrased information in own words"], [15, "Identified patterns in information and derived meaning from them"], + [15, "None"], # Manipulating or Transforming Extent Observable Characteristics 1-3 [16, "Determined what information needs to be converted to accomplish the task"], [16, "Described the process used to generate the transformation"], [16, "Converted all relevant information into a different representation of format"], + [16, "None"], # Manipulating or Transforming Accuracy Observable Characteristics 1-3 [17, "Conveyed the correct or intended meaning of the information in the new representation or format."], [17, "All relevant features of the original information/data are presented in the new representation of format"], [17, "Performed the transformation without errors"], + [17, "None"], # (Latest update is July 5, 2022) Interpersonal Communication # Speaking Observable Characteristics 1-4 [18, "Spoke clear and loudly enough for all team members to hear"], [18, "Used a tone that invited other people to respond"], [18, "Used language that was suitable for the listeners and context"], [18, "Spoke for a reasonable length of time for the situation"], + [18, "None"], # Listening Observable Characteristics 1-4 [19, "Patiently listened without interrupting the speaker"], [19, "Referenced others' ideas to indicate listening and understanding"], [19, "Presented nonverbal cues to indicate attentiveness"], [19, "Avoided engaging in activities that diverted attention"], + [19, "None"], # Responding Observable Characteristics 1-4 [20, "Acknowledged other members for their ideas or contributions"], [20, "Rephrased or referred to what other group members have said"], [20, "Asked other group members to futher explain a concept"], [20, "Elaborated or extended on someone else's idea(s)"], + [20, "None"], # (Latest update is April 24, 2023) Management # Planning Observable Characteristics 1-4 [21, "Generated a summary of the starting and ending points"], [21, "Generated a sequence of steps or tasks to reach the desired goal"], [21, "Discussed a timeline or time frame for completing project tasks"], [21, "Decided on a strategy to share information, updates and progress with all team members"], + [21, "None"], # Organizing Observable Characteristics 1-3 [22, "Decided upon the necessary resources and tools"], [22, "Identified the availability of resources, tools or information"], [22, "Gathered necessary information and tools"], + [22, "None"], # Coordinating Observable Characteristics 1-4 [23, "Determined if tasks need to be delegated or completed by the team as a whole"], [23, "Tailored the tasks toward strengths and availability of team members"], [23, "Assigned specific tasks and responsibilities to team members"], [23, "Established effective communication strategies and productive interactions among team members"], + [23, "None"], # Overseeing Observable Characteristics 1-5 [24, "Reinforced responsibilities and refocused team members toward completing project tasks"], [24, "Communicated status, next steps, and reiterated general plan to accomplish goals"], [24, "Sought and valued input from team members and provided them with constructive feedback"], [24, "Kept track of remaining materials, team and person hours"], [24, "Updated or adapted the tasks or plans as needed"], + [24, "None"], # (Latest update is September 16, 2022) Problem Solving # Analyzing the Situation Observable Characteristics 1-3 [25, "Described the problem that needed to be solved or the decisions that needed to be made"], [25, "Listed complicating factors or constraints that may be important to consider when developing a solution"], [25, "Identified the potential consequences to stakeholders or surrounding"], + [25, "None"], # Identifying Observable Characteristics 1-4 [26, "Reviewed the organized the necessary information and resources"], [26, "Evaluated which available information and resources are critical to solving the problem"], [26, "Determined the limitations of the tools or information that was given or gathered"], [26, "Identified reliable sources that may provide additional needed information, tools, or resources"], + [26, "None"], # Strategizing Observable Characteristics 1-3 [27, "Identified potential starting and ending points for the strategy"], [27, "Determined general steps needed to get from starting point to ending point"], [27, "Sequenced or mapped actions in a logical progression"], + [27, "None"], # Validating Observable Characteristics 1-4 [28, "Reviewed strategy with respect to the identified scope"], [28, "Provided rationale as to how steps within the process were properly sequenced"], [28, "Identified ways the process or stragey could be futher improved or optimized"], [28, "Evaluated the practicality of the overall strategy"], + [28, "None"], # Executing Observable Characteristics 1-4 [29, "Used data and information correctly"], [29, "Made assumptions about the use of data and information that are justifiable"], [29, "Determined that each step is being done in the order and the manner that was planned."], [29, "Verified that each step in the process was providing the desired outcome."], + [29, "None"], # (Latest update is July 19, 2022) Teamwork # Interacting Observable Characteristics 1-3 [30, "All team members communicated ideas related to a common goal"], [30, "Team members responded to each other verbally or nonverbally"], [30, "Directed each other to tasks and information"], + [30, "None"], # Constributing Observable Characteristics 1-4 [31, "Acknowledged the value of the statements of other team members"], [31, "Invited other team members to participate in the conversation, particulary if they had not contributed in a while"], [31, "Expanded on statements of other team members"], [31, "Asked follow-up questions to clarify team members' thoughts"], + [31, "None"], # Progressing Observable Characteristics 1-4 [32, "Stayed on task, focused on the assignment with only brief interruptions"], [32, "Refocused team members to make more effective progress towards the goal"], [32, "Worked simultaneously as single unit on the common goal"], [32, "Checked time to monitor progress on task."], + [32, "None"], # Building Community Observable Characteristics 1-5 [33, "Created a sense of belonging to the team for all team members"], - [33, "Acted as a single unit that did not break up into smaller, gragmented units for the entire task"], + [33, "Acted as a single unit that did not break up into smaller, fragmented units for the entire task"], [33, "Openly and respectfully discussed questions and disagreements between team members"], [33, "Listened carefully to people, and gave weight and respect to their contributions"], [33, "Welcomed and valued the individual identity and experiences of each team member"], + [33, "None"], + # (Latest update is November 20) Metacognition + # Planning Observable Characteristics 1-3 + [34, "Decided on a goal for the task"], + [34, "Determined a strategy, including needed resources, to use in the learning effort"], + [34, "Estimated the time interval needed to reach the goal of the task"], + [34, "None"], + # Monitoring Observable Characteristics 1-4 + [35, "Checked understanding of things to be learned, noting which areas were challenging"], + [35, "Assessed if strategies were effective, and adjusted strategies as needed"], + [35, "Considered if additional resources or assistance would be helpful"], + [35, "Kept track of overall progress on completing the task"], + [35, "None"], + # Evaluating Observable Characteristics 1-3 + [36, "Compared outcomes to personal goals and expectations"], + [36, "Compared performance to external standard or feedback"], + [36, "Identified which strategies were successful and which could be improved"], + [36, "None"], + # Realistic Self-assessment Observable Characteristics 1-4 + [37, "Focused the reflection on the skill or effort that was targeted"], + [37, "Provided specific evidence from past or recent performances in the reflection"], + [37, "Identified how circumstances supported or limited the completion of the task"], + [37, "Made realistic plans (based on previous and current behaviors and circumstances) to improve future performance"], + [37, "None"], ] for observable in observable_characteristics: create_observable_characteristic(observable) @@ -259,24 +322,28 @@ def load_existing_suggestions(): [1, "Highlight or clearly state the question to be addressed or type of conclusion that must be reached."], [1, "List the factors (if any) that may limit the feasibility of some possible conclusions."], [1, "Write down the information you think is needed to address the situation."], + [1, "Nothing specific at this time"], # Evaluating Suggestions 1-5 [2, "Review provided material and circle, highlight, or otherwise indicate information that may be used as evidence in reaching a conclusion."], [2, "Write down the other information (prior knowledge) that might be useful to lead to/support a possible conclusion."], [2, "Set aside any information, patterns, or insights that seem less relevant to addressing the situation at hand."], [2, "Consider whether the information was obtained from a reliable source (textbook, literature, instructor, websites with credible authors)"], [2, "Determine the quality of the information and whether it is sufficient to answer the question."], + [2, "Nothing specific at this time"], # Analyzing Suggestions 1-5 [3, "Interpret and label key pieces of information in text, tables, graphs, diagrams."], [3, "State in your own words what information represents or means."], [3, "Identify general trends in information, and note any information that doesn't fit the pattern."], [3, "Check your understanding of information with others and discuss any differences in understanding."], [3, "State how each piece of information, pattern, or insight can be used to reach a conclusion or support your claim."], + [3, "Nothing specific at this time"], # Synthesizing Suggestions 1-5 [4, "Look for two or more pieces or types of information that can be connected and state how they can be related to each other."], [4, "Write out the aspects that are similar and different in various pieces of information."], [4, "Map out how the information and/or concepts can be combined to support an argument or reach a conclusion."], [4, "Write a statement that summarizes the integration of the information and conveys a new understanding."], [4, "List the ways in which synthesized information could be used as evidence."], + [4, "Nothing specific at this time"], # Forming Arguments Structure Suggestions 1-7 [5, "Review the original goal - what question were you trying to answer?"], [5, "Clearly state your answer to the question (your claim or conclusion)."], @@ -285,12 +352,14 @@ def load_existing_suggestions(): [5, "Explain how each piece of information links to and supports your answer."], [5, "Make sure your answer includes the claim, information and reasoning."], [5, "Make sure the claim or conclusion answers the question."], + [5, "Nothing specific at this time"], # Forming Arguments Validity Suggestions 1-5 [6, "Provide a clear statement that articulates why the evidence you chose leads to the claim or conclusion."], [6, "Check to make sure that your reasoning is consistent with what is accepted in the discipline or context."], [6, "Test your ideas with others, and ask them to judge the quality of the argument or indicate how the argument could be made more convincing."], [6, "Ask yourself (and others) if there is evidence or data that doesn't suport your conclusion or might contradict your claim."], [6, "Consider if there are alternative explanations for the data you are considering."], + [6, "Nothing specific at this time"], # (Latest update is November, 2022) Formal Communication # Intent Suggestions 1-5 [7, "Decide if your main purpose is to inform, to persuade, to argue, to summarize, to entertain, to inspire, etc."], @@ -298,6 +367,7 @@ def load_existing_suggestions(): [7, "Make sure the purpose of the communication is presented early to orient your audience to the focus of the communication."], [7, "Check that the focus of each segment is clearly linked to the main message or intent of the communication."], [7, "Summarize the main ideas to wrap up the presentation (refer back to the initial statement(s) of what was to be learned)."], + [7, "Nothing specific at this time"], # Audience Suggestions 1-6 [8, "Identify the range and level of expertise and interest your audience has for the topic and design your communication to have aspects that will engage all members of the audience."], [8, "Identify what the audience needs to know to understand the narrative."], @@ -305,6 +375,7 @@ def load_existing_suggestions(): [8, "Only use jargon when it is understood readily by most members of your audience, and it makes the communication more effective and succinct."], [8, "Check that the vocabulary, sentence structure, and tone used in your communication is aligned with the level of your audience."], [8, "Collect feedback from others on drafts to make sure the core message of the communication is easily understood."], + [8, "Nothing specific at this time"], # Organization Suggestions 1-6 [9, "Consider the 'story' that you want to tell. Ask yourself what's the main message you want the audience to leave with."], [9, "Identify the critical points for the story (do this before you prepare the actual communication) and map out the key points."], @@ -312,6 +383,7 @@ def load_existing_suggestions(): [9, "Repeat key ideas to ensure the audience can follow the main idea."], [9, "Make sure that you introduce prerequisite information early in the communication."], [9, "Try more than one order for the topics, to see if overall flow is improved."], + [9, "Nothing specific at this time"], # Visual Representations Suggestions 1-6 [10, "Plan what types of figures are needed to support the narrative - consider writing out a figure description before you construct it."], [10, "Avoid including unnecessary details that detract from the intended message."], @@ -319,6 +391,7 @@ def load_existing_suggestions(): [10, "Be sure labels, text, and small details can be easily read."], [10, "Provide a caption that helps interpret the key aspects of the visual."], [10, "Seek feedback on visuals to gauge initial reaction and ease of interpretation."], + [10, "Nothing specific at this time"], # Format Style Suggestions 1-6 [11, "Use titles (headers) and subtitles (subheaders) to orient the audience and help them follow the narrative."], [11, "Look at pages or slides as a whole for an easy-to-read layout, such as white space, headers, line spacing, etc."], @@ -326,6 +399,7 @@ def load_existing_suggestions(): [11, "Use colors to carefully highlight or call attention to key elements to enhance your narrative without distracting from your message."], [11, "Make sure that text, figures, and colors are readable and accessible for all."], [11, "Seek feedback to confirm that the language, tone, and style of your communication match the level of formality needed for your context and purpose."], + [11, "Nothing specific at this time"], # Mechanics Written Words Suggestions 1-7 [12, "Proofread your writing for spelling errors, punctuation, autocorrects, etc."], [12, "Review sentence structure for subject-verb agreement, consistent tense, run on sentences, and other structural problems."], @@ -334,12 +408,14 @@ def load_existing_suggestions(): [12, "Confirm that each figure, table, etc has been numbered consecutively and has been called out and discussed further in the narrative."], [12, "Confirm that all work that has been published elsewhere or ideas/data that were not generated by the author(s) has been properly cited using appropriate conventions."], [12, "Ask someone else to review and provide feedback on your work."], + [12, "Nothing specific at this time"], # Delivery Oral Suggestions 1-5 [13, "Practice for others or record your talk; i. be sure that your voice can be heard, and your word pronunciations are clear. ii. listen for “ums”, “like”, or other verbal tics/filler words that can detract from your message. iii. observe your natural body language, gestures, and stance in front of the audience to be sure that they express confidence and enhance your message."], [13, "Add variety to your speed or vocal tone to emphasize key points or transitions."], [13, "Try to communicate/engage as if telling a story or having a conversation with the audience."], [13, "Face the audience and do not look continuously at the screen or notes."], [13, "Make eye contact with multiple members of the audience."], + [13, "Nothing specific at this time"], # (Latest update is December 29, 2021) Information Processing # Evaluating Suggestions 1-6 [14, "Restate in your own words the task or question that you are trying to address with this information."], @@ -348,23 +424,27 @@ def load_existing_suggestions(): [14, "Write down/circle/highlight the information that is needed to complete the task."], [14, "Put a line through info that you believe is not needed for the task"], [14, "Describe in what ways a particular piece of information may (or may not) be useful (or required) in completing the task"], + [14, "Nothing specific at this time"], # Interpreting Suggestions 1-5 [15, "Add notes or subtitles to key pieces of information found in text, tables, graphs, diagrams to describe its meaning."], [15, "State in your own words what information represents or means."], [15, "Summarize the ideas or relationships the information might convey."], [15, "Determine general trends in information and note any information that doesn't fit the trend"], [15, "Check your understanding of information with others"], + [15, "Nothing specific at this time"], # Manipulating or Transforming Extent Suggestions 1-5 [16, "Identify how the new format of the information differs from the provided format."], [16, "Identify what information needs to be transformed and make notations to ensure that all relevant information has been included."], [16, "Review the new representation or format to be sure all relevant information has been included."], [16, "Consider what information was not included in the new representation or format and make sure it was not necessary."], [16, "Check with peers to see if there is agreement on the method of transformation and final result."], + [16, "Nothing specific at this time"], # Manipulating or Transforming Accuracy Suggestions 1-4 [17, "Write down the features that need to be included in the new form."], [17, "Be sure that you have carefully interpreted the original information and translated that to the new form."], [17, "Carefully check to ensure that the original information is correctly represented in the new form."], [17, "Verify the accuracy of the transformation with others."], + [17, "Nothing specific at this time"], # (Latest update is July 5, 2022) Interpersonal Communication # Speaking Suggestions 1-6 [18, "Direct your voice towards the listeners and ask if you can be heard."], @@ -373,6 +453,7 @@ def load_existing_suggestions(): [18, "Carefully choose your words to align with the nature of the topic and the audience."], [18, "Speak for a length of time that allows frequent back and forth conversation."], [18, "Provide a level of detail appropriate to convey your main idea."], + [18, "Nothing specific at this time"], # Listening Suggestions 1-7 [19, "Allow team members to finish their contribution."], [19, "Indicate if you can't hear someone's spoken words."], @@ -381,12 +462,14 @@ def load_existing_suggestions(): [19, "Face the team member that is speaking and make eye contact."], [19, "Use active-listening body language or facial expressions that indicate attentiveness."], [19, "Remove distractions and direct your attention to the speaker."], + [19, "Nothing specific at this time"], # Responding Suggestions 1-5 [20, "Let team members know when they make a productive contribution."], [20, "State what others have said in your own words and confirm understanding."], [20, "Ask a follow-up question or ask for clarification."], [20, "Reference what others have said when you build on their ideas."], [20, "Offer an altenative to what a team member said."], + [20, "Nothing specific at this time"], # (Latest update is April 24, 2023) Management # Planning Suggestions 1-6 [21, "Write down the general starting point and starting conditions."], @@ -395,10 +478,12 @@ def load_existing_suggestions(): [21, "Double check to make sure that steps are sequenced sensibly."], [21, "Identify time needed for particular steps or other time constraints."], [21, "Make a regular plan to update progress."], + [21, "Nothing specific at this time"], # Organizing Suggestions 1-3 [22, "List the tools, resources, or information that the group needs to obtain."], [22, "List the location of the tools, resources, or information at the group's disposal."], [22, "Strategize about how to obtain the additional/needed tools, resources, or information."], + [22, "Nothing specific at this time"], # Coordinating Suggestions 1-7 [23, "Review the number of people you have addressing each task, and be sure that it is right-sized to make progress."], [23, "Analyze each task for likelihood of success, and be sure you have it staffed appropriately."], @@ -407,6 +492,7 @@ def load_existing_suggestions(): [23, "Delegate tasks outside the team if necessary, especially if the task is too large to complete in the given time."], [23, "Establish a mechanism to share status and work products."], [23, "Set up meetings to discuss challenges and progress."], + [23, "Nothing specific at this time"], # Overseeing Suggestions 1-8 [24, "Check in regularly with each team member to review their progress on tasks."], [24, "Provide a list of steps towards accomplishing the goal that all can refer to and check off each step when completed."], @@ -416,6 +502,7 @@ def load_existing_suggestions(): [24, "Reassign team members to activities that need more attention or person hours as other steps are completed."], [24, "Evaluate whether team members should be reassigned to tasks that better align with their skill sets."], [24, "Check to see if the original plan for project completion is still feasible; make changes if necessary."], + [24, "Nothing specific at this time"], # (Latest update is September 16, 2022) Problem Solving # Analyzing The Situation Suggestions 1-6 [25, "Read closely, and write down short summaries as you read through the entire context of the problem"], @@ -424,18 +511,21 @@ def load_existing_suggestions(): [25, "Prioritize the complicating factors from most to least important"], [25, "List anything that will be significantly impacted by your decision (such as conditions, objects, or people)"], [25, "Deliberate on the consequences of generating a wrong strategy or solution"], + [25, "Nothing specific at this time"], # Identifying Suggestions 1-5 [26, "Highlight or annotate the provided information that may be needed to solve the problem."], [26, "List information or principles that you already know that can help you solve the problem."], [26, "Sort the given and gathered information/resources as 'useful' or 'not useful.'"], [26, "List the particular limitations of the provided information or tools."], [26, "Identify ways to access any additional reliable information, tools or resources that you might need."], + [26, "Nothing specific at this time"], # Strategizing Suggestions 1-5 [27, "Write down a reasonable place to start and add a reasonable end goal"], [27, "Align any two steps in the order or sequence that they must happen. Then, add a third step and so on."], [27, "Consider starting at the end goal and working backwards"], [27, "Sketch a flowchart indicating some general steps from start to finish."], [27, "Add links/actions, or processes that connect the steps"], + [27, "Nothing specific at this time"], # Validating Suggestions 1-7 [28, "Summarize the problem succinctly - does your strategy address each aspect of the problem?"], [28, "Identify any steps that must occur in a specific order and verify that they do."], @@ -444,6 +534,7 @@ def load_existing_suggestions(): [28, "Check to see if you have access to necessary resources, and if not, propose substitutes."], [28, "Check that your strategy is practical and functional, with respect to time, cost, safety, personnel, regulations, etc."], [28, "Take time to continuously assess your strategy throughout the process."], + [28, "Nothing specific at this time"], # Executing Suggestions 1-7 [29, "Use authentic values and reasonable estimates for information needed to solve the problem"], [29, "Make sure that the information you are using applies to the conditions of the problem."], @@ -452,6 +543,7 @@ def load_existing_suggestions(): [29, "List any barriers that you are encountering in executing the steps"], [29, "Identify ways to overcome barriers in implementation steps of the strategy"], [29, "Check the outcome of each step of the strategy for effectiveness."], + [29, "Nothing specific at this time"], # (Latest update is July 19, 2022) Teamwork # Interacting Suggestions 1-6 [30, "Speak up and share your ideas/insights with team members."], @@ -460,6 +552,7 @@ def load_existing_suggestions(): [30, "Explicitly react (nod, speak out loud, write a note, etc.) to contributions from other team members to indicate that you are engaged."], [30, "Restate the prompt to make sure everyone is at the same place on the task."], [30, "Have all members of the team consider the same task at the same time rather than working independently"], + [30, "Nothing specific at this time"], # Contributing Suggestions 1-6 [31, "Acknowledge or point out particularly effective contributions."], [31, "Initiate discussions of agreement or disagreement with statements made by team members."], @@ -467,6 +560,7 @@ def load_existing_suggestions(): [31, "Regularly ask members of the team to share their ideas or explain their reasoning."], [31, "Add information or reasoning to contributions from other team members."], [31, "Ask for clarification or rephrase statements of other team members to ensure understanding."], + [31, "Nothing specific at this time"], # Progressing Suggestions 1-7 [32, "Minimize distractions and focus on the assignment (close unrelated websites or messaging on phone or computer, turn off music, put away unrelated materials)."], [32, "Redirect team members to current task."], @@ -475,6 +569,7 @@ def load_existing_suggestions(): [32, "Compare progress on task to the time remaining on assignment."], [32, "Communicate to team members that you need to move on."], [32, "As a team, list tasks to be done and agree on order for these tasks."], + [32, "Nothing specific at this time"], # Building Community Suggestions 1-8 [33, "Address team members by name."], [33, "Use inclusive (collective) team cues that draw all team members together."], @@ -484,6 +579,46 @@ def load_existing_suggestions(): [33, "Encourage all team members to work together on the same tasks at the same time, as needed."], [33, "Celebrate team successes and persistence through roadblocks."], [33, "Invite other team members to provide alternative views and reasoning."], - ] + [33, "Nothing specific at this time"], + # (Latest update is November 20, 2024) Metacognition + # Planning Suggestions 1-7 + [34, "Describe three or four ways this learning task connects to other topics and components in the course."], + [34, "Identify 2-3 ways that this learning task will help you meet the intended learning goals."], + [34, "Skim the assignment to get a sense of what is involved and to see what resources you will need to support your work."], + [34, "List the things that need to be completed for this assignment or task to be considered successful."], + [34, "Make a detailed plan for how you will complete the assignment."], + [34, "Decide if it makes sense to break the overall assignment into working segments, and figure out how long each would take."], + [34, "Estimate the total amount of time that you will need to complete the task."], + [34, "Nothing specific at this time"], + # Monitoring Suggestions 1-5 + [35, "Read the reflection prompt and objectives before you start and decide what skills or processes you should monitor during the task."], + [35, "Review objectives frequently while completing a task to check your understanding of important concepts."], + [35, "Survey your environment and mindset for distractions that block you from enacting your strategies and making progress (devices, noise, people, physical needs)."], + [35, "Pay attention to where in a process or strategy you are getting stuck, and identify what resources or support would help you to get past that point."], + [35, "Periodically pause and determine the percentage of work that you finished and compare it to the total time available to complete the work."], + [35, "Nothing specific at this time"], + # Evaluating Suggestions 1-7 + [36, "Compare how you actually performed to your initial expectations. Identify which expectations were met and where you fell short."], + [36, "For areas where you met your initial expectations, list your top three effective strategies or activities."], + [36, "For areas where you did not meet your initial expectations, decide if your goals were realistic."], + [36, "If your initial expectations were not realistic, make a list of issues you didn’t account for in your goal setting."], + [36, "For areas where your expectations were realistic but not met, identify what factors made you fall short of your target."], + [36, "Compare how you performed to an external standard or external feedback on your performance. Identify which criteria were met and which require additional work."], + [36, "For areas where you met the criteria, list your top three effective strategies or activities.Describe three or four ways this learning task connects to other topics and components in the course."], + [36, "For areas where you did not meet the criteria, list at least two areas or strategies where you need further work."], + [36, "Determine if you planned your time well by comparing the number of hours you allocated to the number of hours you actually needed to meet your goals."], + [36, "Decide if you were motivated or engaged in this task, and describe how that impacted your efforts."], + [36, "If you weren’t very motivated for this task, generate some ideas for how you could better motivate yourself. Identify 2-3 ways that this learning task will help you meet the intended learning goals."], + [36, "Nothing specific at this time"], + # Realistic Self-assessment Suggestions 1-7 + [37, "Before you start the reflection, review the prompt for the reflection or your goals for the reflection and make sure you are focusing on the intended skill or process (e.g. don’t comment on teamwork if asked to reflect on critical thinking)."], + [37, "List the specific actions that you took in completing this task, including planning and monitoring actions in addition to the task itself.  Which are similar to past approaches?  Which are different?"], + [37, "Considering the actions listed above and your typical approaches; rank them in terms of most productive to least productive."], + [37, "Identify the unproductive behavior or work habit you should change to most positively impact your performance and determine a strategy for how to change it."], + [37, "Consider the contextual factors (physical surroundings, time constraints, life circumstances) that affected your performance; note how your strategies need to be altered to account for them to improve future performance."], + [37, "Summarize 2-3 specific strategies you’ve identified to improve your performance on future tasks."], + [37, "Ask someone who knows you or your work well to review your self-assessment. Ask them if you accurately summarized your past efforts and if they think your future strategies are realistic for you."], + [37, "Nothing specific at this time"], + ] for suggestion in suggestions: create_suggestion(suggestion) \ No newline at end of file From 1470c9f3c9e24c649f98b1b48c268b60552240eb Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Thu, 21 Nov 2024 18:44:14 -0600 Subject: [PATCH 113/160] resolving npm audit issues --- FrontEndReact/package.json | 62 ++++++++++--------- .../Add/AddTask/AdminAddAssessmentTask.js | 2 +- FrontEndReact/src/utility.js | 4 +- 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/FrontEndReact/package.json b/FrontEndReact/package.json index 0861772c4..42651ac00 100644 --- a/FrontEndReact/package.json +++ b/FrontEndReact/package.json @@ -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", @@ -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" } } diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js index 0e99d2c47..6d416c676 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js +++ b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js @@ -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'; diff --git a/FrontEndReact/src/utility.js b/FrontEndReact/src/utility.js index 5b61bcdde..866595f89 100644 --- a/FrontEndReact/src/utility.js +++ b/FrontEndReact/src/utility.js @@ -1,6 +1,6 @@ import { apiUrl } from './App.js'; import Cookies from 'universal-cookie'; -import { zonedTimeToUtc, format } from "date-fns-tz"; +import { fromZonedTime, format } from "date-fns-tz"; import * as eventsource from "eventsource-client"; export async function genericResourceGET(fetchURL, resource, component, options = {}) { @@ -243,7 +243,7 @@ export function formatDueDate(dueDate, timeZone) { const timeZoneId = timeZoneMap[timeZone]; - const zonedDueDate = zonedTimeToUtc(dueDate, timeZoneId); + const zonedDueDate = fromZonedTime(dueDate, timeZoneId); const formattedDueDate = format(zonedDueDate, "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", { timeZone: timeZoneId }); From d23233029ab536224572195be93df6e55eb3a932 Mon Sep 17 00:00:00 2001 From: aldo Date: Thu, 21 Nov 2024 21:12:03 -0600 Subject: [PATCH 114/160] saving some changes to jump to another branch --- .../controller/Routes/Assessment_task_routes.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/BackEndFlask/controller/Routes/Assessment_task_routes.py b/BackEndFlask/controller/Routes/Assessment_task_routes.py index 2953f751f..77e7065ce 100644 --- a/BackEndFlask/controller/Routes/Assessment_task_routes.py +++ b/BackEndFlask/controller/Routes/Assessment_task_routes.py @@ -231,11 +231,11 @@ def update_assessment_task(): notification_date ) - return create_good_response( - assessment_task_schema.dump(one_assessment_task), - 201, - "assessment_tasks" - ) + return create_good_response( + assessment_task_schema.dump(one_assessment_task), + 201, + "assessment_tasks" + ) assessment_task_id = request.args.get("assessment_task_id") From 9a9579666010843e0d3728991b01a062a99013fb Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 10:59:12 -0600 Subject: [PATCH 115/160] Update .env.production --- BackEndFlask/env/.env.production | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BackEndFlask/env/.env.production b/BackEndFlask/env/.env.production index eb4bec572..73b343303 100644 --- a/BackEndFlask/env/.env.production +++ b/BackEndFlask/env/.env.production @@ -1,11 +1,11 @@ # Contains the variables for production enviroment which uses mysql # while the same the urls are meant to override incorrect connection paths meant to serve as examples -DONT_LOOK = 'Thisissupposedtobesecret!2' -WIN_LIN = 'mysql+pymysql://sbadmin_tester:sb_test1@skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/skillbuilder-db' -MAC = 'mysql+pymysql://sbadmin_tester:sb_test1@skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/skillbuilder-db' +DONT_LOOK = 'ThisReallyNeedsToBeASecret1!' +WIN_LIN = 'mysql+pymysql://rubricapp_admin:${DONT_LOOK}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' +MAC = 'mysql+pymysql://rubricapp_admin:${DONT_LOOK}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' #-----------------------------------------------------------------------------# # Final urls should look like the following for when we move to the server # # WIN_LIN = 'mysql+pymysql://user:password@127.0.0.1:3306/name_of_data_base' # # copy the same for mac to ensure everything is properly overwritten # -#-----------------------------------------------------------------------------# \ No newline at end of file +#-----------------------------------------------------------------------------# From e22ad4c23bd152c551d493954c80962c2de564eb Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 11:20:55 -0600 Subject: [PATCH 116/160] Changes to db connect string for MySQL --- BackEndFlask/env/.env.production | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/env/.env.production b/BackEndFlask/env/.env.production index eb4bec572..8a18b26c8 100644 --- a/BackEndFlask/env/.env.production +++ b/BackEndFlask/env/.env.production @@ -1,8 +1,11 @@ # Contains the variables for production enviroment which uses mysql # while the same the urls are meant to override incorrect connection paths meant to serve as examples -DONT_LOOK = 'Thisissupposedtobesecret!2' -WIN_LIN = 'mysql+pymysql://sbadmin_tester:sb_test1@skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/skillbuilder-db' -MAC = 'mysql+pymysql://sbadmin_tester:sb_test1@skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/skillbuilder-db' +MYSQL_HOST = 'skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306' +MYSQL_PASSWORD = 'ThisReallyNeedsToBeASecret1!' +MYSQL_USER = 'rubricapp_admin' +MYSQL_DATABASE = 'rubricapp' +WIN_LIN = 'mysql+pymysql://rubricapp_admin:${MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' +MAC = 'mysql+pymysql://rubricapp_admin:${MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' #-----------------------------------------------------------------------------# # Final urls should look like the following for when we move to the server # From f5108b02e11da8d2169bf1e5f3aa42a75e58e479 Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 11:28:06 -0600 Subject: [PATCH 117/160] Changes to db connect string for MySQL --- BackEndFlask/.env | 8 ++++---- BackEndFlask/env/.env.production | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/BackEndFlask/.env b/BackEndFlask/.env index 47023316d..58ffd64a2 100644 --- a/BackEndFlask/.env +++ b/BackEndFlask/.env @@ -4,7 +4,7 @@ DEMO_ADMIN_PASSWORD=demo_admin DEMO_TA_INSTRUCTOR_PASSWORD=demo_ta DEMO_STUDENT_PASSWORD=demo_student SECRET_KEY=Thisissupposedtobesecret! -MYSQL_HOST=localhost -MYSQL_USER=skillbuilder -MYSQL_PASSWORD=WasPogil1# -MYSQL_DATABASE=account +MYSQL_HOST=skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306 +MYSQL_USER=rubricapp_admin +MYSQL_PASSWORD=ThisReallyNeedsToBeASecret1! +MYSQL_DATABASE=rubricapp_admin diff --git a/BackEndFlask/env/.env.production b/BackEndFlask/env/.env.production index 8a18b26c8..03d7debf4 100644 --- a/BackEndFlask/env/.env.production +++ b/BackEndFlask/env/.env.production @@ -3,7 +3,7 @@ MYSQL_HOST = 'skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306' MYSQL_PASSWORD = 'ThisReallyNeedsToBeASecret1!' MYSQL_USER = 'rubricapp_admin' -MYSQL_DATABASE = 'rubricapp' +MYSQL_DATABASE = 'rubricapp_admin' WIN_LIN = 'mysql+pymysql://rubricapp_admin:${MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' MAC = 'mysql+pymysql://rubricapp_admin:${MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' From e4c6ff7633b49bd7ef623d11147ea5079f5c7c2c Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 11:36:37 -0600 Subject: [PATCH 118/160] Changes to db connect string for MySQL --- BackEndFlask/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/core/__init__.py b/BackEndFlask/core/__init__.py index d70f6939a..adad5557e 100644 --- a/BackEndFlask/core/__init__.py +++ b/BackEndFlask/core/__init__.py @@ -90,7 +90,7 @@ def setup_cron_jobs(): MYSQL_DATABASE=os.getenv('MYSQL_DATABASE') -db_uri = (f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}/{MYSQL_DATABASE}") +db_uri = (f"mysql+pymysql://{MYSQL_USER}:${MYSQL_PASSWORD}@{MYSQL_HOST}/{MYSQL_DATABASE}") app.config['SQLALCHEMY_DATABASE_URI'] = db_uri From f422fffecb69416bbda65ab9f58893cdbd0d9993 Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 11:42:03 -0600 Subject: [PATCH 119/160] Changes to db connect string for MySQL --- BackEndFlask/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/.env b/BackEndFlask/.env index 58ffd64a2..56d298794 100644 --- a/BackEndFlask/.env +++ b/BackEndFlask/.env @@ -4,7 +4,7 @@ DEMO_ADMIN_PASSWORD=demo_admin DEMO_TA_INSTRUCTOR_PASSWORD=demo_ta DEMO_STUDENT_PASSWORD=demo_student SECRET_KEY=Thisissupposedtobesecret! -MYSQL_HOST=skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306 +MYSQL_HOST=rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306 MYSQL_USER=rubricapp_admin MYSQL_PASSWORD=ThisReallyNeedsToBeASecret1! -MYSQL_DATABASE=rubricapp_admin +MYSQL_DATABASE=rubricapp From 42365dd5ae0d57793b27b2371fc1c3e140397a11 Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 22 Nov 2024 12:10:18 -0600 Subject: [PATCH 120/160] wrong branch --- BackEndFlask/models/utility.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/models/utility.py b/BackEndFlask/models/utility.py index 942ea62b0..dd8c0fbfb 100644 --- a/BackEndFlask/models/utility.py +++ b/BackEndFlask/models/utility.py @@ -1,6 +1,6 @@ import sys import yagmail -import random, string +import string, secrets from models.logger import logger from controller.Routes.RouteExceptions import EmailFailureException @@ -58,7 +58,7 @@ def send_email(address: str, subject: str, content: str): def generate_random_password(length: int): letters = string.ascii_letters + string.digits - return ''.join(random.choice(letters) for i in range(length)) + return ''.join(secrets.choice(letters) for i in range(length)) def error_log(f): ''' From e6b76fa0e0ae379ac38b02de683df1cb2f10e5ce Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 12:15:11 -0600 Subject: [PATCH 121/160] Changes to db connect string for MySQL --- BackEndFlask/core/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/core/__init__.py b/BackEndFlask/core/__init__.py index adad5557e..d70f6939a 100644 --- a/BackEndFlask/core/__init__.py +++ b/BackEndFlask/core/__init__.py @@ -90,7 +90,7 @@ def setup_cron_jobs(): MYSQL_DATABASE=os.getenv('MYSQL_DATABASE') -db_uri = (f"mysql+pymysql://{MYSQL_USER}:${MYSQL_PASSWORD}@{MYSQL_HOST}/{MYSQL_DATABASE}") +db_uri = (f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASSWORD}@{MYSQL_HOST}/{MYSQL_DATABASE}") app.config['SQLALCHEMY_DATABASE_URI'] = db_uri From bf9b10a69671e76cca2fe6eac52a024ba022d73a Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 15:30:31 -0600 Subject: [PATCH 122/160] Change to support MySQL --- BackEndFlask/.env | 2 +- BackEndFlask/.gitignore | 3 ++- BackEndFlask/env/.env.production | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/BackEndFlask/.env b/BackEndFlask/.env index 56d298794..1b5d551e9 100644 --- a/BackEndFlask/.env +++ b/BackEndFlask/.env @@ -4,7 +4,7 @@ DEMO_ADMIN_PASSWORD=demo_admin DEMO_TA_INSTRUCTOR_PASSWORD=demo_ta DEMO_STUDENT_PASSWORD=demo_student SECRET_KEY=Thisissupposedtobesecret! -MYSQL_HOST=rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306 +MYSQL_HOST=localhost:3306 MYSQL_USER=rubricapp_admin MYSQL_PASSWORD=ThisReallyNeedsToBeASecret1! MYSQL_DATABASE=rubricapp diff --git a/BackEndFlask/.gitignore b/BackEndFlask/.gitignore index 93272c465..b73b77005 100644 --- a/BackEndFlask/.gitignore +++ b/BackEndFlask/.gitignore @@ -17,4 +17,5 @@ Functions/test.py Models/hidden.py dump.rdb BackEndFlaskVenv -Test \ No newline at end of file +Test +.env \ No newline at end of file diff --git a/BackEndFlask/env/.env.production b/BackEndFlask/env/.env.production index c4afdf68e..d9c37a3eb 100644 --- a/BackEndFlask/env/.env.production +++ b/BackEndFlask/env/.env.production @@ -1,12 +1,12 @@ # Contains the variables for production enviroment which uses mysql # while the same the urls are meant to override incorrect connection paths meant to serve as examples -MYSQL_HOST = 'skillbuilder-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306' +MYSQL_HOST = 'rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306' MYSQL_PASSWORD = 'ThisReallyNeedsToBeASecret1!' MYSQL_USER = 'rubricapp_admin' -MYSQL_DATABASE = 'rubricapp_admin' -WIN_LIN = 'mysql+pymysql://rubricapp_admin:${MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' -MAC = 'mysql+pymysql://rubricapp_admin:${MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' +MYSQL_DATABASE = 'rubricapp' +WIN_LIN = 'mysql+pymysql://rubricapp_admin:{MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' +MAC = 'mysql+pymysql://rubricapp_admin:{MYSQL_PASSWORD}@rubricapp-db.c1db7ief4oer.us-east-2.rds.amazonaws.com:3306/rubricapp' #-----------------------------------------------------------------------------# From e03ba2c36760f4f594b8e8585fe4a29a4ce56c7e Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Fri, 22 Nov 2024 15:47:49 -0600 Subject: [PATCH 123/160] Update .gitignore Added BackEndFlask/.env to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f3e6985a7..9109da4a7 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ package-lock.json BackEndFlask/logs/all.log BackEndFlask/models/hidden.py BackEndFlask/logs/all.log +BackEndFlask/.env .vscode # Nginx configuration files From f2930acd8283c878cdad319aafba478eedf46707 Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 22 Nov 2024 16:23:08 -0600 Subject: [PATCH 124/160] new route created to begin teasing out the complex functionality stuffed in one put route --- .../Routes/Assessment_task_routes.py | 2 - .../controller/Routes/notification_routes.py | 67 +++++++++++++++++++ BackEndFlask/controller/__init__.py | 1 + .../ViewCompleteIndividualAssessmentTasks.js | 3 +- 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 BackEndFlask/controller/Routes/notification_routes.py diff --git a/BackEndFlask/controller/Routes/Assessment_task_routes.py b/BackEndFlask/controller/Routes/Assessment_task_routes.py index 77e7065ce..934d6bc27 100644 --- a/BackEndFlask/controller/Routes/Assessment_task_routes.py +++ b/BackEndFlask/controller/Routes/Assessment_task_routes.py @@ -192,7 +192,6 @@ def add_assessment_task(): ) - @bp.route('/assessment_task', methods = ['PUT']) @jwt_required() @bad_token_check() @@ -209,7 +208,6 @@ def update_assessment_task(): one_assessment_task = get_assessment_task(assessment_task_id) # Trigger an error if not exists - if one_assessment_task.notification_sent == None: list_of_completed_assessments = get_completed_assessments_by_assessment_task_id(assessment_task_id) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py new file mode 100644 index 000000000..8dfaae020 --- /dev/null +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -0,0 +1,67 @@ +#------------------------------------------------------------ +# This is file holds the routes that handle sending +# notifications to individuals. +# +# Explanation of how Assessment.notification_sent will be +# used: +# If a completed assessments last update is after +# assessment.notification_sent, then they are +# considered to be new and elligble to send a msg +# to agian. Any more complex feture will require +# another table or trigger table to be added. +#------------------------------------------------------------ + +from flask import request +from flask_sqlalchemy import * +from controller import bp +from models.assessment_task import * +from models.course import get_course +from models.user import get_user +from models.team import get_team +from models.role import get_role +from controller.Route_response import * +from models.user_course import get_user_courses_by_user_id + +from flask_jwt_extended import jwt_required +from controller.security.CustomDecorators import ( + AuthCheck, bad_token_check, + admin_check +) + +@bp.route('/mass_notification', methods = ['PUT']) +@jwt_required() +@bad_token_check() +@AuthCheck() +@admin_check() +def mass_notify_new_ca_users(): + """ + Description: + This route will email individuals/teams of a related AT; + New/updated completed ATs will be notified upon successive + use. + + Parameters(from the json): + assessment_task_id: str (AT) + team: bool (is the at team based) + notification_message: str (message to send over in the email) + + Exceptions: + None all should be caught and handled + """ + try: + at_id = int(request.args.get('assessment_task_id')) + is_teams = bool(request.args.get('team')) + + msg_to_students = request.json["notification_message"] + + one_assessment_task = get_assessment_task(at_id) # Trigger an error if not exists + + return create_good_response( + "Message Sent", + 201, + "Mass notified" + ) + except Exception as e: + return create_bad_response( + f"An error occurred emailing users: {e}", "mass notified", 400 + ) \ No newline at end of file diff --git a/BackEndFlask/controller/__init__.py b/BackEndFlask/controller/__init__.py index f438f9180..8752a90c7 100644 --- a/BackEndFlask/controller/__init__.py +++ b/BackEndFlask/controller/__init__.py @@ -21,6 +21,7 @@ from controller.Routes import Feedback_routes from controller.Routes import Refresh_route from controller.Routes import Csv_routes +from controller.Routes import notification_routes from controller.security import utility from controller.security import CustomDecorators from controller.security import blacklist \ No newline at end of file diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index c98af7e48..d7e8e613e 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -70,9 +70,8 @@ class ViewCompleteIndividualAssessmentTasks extends Component { } genericResourcePUT( - `/assessment_task?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}¬ification=${true}`, + `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${false}`, this, JSON.stringify({ - "notification_date": date, "notification_message": notes }) ).then((result) => { From 94aef66ead14759597d53aff4c8fb2bcd15bff2f Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 22 Nov 2024 20:06:21 -0600 Subject: [PATCH 125/160] figuring out some datetime things --- .../controller/Routes/notification_routes.py | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 8dfaae020..6b58dafad 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -14,15 +14,12 @@ from flask import request from flask_sqlalchemy import * from controller import bp -from models.assessment_task import * -from models.course import get_course -from models.user import get_user -from models.team import get_team -from models.role import get_role +from models.assessment_task import get_assessment_task from controller.Route_response import * from models.user_course import get_user_courses_by_user_id - from flask_jwt_extended import jwt_required +import datetime + from controller.security.CustomDecorators import ( AuthCheck, bad_token_check, admin_check @@ -41,9 +38,9 @@ def mass_notify_new_ca_users(): use. Parameters(from the json): - assessment_task_id: str (AT) - team: bool (is the at team based) - notification_message: str (message to send over in the email) + assessment_task_id: r (AT) + team: (is the at team based) + notification_message: (message to send over in the email) Exceptions: None all should be caught and handled @@ -51,17 +48,23 @@ def mass_notify_new_ca_users(): try: at_id = int(request.args.get('assessment_task_id')) is_teams = bool(request.args.get('team')) - msg_to_students = request.json["notification_message"] - one_assessment_task = get_assessment_task(at_id) # Trigger an error if not exists + # Raises InvalidAssessmentTaskID if non-existant AT. + at_time = get_assessment_task(at_id).notification_sent + + # Lowest possible time for easier comparisons. + if at_time == None : at_time = datetime.datetime(1,1,1,0,0,0,0) + + with open("ap.txt", 'w') as out: + print(at_time, file=out) return create_good_response( "Message Sent", 201, - "Mass notified" + "Mass_notified" ) except Exception as e: return create_bad_response( - f"An error occurred emailing users: {e}", "mass notified", 400 + f"An error occurred emailing users: {e}", "mass_not_notified", 400 ) \ No newline at end of file From a2c0341dc42ffe485d37094612224fc4ce0694e9 Mon Sep 17 00:00:00 2001 From: Hudah Date: Mon, 25 Nov 2024 02:08:32 -0600 Subject: [PATCH 126/160] Truncated progress bar percent to be 2 decimal places max. --- .../View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js index 4a1813da7..551de6ec8 100644 --- a/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js +++ b/FrontEndReact/src/View/Admin/View/Reporting/ViewAssessmentStatus/ViewAssessmentStatus.js @@ -65,7 +65,7 @@ export default function ViewAssessmentStatus(props) { var allRatings = []; var avg = 0; var stdev = 0; - var progress = props.completedAssessmentsPercentage; + var progress = +props.completedAssessmentsPercentage.toFixed(2); if (props.completedAssessments !== null && props.completedAssessments.length > 0) { // Iterate through each completed assessment for chosen assessment task From b1fd67a8a9c011bd54e8b814118406b8c52d99d2 Mon Sep 17 00:00:00 2001 From: aldo Date: Mon, 25 Nov 2024 14:43:25 -0600 Subject: [PATCH 127/160] sending mass notifications for ats without teams works now. --- .../controller/Routes/notification_routes.py | 17 ++++++++-- BackEndFlask/models/queries.py | 34 +++++++++++++++++-- BackEndFlask/models/utility.py | 2 ++ .../ViewCompleteIndividualAssessmentTasks.js | 3 +- .../ViewCompleteTeamAssessmentTasks.js | 4 +-- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 6b58dafad..60d7d4335 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -14,10 +14,11 @@ from flask import request from flask_sqlalchemy import * from controller import bp -from models.assessment_task import get_assessment_task +from models.assessment_task import get_assessment_task, toggle_notification_sent_to_true from controller.Route_response import * -from models.user_course import get_user_courses_by_user_id +from models.queries import get_students_for_emailing from flask_jwt_extended import jwt_required +from models.utility import email_students_feedback_is_ready_to_view import datetime from controller.security.CustomDecorators import ( @@ -49,6 +50,7 @@ def mass_notify_new_ca_users(): at_id = int(request.args.get('assessment_task_id')) is_teams = bool(request.args.get('team')) msg_to_students = request.json["notification_message"] + date = request.json["date"] # Raises InvalidAssessmentTaskID if non-existant AT. at_time = get_assessment_task(at_id).notification_sent @@ -56,8 +58,17 @@ def mass_notify_new_ca_users(): # Lowest possible time for easier comparisons. if at_time == None : at_time = datetime.datetime(1,1,1,0,0,0,0) + students = get_students_for_emailing(at_id) + + left_to_notifiy = [singular_student for singular_student in students if singular_student.last_update > at_time] + + email_students_feedback_is_ready_to_view(left_to_notifiy, msg_to_students) + + #update the at noti time + toggle_notification_sent_to_true(at_id, date) + with open("ap.txt", 'w') as out: - print(at_time, file=out) + print(left_to_notifiy, file=out) return create_good_response( "Message Sent", diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index f66838a7b..2ee340af0 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1083,8 +1083,6 @@ def get_course_name_by_at_id(at_id:int) -> str : return course_name[0][0] - - def get_completed_assessment_ratio(course_id: int, assessment_task_id: int) -> int: """ Description: @@ -1126,4 +1124,34 @@ def is_admin_by_user_id(user_id: int) -> bool: if is_admin[0][0]: return True - return False \ No newline at end of file + return False + +def get_students_for_emailing(at_id: int) -> tuple[dict[str],dict[str]]: + """ + Description: + Returns the needed data for emailing students who should be reciving the notification from + their professors. + + Parameters: + at_id: (AT id) + + Returns: + tuple[dict[str],dict[str]] (The students information such as first_name, last_name, last_update, and email) + + Exceptions: + None raise by me except the database my raise some. + """ + # Note a similar function exists but its a select * query which hinders prefomance. + student_info = db.session.query( + CompletedAssessment.last_update, + User.first_name, + User.last_name, + User.email + ).join( + User, + User.user_id == CompletedAssessment.user_id + ).filter( + CompletedAssessment.assessment_task_id == at_id + ).all() + + return student_info \ No newline at end of file diff --git a/BackEndFlask/models/utility.py b/BackEndFlask/models/utility.py index 942ea62b0..27f538ea6 100644 --- a/BackEndFlask/models/utility.py +++ b/BackEndFlask/models/utility.py @@ -48,11 +48,13 @@ def email_students_feedback_is_ready_to_view(students: list, notification_messag send_email(student.email, subject, message) +##############################################################Rest the excepttion before commiting def send_email(address: str, subject: str, content: str): try: yag = yagmail.SMTP("skillbuilder02", PASSWORD) yag.send(address, subject, content) except: + return raise EmailFailureException def generate_random_password(length: int): diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index d7e8e613e..0e0d485cb 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -72,7 +72,8 @@ class ViewCompleteIndividualAssessmentTasks extends Component { genericResourcePUT( `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${false}`, this, JSON.stringify({ - "notification_message": notes + "notification_message": notes, + "date" : date }) ).then((result) => { if (result !== undefined && result.errorMessage === null) { diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js index bf3d6afe5..2e36b51a5 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js @@ -70,9 +70,9 @@ class ViewCompleteTeamAssessmentTasks extends Component { } genericResourcePUT( - `/assessment_task?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}¬ification=${true}`, + `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${true}`, this, JSON.stringify({ - "notification_date": date, + "date": date, "notification_message": notes }) ).then((result) => { From 5d53c77dbf8a6810076922cfb26385302f2f0665 Mon Sep 17 00:00:00 2001 From: aldo Date: Mon, 25 Nov 2024 16:59:54 -0600 Subject: [PATCH 128/160] The send notification button can now be used on both individuals and teams --- .../controller/Routes/notification_routes.py | 11 +++----- BackEndFlask/models/queries.py | 28 ++++++++++++++----- .../ViewCompleteIndividualAssessmentTasks.js | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 60d7d4335..e19552776 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -48,7 +48,7 @@ def mass_notify_new_ca_users(): """ try: at_id = int(request.args.get('assessment_task_id')) - is_teams = bool(request.args.get('team')) + is_teams = request.args.get('team') == "true" msg_to_students = request.json["notification_message"] date = request.json["date"] @@ -58,18 +58,15 @@ def mass_notify_new_ca_users(): # Lowest possible time for easier comparisons. if at_time == None : at_time = datetime.datetime(1,1,1,0,0,0,0) - students = get_students_for_emailing(at_id) + collection = get_students_for_emailing(at_id, is_teams) - left_to_notifiy = [singular_student for singular_student in students if singular_student.last_update > at_time] + left_to_notifiy = [singular_student for singular_student in collection if singular_student.last_update > at_time] email_students_feedback_is_ready_to_view(left_to_notifiy, msg_to_students) - #update the at noti time + # Updating AT notification time toggle_notification_sent_to_true(at_id, date) - with open("ap.txt", 'w') as out: - print(left_to_notifiy, file=out) - return create_good_response( "Message Sent", 201, diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 2ee340af0..0183b3190 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1126,7 +1126,7 @@ def is_admin_by_user_id(user_id: int) -> bool: return True return False -def get_students_for_emailing(at_id: int) -> tuple[dict[str],dict[str]]: +def get_students_for_emailing(at_id: int, is_teams: bool) -> tuple[dict[str],dict[str]]: """ Description: Returns the needed data for emailing students who should be reciving the notification from @@ -1134,6 +1134,7 @@ def get_students_for_emailing(at_id: int) -> tuple[dict[str],dict[str]]: Parameters: at_id: (AT id) + is_teams: (are we looking for students associated to a team?) Returns: tuple[dict[str],dict[str]] (The students information such as first_name, last_name, last_update, and email) @@ -1147,11 +1148,24 @@ def get_students_for_emailing(at_id: int) -> tuple[dict[str],dict[str]]: User.first_name, User.last_name, User.email - ).join( - User, - User.user_id == CompletedAssessment.user_id - ).filter( + ) + + if is_teams: + student_info = student_info.join( + TeamUser, + TeamUser.team_id == CompletedAssessment.team_id + ).join( + User, + User.user_id == TeamUser.user_id + ) + else: + student_info = student_info.join( + User, + User.user_id == CompletedAssessment.user_id + ) + + student_info = student_info.filter( CompletedAssessment.assessment_task_id == at_id - ).all() + ) - return student_info \ No newline at end of file + return student_info.all() \ No newline at end of file diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index 0e0d485cb..1ff8a2028 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -72,7 +72,7 @@ class ViewCompleteIndividualAssessmentTasks extends Component { genericResourcePUT( `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${false}`, this, JSON.stringify({ - "notification_message": notes, + "notification_message": notes, "date" : date }) ).then((result) => { From 385ca17e68df9d486d10059c0333c52547694642 Mon Sep 17 00:00:00 2001 From: aldo Date: Mon, 25 Nov 2024 17:00:32 -0600 Subject: [PATCH 129/160] I mean that it works on mass sending to individuals at or team ats --- BackEndFlask/models/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 0183b3190..e4649332c 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1168,4 +1168,4 @@ def get_students_for_emailing(at_id: int, is_teams: bool) -> tuple[dict[str],dic CompletedAssessment.assessment_task_id == at_id ) - return student_info.all() \ No newline at end of file + return student_info.all() \ No newline at end of file From 2f9eb8aae0485eac975460ba5ff98e0b13949702 Mon Sep 17 00:00:00 2001 From: aldo Date: Mon, 25 Nov 2024 18:01:36 -0600 Subject: [PATCH 130/160] might want to rework the single email sender to make code reuse a thing for email sending between single and mass notifications --- .../controller/Routes/notification_routes.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index e19552776..8dd1fd707 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -42,6 +42,7 @@ def mass_notify_new_ca_users(): assessment_task_id: r (AT) team: (is the at team based) notification_message: (message to send over in the email) + date: (date to record things) Exceptions: None all should be caught and handled @@ -75,4 +76,15 @@ def mass_notify_new_ca_users(): except Exception as e: return create_bad_response( f"An error occurred emailing users: {e}", "mass_not_notified", 400 - ) \ No newline at end of file + ) + + +@bp.route('/send_single_email', methods = ['POST']) +@jwt_required() +@bad_token_check() +@AuthCheck() +@admin_check() +def send_single_email(): + """ + """ + return \ No newline at end of file From 6a6ee36a58cb78dc2f6bcfb22e89747aefd6dc6e Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Tue, 26 Nov 2024 10:52:35 -0600 Subject: [PATCH 131/160] Make SuperAdmin also an admin; send null to db for lms_id is not set. --- BackEndFlask/models/user.py | 3 ++- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/models/user.py b/BackEndFlask/models/user.py index f3011ec6a..cd200d141 100644 --- a/BackEndFlask/models/user.py +++ b/BackEndFlask/models/user.py @@ -243,7 +243,8 @@ def load_SuperAdminUser(): "lms_id": 0, "consent": None, "owner_id": None, - "role_id": None + "role_id": None, + "is_admin": True }) # user_id = 2 diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index eb7e009d4..791169c53 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -188,7 +188,7 @@ class AdminAddUser extends Component { "first_name": firstName, "last_name": lastName, "email": email, - "lms_id": lmsId, + "lms_id": lmsId != "" ? lmsId : null, "consent": null, "owner_id": cookies.get('user')['user_id'], "role_id": navbar.props.isSuperAdmin ? 3 : role From 175f35e4954e87bcc6641e32eda9dd56980ea75f Mon Sep 17 00:00:00 2001 From: aldo Date: Tue, 26 Nov 2024 11:17:45 -0600 Subject: [PATCH 132/160] This will turn on the superadmins is_admin column to true allowing it to pass admincheck() on more secure routes --- BackEndFlask/models/user.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/models/user.py b/BackEndFlask/models/user.py index cd200d141..dc9cf4624 100644 --- a/BackEndFlask/models/user.py +++ b/BackEndFlask/models/user.py @@ -186,7 +186,7 @@ def create_user(user_data): lms_id=user_data["lms_id"], consent=user_data["consent"], owner_id=user_data["owner_id"], - is_admin="role_id" in user_data.keys() and user_data["role_id"]==3, + is_admin="role_id" in user_data.keys() and user_data["role_id"] in [1,2,3], has_set_password=has_set_password, reset_code=None ) @@ -243,7 +243,7 @@ def load_SuperAdminUser(): "lms_id": 0, "consent": None, "owner_id": None, - "role_id": None, + "role_id": 2, "is_admin": True }) From e06d67e149b16a1fa88b08b70ccdff5bef071652 Mon Sep 17 00:00:00 2001 From: aldo Date: Tue, 26 Nov 2024 12:40:09 -0600 Subject: [PATCH 133/160] changed the comparision symbol to pass checks --- FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js index 791169c53..fe4bab14c 100644 --- a/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js +++ b/FrontEndReact/src/View/Admin/Add/AddUsers/AdminAddUser.js @@ -188,7 +188,7 @@ class AdminAddUser extends Component { "first_name": firstName, "last_name": lastName, "email": email, - "lms_id": lmsId != "" ? lmsId : null, + "lms_id": lmsId !== "" ? lmsId : null, "consent": null, "owner_id": cookies.get('user')['user_id'], "role_id": navbar.props.isSuperAdmin ? 3 : role From 40b53d6cf957cd5149d9b27b65d43239a28f9d44 Mon Sep 17 00:00:00 2001 From: aldo Date: Tue, 26 Nov 2024 20:13:58 -0600 Subject: [PATCH 134/160] added a new button and added the single message route in the backend. Both are still in progress --- .../controller/Routes/notification_routes.py | 34 ++++++++++++++++++- .../ViewCompleteIndividualAssessmentTasks.js | 21 ++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 8dd1fd707..1b2b5e132 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -43,6 +43,7 @@ def mass_notify_new_ca_users(): team: (is the at team based) notification_message: (message to send over in the email) date: (date to record things) + user_id: (who is requested the route[The decorators need it]) Exceptions: None all should be caught and handled @@ -86,5 +87,36 @@ def mass_notify_new_ca_users(): @admin_check() def send_single_email(): """ + Description: + Parameters: + user_id: (who requested the route {decorators uses it}) + is_team: (is this a team or individual msg) + targeted_id: (intended student/team for the message) + msg: (The message the team or individual should recive) + + Returns: + Good or bad Response + + Exceptions: + None """ - return \ No newline at end of file + + try: + is_teams = request.args.get('team') == "true" + targeted_id = request.args.get('targeted_id') + msg = request.json['msg'] + + # Find the students to email. Need first_name, last_name, and email. + + # Put into a compreshion list and call emailing funciton(same func at above) + + + return create_good_response( + "Message Sent", + 201, + "Individual/Team notified" + ) + except Exception as e: + return create_bad_response( + f"An error occurred emailing user/s: {e}", "Individual/Team not notified", 400 + ) \ No newline at end of file diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index 1ff8a2028..f8772c15c 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -255,6 +255,27 @@ class ViewCompleteIndividualAssessmentTasks extends Component { } } } + }, + { + name: "Student/Team Id", + label: " ", + options: { + filter: false, + sort: false, + setCellHeaderProps: () => { return { align:"center", className:"button-column-alignment"}}, + setCellProps: () => { return { align:"center", className:"button-column-alignment"} }, + customBodyRender: (completedAssessmentId) => { + return ( + + ) + } + } } ]; From 60b8ce1f35c132dedf5345d7ee3eac28acf1d33d Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Wed, 27 Nov 2024 13:40:42 -0600 Subject: [PATCH 135/160] Added clarifying text to the AddCourse component. --- .../Admin/Add/AddCourse/AdminAddCourse.js | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddCourse/AdminAddCourse.js b/FrontEndReact/src/View/Admin/Add/AddCourse/AdminAddCourse.js index 0f4ddb821..3be2ad788 100644 --- a/FrontEndReact/src/View/Admin/Add/AddCourse/AdminAddCourse.js +++ b/FrontEndReact/src/View/Admin/Add/AddCourse/AdminAddCourse.js @@ -5,7 +5,8 @@ import validator from "validator"; import ErrorMessage from "../../../Error/ErrorMessage.js"; import { genericResourcePOST, genericResourcePUT } from "../../../../utility.js"; import Cookies from "universal-cookie"; -import { Box, Button, FormControl, Typography, TextField, FormControlLabel, Checkbox, FormGroup, } from "@mui/material"; +import HelpOutlineIcon from "@mui/icons-material/HelpOutline"; +import { Box, Button, FormControl, Typography, Popover, TextField, Tooltip, IconButton, FormControlLabel, Checkbox, FormGroup, } from "@mui/material"; @@ -25,7 +26,8 @@ class AdminAddCourse extends Component { year: "", active: true, useTas: true, - useFixedTeams: true, + useFixedTeams: true, + anchorEl: null, errors: { courseName: "", @@ -34,8 +36,13 @@ class AdminAddCourse extends Component { year: "", }, }; + } + setAnchorEl = (element) => { + this.setState({ anchorEl: element }); + }; + componentDidMount() { var navbar = this.props.navbar; var state = navbar.state; @@ -56,6 +63,13 @@ class AdminAddCourse extends Component { }); } } + handleClick = (event) => { + this.setAnchorEl(event.currentTarget); + }; + + handleClose = () => { + this.setAnchorEl(null); + }; handleChange = (e) => { const { id, value } = e.target; @@ -144,9 +158,6 @@ class AdminAddCourse extends Component { if (term.trim() === "") newErrors["term"] = "Term cannot be empty"; - else if (term.trim() !== "Spring" && term.trim() !== "Fall" && term.trim() !== "Summer") - newErrors["term"] = "Term should be either Spring, Fall, or Summer"; - if (newErrors["courseName"] !== "" || newErrors["courseNumber"] !== "" ||newErrors["year"] !== "" ||newErrors["term"] !== "") { this.setState({ errors: newErrors @@ -208,6 +219,8 @@ class AdminAddCourse extends Component { var navbar = this.props.navbar; var state = navbar.state; var addCourse = state.addCourse; + const open = Boolean(this.state.anchorEl); + const id = open ? 'simple-popover' : undefined; return ( @@ -266,7 +279,7 @@ class AdminAddCourse extends Component { id="term" name="newTerm" variant="outlined" - label="Term" + label="Type your Term name here" fullWidth value={term} error={!!errors.term} @@ -344,8 +357,31 @@ class AdminAddCourse extends Component { } name="newFixedTeams" - label="Fixed Team" - /> + label="Fixed Teams" + /> +
+ + + + + +
+ + Active: Uncheck this box at the end of the term to move it to the Inactive Courses table.
+
Use TA's: + Will you use Teaching or Learning Assistants in this course to fill out rubrics?
+
Fixed teams: Do you assign students to the same team for the entire semester?
+
+ Date: Wed, 27 Nov 2024 14:01:05 -0600 Subject: [PATCH 136/160] front end button can now distinguish who is calling it. Need to work on which completed assessment is calling it --- .../controller/Routes/notification_routes.py | 9 ++- .../ViewCompleteIndividualAssessmentTasks.js | 57 +++++++++++++------ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 1b2b5e132..499844798 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -103,8 +103,8 @@ def send_single_email(): try: is_teams = request.args.get('team') == "true" - targeted_id = request.args.get('targeted_id') - msg = request.json['msg'] + completed_assessment_id = request.args.get('completed_assessment_id') + msg = request.json['notification_message'] # Find the students to email. Need first_name, last_name, and email. @@ -117,6 +117,11 @@ def send_single_email(): "Individual/Team notified" ) except Exception as e: + return create_good_response( + "Message Sent", + 201, + "Individual/Team notified" + ) return create_bad_response( f"An error occurred emailing user/s: {e}", "Individual/Team not notified", 400 ) \ No newline at end of file diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index f8772c15c..f83306467 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -6,7 +6,7 @@ import IconButton from '@mui/material/IconButton'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { Box, Typography } from "@mui/material"; import CustomButton from "../../../Student/View/Components/CustomButton"; -import { genericResourcePUT } from "../../../../utility"; +import { genericResourcePOST, genericResourcePUT } from "../../../../utility"; import ResponsiveNotification from "../../../Components/SendNotification"; import CourseInfo from "../../../Components/CourseInfo"; @@ -23,6 +23,7 @@ class ViewCompleteIndividualAssessmentTasks extends Component { showDialog: false, notes: '', notificationSent: false, + singleMessage: false, errors: { notes:'' @@ -42,9 +43,10 @@ class ViewCompleteIndividualAssessmentTasks extends Component { }); }; - handleDialog = () => { + handleDialog = (isSingleMessage) => { this.setState({ showDialog: this.state.showDialog === false ? true : false, + singleMessage: isSingleMessage, }) } @@ -68,21 +70,39 @@ class ViewCompleteIndividualAssessmentTasks extends Component { return; } - - genericResourcePUT( - `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${false}`, - this, JSON.stringify({ - "notification_message": notes, - "date" : date - }) - ).then((result) => { - if (result !== undefined && result.errorMessage === null) { - this.setState({ - showDialog: false, - notificationSent: date, + if(this.state.singleMessage) { + this.setState({singleMessage: false}, () => { + genericResourcePOST( + `/send_single_email?team=${false}&completed_assessment_id=${1}`, + this, JSON.stringify({ + "notification_message": notes, + }) + ).then((result) => { + if(result !== undefined && result.errorMessage === null){ + this.setState({ + showDialog: false, + notificationSent: date, + }); + } }); - } - }); + }); + } else { + genericResourcePUT( + `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${false}`, + this, JSON.stringify({ + "notification_message": notes, + "date" : date + }) + ).then((result) => { + if (result !== undefined && result.errorMessage === null) { + this.setState({ + showDialog: false, + notificationSent: date, + }); + } + }); + } + }; render() { @@ -258,7 +278,7 @@ class ViewCompleteIndividualAssessmentTasks extends Component { }, { name: "Student/Team Id", - label: " ", + label: "Message", options: { filter: false, sort: false, @@ -267,6 +287,7 @@ class ViewCompleteIndividualAssessmentTasks extends Component { customBodyRender: (completedAssessmentId) => { return ( this.handleDialog(true)} label="Message" align="center" isOutlined={true} @@ -315,7 +336,7 @@ class ViewCompleteIndividualAssessmentTasks extends Component { this.handleDialog(false)} isOutlined={false} disabled={notificationSent} aria-label="viewCompletedAssessmentSendNotificationButton" From 1ea387fb6433b203fdbeceb2fd413e7e8898fc5f Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Wed, 27 Nov 2024 19:41:25 -0600 Subject: [PATCH 137/160] Removed an invalid test. --- .../__tests__/AdminAddCourse.test.js | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js b/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js index 7707092ba..5d659fcd5 100644 --- a/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js @@ -224,38 +224,6 @@ test("AdminAddCourse.test.js Test 7: HelperText error should show for the addCou }); }); - -test("AdminAddCourse.test.js Test 8: HelperText error should show for the addCourseTerm text field when input is not 'Fall', 'Spring' or 'Summer'", async () => { - render(); - - await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(ct); - }); - - clickElementWithAriaLabel(ac); - - await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(act); - }); - - changeElementWithAriaLabelWithInput(cnami, "Object Oriented Programming"); - - changeElementWithAriaLabelWithInput(cnumi, "CS3423"); - - changeElementWithAriaLabelWithInput(cti, "A"); - - changeElementWithAriaLabelWithInput(cyi, "2025"); - - clickElementWithAriaLabel(aosacb); - - await waitFor(() => { - expectElementWithAriaLabelToBeInDocument(acf); - - expectElementWithAriaLabelToHaveErrorMessage(cti, "Term should be either Spring, Fall, or Summer"); - }); -}); - - test("AdminAddCourse.test.js Test 9: HelperText error should show for the addCourseYear text field when input is less than 2023", async () => { render(); From b1e14722d0aa51a56963d216c1b584404161ca46 Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Wed, 27 Nov 2024 19:51:21 -0600 Subject: [PATCH 138/160] Removed another invalid test --- .../Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js b/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js index 5d659fcd5..22557c62d 100644 --- a/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js +++ b/FrontEndReact/src/View/Admin/Add/AddCourse/__tests__/AdminAddCourse.test.js @@ -224,7 +224,7 @@ test("AdminAddCourse.test.js Test 7: HelperText error should show for the addCou }); }); -test("AdminAddCourse.test.js Test 9: HelperText error should show for the addCourseYear text field when input is less than 2023", async () => { +test("AdminAddCourse.test.js Test 8: HelperText error should show for the addCourseYear text field when input is less than 2023", async () => { render(); await waitFor(() => { @@ -255,7 +255,7 @@ test("AdminAddCourse.test.js Test 9: HelperText error should show for the addCou }); -test("AdminAddCourse.test.js Test 10: HelperText error should show for the addCourseYear text field when input is not a numeric value", async () => { +test("AdminAddCourse.test.js Test 9: HelperText error should show for the addCourseYear text field when input is not a numeric value", async () => { render(); await waitFor(() => { @@ -286,7 +286,7 @@ test("AdminAddCourse.test.js Test 10: HelperText error should show for the addCo }); -test("AdminAddCourse.test.js Test 11: Filling in valid input and clicking the Add Course button should redirect you to course view page, and should contain the new course you just added", async () => { +test("AdminAddCourse.test.js Test 10: Filling in valid input and clicking the Add Course button should redirect you to course view page, and should contain the new course you just added", async () => { render(); await waitFor(() => { @@ -322,7 +322,7 @@ test("AdminAddCourse.test.js Test 11: Filling in valid input and clicking the Ad }); }); -test("AdminAddCourse.test.js Test 12: HelperText errors should show for the addCourseYear and addCourseTerm text fields when the input year is not numeric and the term is not 'Spring', 'Fall', or 'Summer'", async () => { +test("AdminAddCourse.test.js Test 11: HelperText errors should show for the addCourseYear text field when the input year is not numeric", async () => { render(); await waitFor(() => { @@ -350,6 +350,5 @@ test("AdminAddCourse.test.js Test 12: HelperText errors should show for the addC expectElementWithAriaLabelToHaveErrorMessage(cyi, "Year must be a numeric value"); - expectElementWithAriaLabelToHaveErrorMessage(cti, "Term should be either Spring, Fall, or Summer"); }); }); \ No newline at end of file From 850826bd401a3deeae974d0c6d86cc2e6fb0bcd7 Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Wed, 27 Nov 2024 20:15:25 -0600 Subject: [PATCH 139/160] Added extra helper text based on comments from Alpha testers. --- .../src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js index 0e99d2c47..5228d0cec 100644 --- a/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js +++ b/FrontEndReact/src/View/Admin/Add/AddTask/AdminAddAssessmentTask.js @@ -386,7 +386,7 @@ class AdminAddAssessmentTask extends Component { id="numberOfTeams" name="newPassword" variant='outlined' - label="Number of teams" + label="Maximum number of teams you will use during class for this assessment" value={this.state.numberOfTeams} error={!!errors.numberOfTeams} helperText={errors.numberOfTeams} @@ -406,7 +406,7 @@ class AdminAddAssessmentTask extends Component { id="maxTeamSize" name="setTeamSize" variant='outlined' - label="Max team size" + label="Max team size allowed for each team in class" value={this.state.maxTeamSize} error={!!errors.maxTeamSize} helperText={errors.maxTeamSize} @@ -534,7 +534,7 @@ class AdminAddAssessmentTask extends Component { id="password" name="newPassword" variant='outlined' - label="Password to switch teams" + label="Password to switch teams (Prevents students from switching teams without instructor approval.)" value={password} error={!!errors.password} helperText={errors.password} @@ -549,7 +549,7 @@ class AdminAddAssessmentTask extends Component { id="notes" name="notes" variant='outlined' - label="Instructions to Students/TA's" + label="Instructions for Students/TA's about the Assessment or particular focus areas" value={notes} error={!!errors.notes} helperText={errors.notes} From 42144ce7fdde12774ab6952952411b87ffd2f25c Mon Sep 17 00:00:00 2001 From: aldo Date: Wed, 27 Nov 2024 20:22:07 -0600 Subject: [PATCH 140/160] There undefiened error i need to find --- .../ViewCompleteIndividualAssessmentTasks.js | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index f83306467..faf4cb479 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -23,7 +23,8 @@ class ViewCompleteIndividualAssessmentTasks extends Component { showDialog: false, notes: '', notificationSent: false, - singleMessage: false, + isSingleMsg: false, + compATId: null, errors: { notes:'' @@ -43,15 +44,17 @@ class ViewCompleteIndividualAssessmentTasks extends Component { }); }; - handleDialog = (isSingleMessage) => { + handleDialog = (isSingleMessage, singleCompletedAT) => { + console.log(singleCompletedAT); this.setState({ showDialog: this.state.showDialog === false ? true : false, - singleMessage: isSingleMessage, - }) + isSingleMsg: isSingleMessage, + compATId: singleCompletedAT, + }); } handleSendNotification = () => { - var notes = this.state.notes; + var notes = this.state.notes; var navbar = this.props.navbar; @@ -70,10 +73,10 @@ class ViewCompleteIndividualAssessmentTasks extends Component { return; } - if(this.state.singleMessage) { - this.setState({singleMessage: false}, () => { + if(this.state.isSingleMsg) { + this.setState({isSingleMsg: false}, () => { genericResourcePOST( - `/send_single_email?team=${false}&completed_assessment_id=${1}`, + `/send_single_email?team=${false}&completed_assessment_id=${this.state.compATId}`, this, JSON.stringify({ "notification_message": notes, }) @@ -279,25 +282,32 @@ class ViewCompleteIndividualAssessmentTasks extends Component { { name: "Student/Team Id", label: "Message", + at: this.state.compATId, options: { filter: false, sort: false, setCellHeaderProps: () => { return { align:"center", className:"button-column-alignment"}}, setCellProps: () => { return { align:"center", className:"button-column-alignment"} }, customBodyRender: (completedAssessmentId) => { - return ( - this.handleDialog(true)} - label="Message" - align="center" - isOutlined={true} - disabled={notificationSent} - aria-label="viewCompletedAssessmentSendNotificationButton" - /> - ) + if (completedAssessmentId) { + return ( + this.handleDialog(true, completedAssessmentId)} + label="Message" + align="center" + isOutlined={true} + disabled={notificationSent} + aria-label="Send individual messages" + /> + ) + }else{ + return( +

{''}

+ ) + } } } - } + }, ]; const options = { From f216ad1bf4a2d4b6e26848f10fef94cd597cd677 Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 29 Nov 2024 14:26:25 -0600 Subject: [PATCH 141/160] frontend msg buttons are now in working order --- .../ViewCompleteIndividualAssessmentTasks.js | 20 ++--- .../ViewCompleteTeamAssessmentTasks.js | 90 +++++++++++++++---- 2 files changed, 82 insertions(+), 28 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js index faf4cb479..b83652f6f 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteIndividualAssessmentTasks.js @@ -45,7 +45,6 @@ class ViewCompleteIndividualAssessmentTasks extends Component { }; handleDialog = (isSingleMessage, singleCompletedAT) => { - console.log(singleCompletedAT); this.setState({ showDialog: this.state.showDialog === false ? true : false, isSingleMsg: isSingleMessage, @@ -282,14 +281,16 @@ class ViewCompleteIndividualAssessmentTasks extends Component { { name: "Student/Team Id", label: "Message", - at: this.state.compATId, options: { filter: false, sort: false, setCellHeaderProps: () => { return { align:"center", className:"button-column-alignment"}}, setCellProps: () => { return { align:"center", className:"button-column-alignment"} }, - customBodyRender: (completedAssessmentId) => { - if (completedAssessmentId) { + customBodyRender: (completedAssessmentId, completeAssessmentTasks) => { + const rowIndex = completeAssessmentTasks.rowIndex; + const completedATIndex = 5; + completedAssessmentId = completeAssessmentTasks.tableData[rowIndex][completedATIndex]; + if (completedAssessmentId !== null) { return ( this.handleDialog(true, completedAssessmentId)} @@ -355,13 +356,12 @@ class ViewCompleteIndividualAssessmentTasks extends Component {
- + - ); } diff --git a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js index 2e36b51a5..b2fa72f1b 100644 --- a/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js +++ b/FrontEndReact/src/View/Admin/View/ViewCompleteAssessmentTasks/ViewCompleteTeamAssessmentTasks.js @@ -6,7 +6,7 @@ import IconButton from '@mui/material/IconButton'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { Box, Typography } from "@mui/material"; import CustomButton from "../../../Student/View/Components/CustomButton"; -import { genericResourcePUT } from "../../../../utility"; +import { genericResourcePUT, genericResourcePOST } from "../../../../utility"; import ResponsiveNotification from "../../../Components/SendNotification"; import CourseInfo from "../../../Components/CourseInfo"; @@ -23,6 +23,8 @@ class ViewCompleteTeamAssessmentTasks extends Component { showDialog: false, notes: '', notificationSent: false, + isSingleMsg: false, + compATId: null, errors: { notes:'' @@ -42,10 +44,12 @@ class ViewCompleteTeamAssessmentTasks extends Component { }); }; - handleDialog = () => { + handleDialog = (isSingleMessage, singleCompletedAT) => { this.setState({ showDialog: this.state.showDialog === false ? true : false, - }) + isSingleMsg: isSingleMessage, + compATId: singleCompletedAT, + }); } handleSendNotification = () => { @@ -69,20 +73,39 @@ class ViewCompleteTeamAssessmentTasks extends Component { return; } - genericResourcePUT( - `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${true}`, - this, JSON.stringify({ - "date": date, - "notification_message": notes - }) - ).then((result) => { - if (result !== undefined && result.errorMessage === null) { - this.setState({ - showDialog: false, - notificationSent: date, + if(this.state.isSingleMsg){ + this.setState({isSingleMsg: false}, () => { + genericResourcePOST( + `/send_single_email?team=${true}&completed_assessment_id=${this.state.compATId}`, + this, JSON.stringify({ + "notification_message": notes, + }) + ).then((result) => { + if(result !== undefined && result.errorMessage === null){ + this.setState({ + showDialog: false, + notificationSent: date, + }); + } }); - } - }); + }); + }else{ + genericResourcePUT( + `/mass_notification?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}&team=${true}`, + this, JSON.stringify({ + "date": date, + "notification_message": notes + }) + ).then((result) => { + if (result !== undefined && result.errorMessage === null) { + this.setState({ + showDialog: false, + notificationSent: date, + }); + } + }); + } + }; render() { @@ -255,7 +278,38 @@ class ViewCompleteTeamAssessmentTasks extends Component { } } } - } + }, + { + name: "Student/Team Id", + label: "Message", + options: { + filter: false, + sort: false, + setCellHeaderProps: () => { return { align:"center", className:"button-column-alignment"}}, + setCellProps: () => { return { align:"center", className:"button-column-alignment"} }, + customBodyRender: (completedAssessmentId, completeAssessmentTasks) => { + const rowIndex = completeAssessmentTasks.rowIndex; + const completedATIndex = 5; + completedAssessmentId = completeAssessmentTasks.tableData[rowIndex][completedATIndex]; + if (completedAssessmentId !== null) { + return ( + this.handleDialog(true, completedAssessmentId)} + label="Message" + align="center" + isOutlined={true} + disabled={notificationSent} + aria-label="Send individual messages" + /> + ) + }else{ + return( +

{''}

+ ) + } + } + } + }, ]; const options = { @@ -294,7 +348,7 @@ class ViewCompleteTeamAssessmentTasks extends Component { this.handleDialog(false)} isOutlined={false} disabled={notificationSent} aria-label="viewCompletedAssessmentSendNotificationButton" From 5df054804293ed3b4108b87c2f1b58de1862bf3e Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 29 Nov 2024 14:56:29 -0600 Subject: [PATCH 142/160] modified query for finding students to email --- .../controller/Routes/notification_routes.py | 2 +- BackEndFlask/models/queries.py | 26 +++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 499844798..efc0ff369 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -60,7 +60,7 @@ def mass_notify_new_ca_users(): # Lowest possible time for easier comparisons. if at_time == None : at_time = datetime.datetime(1,1,1,0,0,0,0) - collection = get_students_for_emailing(at_id, is_teams) + collection = get_students_for_emailing(is_teams, at_id=at_id) left_to_notifiy = [singular_student for singular_student in collection if singular_student.last_update > at_time] diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index e4649332c..6911fcc42 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1126,23 +1126,28 @@ def is_admin_by_user_id(user_id: int) -> bool: return True return False -def get_students_for_emailing(at_id: int, is_teams: bool) -> tuple[dict[str],dict[str]]: +def get_students_for_emailing(is_teams: bool, completed_at_id: int = None, at_id: int = None) -> tuple[dict[str],dict[str]]: """ Description: Returns the needed data for emailing students who should be reciving the notification from - their professors. + their professors. Note that it can also work for it you have a at_id or completed_at_id. Parameters: - at_id: (AT id) is_teams: (are we looking for students associated to a team?) - + at_id: (assessment Id) + completed_at_id: (Completed assessment Id) + Returns: tuple[dict[str],dict[str]] (The students information such as first_name, last_name, last_update, and email) Exceptions: - None raise by me except the database my raise some. + TypeError if completed_id and at_id are None. """ # Note a similar function exists but its a select * query which hinders prefomance. + + if at_id is None and completed_at_id is None: + raise TypeError("Both at_id and completed_at_id can not be .") + student_info = db.session.query( CompletedAssessment.last_update, User.first_name, @@ -1164,8 +1169,13 @@ def get_students_for_emailing(at_id: int, is_teams: bool) -> tuple[dict[str],dic User.user_id == CompletedAssessment.user_id ) - student_info = student_info.filter( - CompletedAssessment.assessment_task_id == at_id - ) + if at_id is not None: + student_info = student_info.filter( + CompletedAssessment.assessment_task_id == at_id + ) + else: + student_info = student_info.filter( + CompletedAssessment.completed_assessment_id == completed_at_id + ) return student_info.all() \ No newline at end of file From 0b56aff7d7927bf332f653e490df0f4cea77f564 Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 29 Nov 2024 15:06:15 -0600 Subject: [PATCH 143/160] backend and frontend single messaging are now hooked up --- BackEndFlask/controller/Routes/notification_routes.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index efc0ff369..8372e8ddf 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -106,10 +106,12 @@ def send_single_email(): completed_assessment_id = request.args.get('completed_assessment_id') msg = request.json['notification_message'] - # Find the students to email. Need first_name, last_name, and email. + collection = get_students_for_emailing(is_teams, completed_at_id= completed_assessment_id) - # Put into a compreshion list and call emailing funciton(same func at above) + # Putting into a list as thats what the function wants. + left_to_notifiy = [singular_student for singular_student in collection] + email_students_feedback_is_ready_to_view(left_to_notifiy, msg) return create_good_response( "Message Sent", @@ -117,11 +119,6 @@ def send_single_email(): "Individual/Team notified" ) except Exception as e: - return create_good_response( - "Message Sent", - 201, - "Individual/Team notified" - ) return create_bad_response( f"An error occurred emailing user/s: {e}", "Individual/Team not notified", 400 ) \ No newline at end of file From 743efb12df43e0cd6afb5b8bc1c4c806dbfa2bb0 Mon Sep 17 00:00:00 2001 From: aldo Date: Fri, 29 Nov 2024 18:15:52 -0600 Subject: [PATCH 144/160] updated documentation --- BackEndFlask/controller/Routes/notification_routes.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BackEndFlask/controller/Routes/notification_routes.py b/BackEndFlask/controller/Routes/notification_routes.py index 8372e8ddf..a651d31d9 100644 --- a/BackEndFlask/controller/Routes/notification_routes.py +++ b/BackEndFlask/controller/Routes/notification_routes.py @@ -45,6 +45,9 @@ def mass_notify_new_ca_users(): date: (date to record things) user_id: (who is requested the route[The decorators need it]) + Returns: + Bad or good response. + Exceptions: None all should be caught and handled """ @@ -88,6 +91,9 @@ def mass_notify_new_ca_users(): def send_single_email(): """ Description: + This function sends emails to select single students or teams based on a completed_assessment_id. + The function was teased out from the above function to allow the addition of new features. + Parameters: user_id: (who requested the route {decorators uses it}) is_team: (is this a team or individual msg) From a45a1dfda9a34a405d5d282a388aefcd1582ea52 Mon Sep 17 00:00:00 2001 From: aldo Date: Sun, 1 Dec 2024 12:31:38 -0600 Subject: [PATCH 145/160] Allowing email to raise the proper errors again. --- BackEndFlask/models/utility.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/BackEndFlask/models/utility.py b/BackEndFlask/models/utility.py index 27f538ea6..942ea62b0 100644 --- a/BackEndFlask/models/utility.py +++ b/BackEndFlask/models/utility.py @@ -48,13 +48,11 @@ def email_students_feedback_is_ready_to_view(students: list, notification_messag send_email(student.email, subject, message) -##############################################################Rest the excepttion before commiting def send_email(address: str, subject: str, content: str): try: yag = yagmail.SMTP("skillbuilder02", PASSWORD) yag.send(address, subject, content) except: - return raise EmailFailureException def generate_random_password(length: int): From 5648ef11978defbdff800882b5867e2c69b3bd98 Mon Sep 17 00:00:00 2001 From: aldo Date: Sun, 1 Dec 2024 13:32:38 -0600 Subject: [PATCH 146/160] fixed mysql issue that arose --- BackEndFlask/Functions/exportCsv.py | 29 +++++++++++++++++++++++++---- BackEndFlask/models/queries.py | 3 ++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/BackEndFlask/Functions/exportCsv.py b/BackEndFlask/Functions/exportCsv.py index 78a981c12..80808a511 100644 --- a/BackEndFlask/Functions/exportCsv.py +++ b/BackEndFlask/Functions/exportCsv.py @@ -212,7 +212,7 @@ def _format(self) -> None: self._singular[Csv_Data.USER_ID.value], self._singular[Csv_Data.TEAM_ID.value], self._at_id, category) - + # Adding the other column names which are the ocs and sfi text. headers += ["OC:" + i[0] for i in oc_sfi_per_category[0]] + ["SFI:" + i[0] for i in oc_sfi_per_category[1]] @@ -234,14 +234,32 @@ def _format(self) -> None: self._writer.writerow(row) self._writer.writerow(['']) +class Comments_Csv(Csv_Creation): + """ + Description: Singleton that creates a csv string of comments per category per student. + """ + def __init__(self, at_id: int) -> None: + """ + Parameters: + at_id: + """ + super().__init__(at_id) + + def _format(self) -> None: + """ + Description: Formats the data in the csv string. + Exceptions: None except what IO can rise. + """ + class CSV_Type(Enum): """ Description: This is the enum for the different types of csv file formats the clients have requested. """ RATING_CSV = 0 OCS_SFI_CSV = 1 + COMMENTS_CSV = 2 -def create_csv_strings(at_id:int, type_csv=CSV_Type.OCS_SFI_CSV.value) -> str: +def create_csv_strings(at_id:int, type_csv:int=1) -> str: """ Description: Creates a csv file with the data in the format specified by type_csv. @@ -254,10 +272,13 @@ def create_csv_strings(at_id:int, type_csv=CSV_Type.OCS_SFI_CSV.value) -> str: Exceptions: None except the chance the database or IO calls raise one. """ + type_csv = CSV_Type(type_csv) match type_csv: - case CSV_Type.RATING_CSV.value: + case CSV_Type.RATING_CSV: return Ratings_Csv(at_id).return_csv_str() - case CSV_Type.OCS_SFI_CSV.value: + case CSV_Type.OCS_SFI_CSV: return Ocs_Sfis_Csv(at_id).return_csv_str() + case CSV_Type.COMMENTS_CSV: + return Comments_Csv(at_id).return_csv_str() case _: return "No current class meets the deisred csv format. Error in create_csv_strings()." \ No newline at end of file diff --git a/BackEndFlask/models/queries.py b/BackEndFlask/models/queries.py index 3429077ac..02d38a639 100644 --- a/BackEndFlask/models/queries.py +++ b/BackEndFlask/models/queries.py @@ -1026,7 +1026,8 @@ def get_csv_categories(rubric_id: int, user_id: int, team_id: int, at_id: int, c for i in range(0, 2): ocs_sfis_query[i] = db.session.query( - ObservableCharacteristic.observable_characteristic_text if i == 0 else SuggestionsForImprovement.suggestion_text + ObservableCharacteristic.observable_characteristic_text if i == 0 else SuggestionsForImprovement.suggestion_text, + ObservableCharacteristic.observable_characteristics_id if i == 0 else SuggestionsForImprovement.suggestion_id, ).join( Category, (ObservableCharacteristic.category_id if i == 0 else SuggestionsForImprovement.category_id) == Category.category_id From 17df81264eaa5a1800b3fb1beb303d9b7b2f430e Mon Sep 17 00:00:00 2001 From: aldo Date: Sun, 1 Dec 2024 14:02:06 -0600 Subject: [PATCH 147/160] back-end can now do a ratings and comments output --- BackEndFlask/Functions/exportCsv.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/Functions/exportCsv.py b/BackEndFlask/Functions/exportCsv.py index 80808a511..dad1c799e 100644 --- a/BackEndFlask/Functions/exportCsv.py +++ b/BackEndFlask/Functions/exportCsv.py @@ -250,14 +250,34 @@ def _format(self) -> None: Description: Formats the data in the csv string. Exceptions: None except what IO can rise. """ + column_name = ["First Name"] + ["Last Name"] if not self._is_teams else ["Team Name"] + + # Adding the column name. Noitice that done and comments is skipped since they are categories but are not important. + column_name += [i for i in self._singular[Csv_Data.JSON.value] if (i != "done" and i !="comments")] + + with open("ap.txt", 'w') as out: + print(self._singular, file=out) + + self._writer.writerow(column_name) + + row_info = None + + # Notice that in the list comphrehensions done and comments are skiped since they are categories but dont hold relavent data. + for individual in self._completed_assessment_data: + + row_info = [individual[Csv_Data.FIRST_NAME.value]] + [individual[Csv_Data.LAST_NAME.value]] if not self._is_teams else [individual[Csv_Data.TEAM_NAME.value]] + + row_info += [individual[Csv_Data.JSON.value][category]["comments"] for category in individual[Csv_Data.JSON.value] if (category != "done" and category !="comments")] + + self._writer.writerow(row_info) class CSV_Type(Enum): """ Description: This is the enum for the different types of csv file formats the clients have requested. """ RATING_CSV = 0 - OCS_SFI_CSV = 1 - COMMENTS_CSV = 2 + OCS_SFI_CSV = 2 + COMMENTS_CSV = 1 def create_csv_strings(at_id:int, type_csv:int=1) -> str: """ From 16c2f9721f56962023c85d0e8f8f57c567158e0e Mon Sep 17 00:00:00 2001 From: aldo Date: Sun, 1 Dec 2024 14:03:39 -0600 Subject: [PATCH 148/160] removing debugging code --- BackEndFlask/Functions/exportCsv.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/BackEndFlask/Functions/exportCsv.py b/BackEndFlask/Functions/exportCsv.py index dad1c799e..396201b8e 100644 --- a/BackEndFlask/Functions/exportCsv.py +++ b/BackEndFlask/Functions/exportCsv.py @@ -255,9 +255,6 @@ def _format(self) -> None: # Adding the column name. Noitice that done and comments is skipped since they are categories but are not important. column_name += [i for i in self._singular[Csv_Data.JSON.value] if (i != "done" and i !="comments")] - with open("ap.txt", 'w') as out: - print(self._singular, file=out) - self._writer.writerow(column_name) row_info = None @@ -276,8 +273,8 @@ class CSV_Type(Enum): Description: This is the enum for the different types of csv file formats the clients have requested. """ RATING_CSV = 0 - OCS_SFI_CSV = 2 - COMMENTS_CSV = 1 + OCS_SFI_CSV = 1 + COMMENTS_CSV = 2 def create_csv_strings(at_id:int, type_csv:int=1) -> str: """ From 39f762df8dcb994cd499e0586afa9b62fe5d19e8 Mon Sep 17 00:00:00 2001 From: aldo Date: Sun, 1 Dec 2024 14:14:23 -0600 Subject: [PATCH 149/160] New error catching --- BackEndFlask/Functions/exportCsv.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/BackEndFlask/Functions/exportCsv.py b/BackEndFlask/Functions/exportCsv.py index 396201b8e..1e0476524 100644 --- a/BackEndFlask/Functions/exportCsv.py +++ b/BackEndFlask/Functions/exportCsv.py @@ -289,7 +289,10 @@ def create_csv_strings(at_id:int, type_csv:int=1) -> str: Exceptions: None except the chance the database or IO calls raise one. """ - type_csv = CSV_Type(type_csv) + try: + type_csv = CSV_Type(type_csv) + except: + raise ValueError("No type of csv is associated for the value passed.") match type_csv: case CSV_Type.RATING_CSV: return Ratings_Csv(at_id).return_csv_str() @@ -298,4 +301,4 @@ def create_csv_strings(at_id:int, type_csv:int=1) -> str: case CSV_Type.COMMENTS_CSV: return Comments_Csv(at_id).return_csv_str() case _: - return "No current class meets the deisred csv format. Error in create_csv_strings()." \ No newline at end of file + return "Error in create_csv_strings()." \ No newline at end of file From 54add997602d76abc32b4a1cb19fd0d0984fedf5 Mon Sep 17 00:00:00 2001 From: AdhamA Date: Sun, 1 Dec 2024 22:07:36 -0600 Subject: [PATCH 150/160] Fix: update wording in new user email message --- BackEndFlask/models/utility.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/BackEndFlask/models/utility.py b/BackEndFlask/models/utility.py index 942ea62b0..e0b61188c 100644 --- a/BackEndFlask/models/utility.py +++ b/BackEndFlask/models/utility.py @@ -13,10 +13,15 @@ def send_new_user_email(address: str, password: str): subject = "Welcome to Skillbuilder!" - message = f'''Your password is {password}. You will need to choose a new password after logging in for the first time. - - Cheers, - The Skillbuilder Team''' + message = f'''Welcome to SkillBuilder, a tool to enhance your learning experience this semester! This app will be our hub for assessing and providing feedback on transferable skills (these are also referred to as process skills, professional skills, durable skills). + + Access the app at this link: skill-builder.net + + Login Information: Your Username is {address} + + Temporary Password: {password} + + Please change your password after your first login to keep your account secure.''' send_email(address, subject, message) From d15d106c5159888a1849cae85e5d58798d3b21b0 Mon Sep 17 00:00:00 2001 From: Susan Hammond Date: Mon, 2 Dec 2024 13:39:13 -0600 Subject: [PATCH 151/160] Hot fix on MySQL Foreign key constraint causing assessment tasks to not save. --- .../controller/Routes/Completed_assessment_routes.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BackEndFlask/controller/Routes/Completed_assessment_routes.py b/BackEndFlask/controller/Routes/Completed_assessment_routes.py index eadf8c013..ced1c1ef3 100644 --- a/BackEndFlask/controller/Routes/Completed_assessment_routes.py +++ b/BackEndFlask/controller/Routes/Completed_assessment_routes.py @@ -159,17 +159,20 @@ def get_completed_assessment_by_team_or_user_id(): def add_completed_assessment(): try: assessment_data = request.json - team_id = int(assessment_data["team_id"]) + if (team_id == -1): + assessment_data["team_id"] = None assessment_task_id = int(request.args.get("assessment_task_id")) user_id = int(assessment_data["user_id"]) + if (user_id == -1): + assessment_data["user_id"] = None completed = completed_assessment_exists(team_id, assessment_task_id, user_id) if completed: - completed = replace_completed_assessment(request.json, completed.completed_assessment_id) + completed = replace_completed_assessment(assessment_data, completed.completed_assessment_id) else: - completed = create_completed_assessment(request.json) + completed = create_completed_assessment(assessment_data) return create_good_response(completed_assessment_schema.dump(completed), 201, "completed_assessments") From b0e86b1c999e017b0fbdb5112228966493ca770f Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Mon, 2 Dec 2024 14:38:25 -0600 Subject: [PATCH 152/160] almost done, only an error when clicked on submit --- .../Admin/View/ViewDashboard/Notifications.js | 155 ++++++++++++++++++ .../src/View/Components/SendMessageModal.js | 27 ++- FrontEndReact/src/View/Navbar/AppState.js | 74 ++++----- 3 files changed, 202 insertions(+), 54 deletions(-) create mode 100644 FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js diff --git a/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js b/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js new file mode 100644 index 000000000..a0e5b5d13 --- /dev/null +++ b/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js @@ -0,0 +1,155 @@ +import React, { Component } from "react"; +import "bootstrap/dist/css/bootstrap.css"; +import "../../../../SBStyles.css"; +import { Box, Typography } from "@mui/material"; +import CustomButton from "../../../Student/View/Components/CustomButton.js"; +import { genericResourcePUT } from "../../../../utility.js"; +import SendMessageModal from '../../../Components/SendMessageModal.js'; +import CustomDataTable from "../../../Components/CustomDataTable.js"; + + +class ViewNotification extends Component { + constructor(props) { + super(props); + + this.state = { + errorMessage: null, + isLoaded: null, + showDialog: false, + // notes: '', + emailSubject: '', + emailMessage: '', + notificationSent: false, + + errors: { + // notes:'' + emailSubject: '', + emailMessage: '', + } + }; + } + + handleChange = (e) => { + const { id, value } = e.target; + + this.setState({ + [id]: value, + errors: { + ...this.state.errors, + [id]: value.trim() === '' ? `${id.charAt(0).toUpperCase() + id.slice(1)} cannot be empty` : '', + }, + }); + }; + + handleDialog = () => { + this.setState({ + showDialog: this.state.showDialog === false ? true : false, + }) + } + + handleSendNotification = () => { + // var notes = this.state.notes; + + var emailSubject = this.state.emailSubject; + + var emailMessage = this.state.emailMessage; + + // var navbar = this.props.navbar; + + // var state = navbar.state; + + // var chosenAssessmentTask = state.chosenAssessmentTask; + + var date = new Date(); + + if (emailSubject.trim() === '' && emailMessage.trim() === '') { + this.setState({ + errors: { + emailSubject: 'Subject cannot be empty', + emailMessage: 'Message cannot be empty', + }, + }); + + return; + } + + if (emailMessage.trim() === '') { + this.setState({ + errors: { + emailMessage: 'Message cannot be empty', + }, + }); + + return; + } + + + if (emailSubject.trim() === '') { + this.setState({ + errors: { + emailSubject: 'Subject cannot be empty', + }, + }); + + return; + } + + genericResourcePUT( + // `/assessment_task?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}¬ification=${true}`, + this, JSON.stringify({ + "notification_date": date, + "notification_message": emailMessage + }) + ).then((result) => { + if (result !== undefined && result.errorMessage === null) { + this.setState({ + showDialog: false, + notificationSent: date, + }); + } + }); + }; + + render() { + var navbar = this.props.navbar; + + var state = navbar.state; + + var notificationSent = state.notificationSent; + + return ( + + + View Notifications + + + + + + + + + + + ); + } +} + +export default ViewNotification; diff --git a/FrontEndReact/src/View/Components/SendMessageModal.js b/FrontEndReact/src/View/Components/SendMessageModal.js index 4ceb6e458..115575446 100644 --- a/FrontEndReact/src/View/Components/SendMessageModal.js +++ b/FrontEndReact/src/View/Components/SendMessageModal.js @@ -16,7 +16,7 @@ export default function SendMessageModal ( props ) { aria-labelledby="responsive-dialog-title" > - {"Send Notification to Admins"} + {"Send Message to Admins"} @@ -31,9 +31,9 @@ export default function SendMessageModal ( props ) { name="emailSubject" variant='outlined' label="Add Subject" - value={props.emailSubject || ''} - error={!!(props.error && props.error.emailSubject)} - helperText={props.error?.emailSubject || ''} + value={props.emailSubject} + error={!!props.error.emailSubject} + helperText={props.error.emailSubject} onChange={props.handleChange} required fullWidth @@ -48,9 +48,9 @@ export default function SendMessageModal ( props ) { name="emailMessage" variant='outlined' label="Add Message" - value={props.emailMessage || ''} - error={!!(props.error && props.error.emailMessage)} - helperText={props.error?.emailMessage || ''} + value={props.emailMessage} + error={!!props.error.emailMessage} + helperText={props.error.emailMessage} onChange={props.handleChange} required multiline @@ -63,18 +63,11 @@ export default function SendMessageModal ( props ) { - - diff --git a/FrontEndReact/src/View/Navbar/AppState.js b/FrontEndReact/src/View/Navbar/AppState.js index 0298d0623..054ddaace 100644 --- a/FrontEndReact/src/View/Navbar/AppState.js +++ b/FrontEndReact/src/View/Navbar/AppState.js @@ -29,8 +29,8 @@ import StudentNavigation from '../Components/StudentNavigation.js'; import ReportingDashboard from '../Admin/View/Reporting/ReportingDashboard.js'; import AdminAddCustomRubric from '../Admin/Add/AddCustomRubric/AdminAddCustomRubric.js'; import AdminViewCustomRubrics from '../Admin/View/ViewCustomRubrics/AdminViewCustomRubrics.js'; -import UserAccount from './UserAccount.js' -import SendMessageModal from '../Components/SendMessageModal.js'; +import UserAccount from './UserAccount.js'; +import ViewNotification from '../Admin/View/ViewDashboard/Notifications.js'; class AppState extends Component { @@ -465,38 +465,41 @@ class AppState extends Component { {this.state.activeTab==="SuperAdminUsers" && -
+
Users - - - + +
+ + +
+
- @@ -847,17 +850,14 @@ class AppState extends Component { /> } - - {this.state.activeTab==="SendMessageModal" && + {this.state.activeTab==="ViewNotification" && - - } From ea2ed82f40d98adfccf41673f066cd26c471b5bb Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Mon, 2 Dec 2024 15:59:42 -0600 Subject: [PATCH 153/160] Is working but no genericResourePUT since we have nothing in that page --- .../Admin/View/ViewDashboard/Notifications.js | 37 +++++++++---------- .../src/View/Components/SendMessageModal.js | 12 +++++- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js b/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js index a0e5b5d13..f098ef57a 100644 --- a/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js +++ b/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js @@ -7,7 +7,6 @@ import { genericResourcePUT } from "../../../../utility.js"; import SendMessageModal from '../../../Components/SendMessageModal.js'; import CustomDataTable from "../../../Components/CustomDataTable.js"; - class ViewNotification extends Component { constructor(props) { super(props); @@ -48,17 +47,15 @@ class ViewNotification extends Component { } handleSendNotification = () => { - // var notes = this.state.notes; - var emailSubject = this.state.emailSubject; var emailMessage = this.state.emailMessage; - // var navbar = this.props.navbar; + var navbar = this.props.navbar; - // var state = navbar.state; + var state = navbar.state; - // var chosenAssessmentTask = state.chosenAssessmentTask; + var chosenAssessmentTask = state.chosenAssessmentTask; var date = new Date(); @@ -94,20 +91,20 @@ class ViewNotification extends Component { return; } - genericResourcePUT( - // `/assessment_task?assessment_task_id=${chosenAssessmentTask["assessment_task_id"]}¬ification=${true}`, - this, JSON.stringify({ - "notification_date": date, - "notification_message": emailMessage - }) - ).then((result) => { - if (result !== undefined && result.errorMessage === null) { - this.setState({ - showDialog: false, - notificationSent: date, - }); - } - }); + // genericResourcePUT( + // this, JSON.stringify({ + // "notification_date": date, + // "notification_subject": emailSubject, + // "notification_message": emailMessage + // }) + // ).then((result) => { + // if (result !== undefined && result.errorMessage === null) { + // this.setState({ + // showDialog: false, + // notificationSent: date, + // }); + // } + // }); }; render() { diff --git a/FrontEndReact/src/View/Components/SendMessageModal.js b/FrontEndReact/src/View/Components/SendMessageModal.js index 115575446..31af4699d 100644 --- a/FrontEndReact/src/View/Components/SendMessageModal.js +++ b/FrontEndReact/src/View/Components/SendMessageModal.js @@ -66,7 +66,17 @@ export default function SendMessageModal ( props ) { - */} + From b371190749867de5aa4038c367c20b8e8ae7b095 Mon Sep 17 00:00:00 2001 From: AdhamA Date: Mon, 2 Dec 2024 21:09:28 -0600 Subject: [PATCH 154/160] fix: Update datetime handling to respect user timezone --- FrontEndReact/src/utility.js | 40 ++++++++++++++---------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/FrontEndReact/src/utility.js b/FrontEndReact/src/utility.js index 5b61bcdde..3d5009743 100644 --- a/FrontEndReact/src/utility.js +++ b/FrontEndReact/src/utility.js @@ -271,37 +271,27 @@ export function getDueDateString(dueDate) { } export function getHumanReadableDueDate(dueDate, timeZone) { - dueDate = dueDate.substring(5); + const date = new Date(dueDate); - var month = Number(dueDate.substring(0, 2)) - 1; + const month = date.getMonth(); - dueDate = dueDate.substring(3); + const day = date.getDate(); - var day = Number(dueDate.substring(0, 2)); - - dueDate = dueDate.substring(3); - - var hour = Number(dueDate.substring(0, 2)); - - var twelveHourClock = hour < 12 ? "am": "pm"; - - hour = hour > 12 ? (hour % 12) : hour; - - hour = hour === 0 ? 12 : hour; - - dueDate = dueDate.substring(3); - - var minute = Number(dueDate.substring(0, 2)); + const hour = date.getHours(); + const minute = date.getMinutes(); + const monthNames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; + + const twelveHourClock = hour < 12 ? "am": "pm"; - var minutesString = minute < 10 ? ("0" + minute): minute; - - var timeString = `${hour}:${minutesString}${twelveHourClock}`; - - var dueDateString = `${monthNames[month]} ${day} at ${timeString} ${timeZone ? timeZone : ""}`; - - return dueDateString; + const displayHour = hour > 12 ? (hour % 12) : (hour === 0 ? 12 : hour); + + const minutesString = minute < 10 ? ("0" + minute): minute; + + const timeString = `${displayHour}:${minutesString}${twelveHourClock}`; + + return `${monthNames[month]} ${day} at ${timeString} ${timeZone ? timeZone : ""}`; } /** From 422966437cde2e4e4ba183dbf2e47fe83ce13201 Mon Sep 17 00:00:00 2001 From: zaramalik03 Date: Tue, 3 Dec 2024 15:59:41 -0600 Subject: [PATCH 155/160] removed unecessary comments --- .../Admin/View/ViewDashboard/Notifications.js | 27 ------------------- .../src/View/Components/SendMessageModal.js | 4 +-- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js b/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js index f098ef57a..b9195bb92 100644 --- a/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js +++ b/FrontEndReact/src/View/Admin/View/ViewDashboard/Notifications.js @@ -3,7 +3,6 @@ import "bootstrap/dist/css/bootstrap.css"; import "../../../../SBStyles.css"; import { Box, Typography } from "@mui/material"; import CustomButton from "../../../Student/View/Components/CustomButton.js"; -import { genericResourcePUT } from "../../../../utility.js"; import SendMessageModal from '../../../Components/SendMessageModal.js'; import CustomDataTable from "../../../Components/CustomDataTable.js"; @@ -15,13 +14,11 @@ class ViewNotification extends Component { errorMessage: null, isLoaded: null, showDialog: false, - // notes: '', emailSubject: '', emailMessage: '', notificationSent: false, errors: { - // notes:'' emailSubject: '', emailMessage: '', } @@ -51,14 +48,6 @@ class ViewNotification extends Component { var emailMessage = this.state.emailMessage; - var navbar = this.props.navbar; - - var state = navbar.state; - - var chosenAssessmentTask = state.chosenAssessmentTask; - - var date = new Date(); - if (emailSubject.trim() === '' && emailMessage.trim() === '') { this.setState({ errors: { @@ -91,20 +80,6 @@ class ViewNotification extends Component { return; } - // genericResourcePUT( - // this, JSON.stringify({ - // "notification_date": date, - // "notification_subject": emailSubject, - // "notification_message": emailMessage - // }) - // ).then((result) => { - // if (result !== undefined && result.errorMessage === null) { - // this.setState({ - // showDialog: false, - // notificationSent: date, - // }); - // } - // }); }; render() { @@ -140,8 +115,6 @@ class ViewNotification extends Component { diff --git a/FrontEndReact/src/View/Components/SendMessageModal.js b/FrontEndReact/src/View/Components/SendMessageModal.js index 31af4699d..51509df5d 100644 --- a/FrontEndReact/src/View/Components/SendMessageModal.js +++ b/FrontEndReact/src/View/Components/SendMessageModal.js @@ -72,8 +72,8 @@ export default function SendMessageModal ( props ) {