diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 00000000..9132983a --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,32 @@ +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Java CI with Maven + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + - name: Build with Maven + run: mvn -B package + working-directory: ./IndividualProject diff --git a/Google-Cloud-Instance.png b/Google-Cloud-Instance.png new file mode 100644 index 00000000..9bcc99d5 Binary files /dev/null and b/Google-Cloud-Instance.png differ diff --git a/IndividualProject/data.txt b/IndividualProject/data.txt new file mode 100644 index 00000000..52e564ee Binary files /dev/null and b/IndividualProject/data.txt differ diff --git a/IndividualProject/dummy-data.txt b/IndividualProject/dummy-data.txt new file mode 100644 index 00000000..947bf33e Binary files /dev/null and b/IndividualProject/dummy-data.txt differ diff --git a/IndividualProject/pom.xml b/IndividualProject/pom.xml index 6d87d4c9..b72f33b3 100644 --- a/IndividualProject/pom.xml +++ b/IndividualProject/pom.xml @@ -21,7 +21,33 @@ org.springframework.boot spring-boot-starter-web - + + org.springframework.boot + spring-boot-starter-test + test + + + org.ow2.asm + asm + 9.2 + + + net.sourceforge.pmd + pmd-java + 6.40.0 + + + org.mockito + mockito-core + 4.8.0 + test + + + org.mockito + mockito-inline + 4.8.0 + test + org.springframework.boot spring-boot-starter-test @@ -107,6 +133,16 @@ + + org.apache.maven.plugins + maven-pmd-plugin + 3.15.0 + + + category/java/bestpractices.xml + + + diff --git a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Course.java b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Course.java index 272c94c9..f7c416f7 100644 --- a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Course.java +++ b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Course.java @@ -1,7 +1,15 @@ package dev.coms4156.project.individualproject; -import java.io.*; - +import java.io.Serial; +import java.io.Serializable; + +/** + * The Course class represents a course in a department. + * This class contains attributes and methods related to managing + * a course, including course code, location, and other relevant details. + * It implements Serializable to allow the course objects to be saved and + * retrieved from storage. + */ public class Course implements Serializable { /** @@ -20,34 +28,39 @@ public Course(String instructorName, String courseLocation, String timeSlot, int this.enrolledStudentCount = 500; } - /** + /** * Enrolls a student in the course if there is space available. * * @return true if the student is successfully enrolled, false otherwise. */ public boolean enrollStudent() { - enrolledStudentCount++; + if (enrolledStudentCount < enrollmentCapacity) { + enrolledStudentCount++; + return true; + } return false; } - /** + /** * Drops a student from the course if a student is enrolled. * * @return true if the student is successfully dropped, false otherwise. */ public boolean dropStudent() { - enrolledStudentCount--; + if (enrolledStudentCount > 0) { + enrolledStudentCount--; + return true; + } return false; } - public String getCourseLocation() { - return this.instructorName; + return this.courseLocation; } public String getInstructorName() { - return this.courseLocation; + return this.instructorName; } @@ -55,9 +68,10 @@ public String getCourseTimeSlot() { return this.courseTimeSlot; } - + @Override public String toString() { - return "\nInstructor: " + instructorName + "; Location: " + courseLocation + "; Time: " + courseTimeSlot; + return "\nInstructor: " + instructorName + "; Location: " + + courseLocation + "; Time: " + courseTimeSlot; } @@ -75,14 +89,27 @@ public void reassignTime(String newTime) { this.courseTimeSlot = newTime; } - - public void setEnrolledStudentCount(int count) { - this.enrolledStudentCount = count; + /** + * Sets the enrolled student count for the course. + * + * + * @param count an {@code int} representing the new enrolled student count for + * the course. The value must be greater than or equal to 0. + * + * @return {@code true} if the enrolled student count was successfully updated, + * {@code false} if the provided count was negative and the update + * was not performed. + */ + public boolean setEnrolledStudentCount(int count) { + if (count >= 0) { + this.enrolledStudentCount = count; + return true; + } + return false; } - public boolean isCourseFull() { - return enrollmentCapacity > enrolledStudentCount; + return enrolledStudentCount >= enrollmentCapacity; } @Serial diff --git a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Department.java b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Department.java index 4bab0f08..a2ed40b8 100644 --- a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Department.java +++ b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/Department.java @@ -1,8 +1,8 @@ package dev.coms4156.project.individualproject; -import java.io.*; -import java.util.*; - +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; /** * Represents a department within an educational institution. @@ -19,7 +19,7 @@ public class Department implements Serializable { * @param departmentChair The name of the department chair. * @param numberOfMajors The number of majors in the department. */ - public Department(String deptCode, HashMap courses, String departmentChair, + public Department(String deptCode, Map courses, String departmentChair, int numberOfMajors) { this.courses = courses; this.departmentChair = departmentChair; @@ -27,13 +27,33 @@ public Department(String deptCode, HashMap courses, String depar this.deptCode = deptCode; } + /** + * Sets the number of majors in the department. + * + * + * @param numberOfMajors an {@code int} representing the number of majors + * to set for the department. The value must be + * greater than or equal to 0. + * + * @return {@code true} if the number of majors was successfully updated, + * {@code false} if the provided number was negative and the + * update was not performed. + */ + public boolean setNumberOfMajors(int numberOfMajors) { + if (numberOfMajors >= 0) { + this.numberOfMajors = numberOfMajors; + return true; + } + return false; + } + /** * Gets the number of majors in the department. * * @return The number of majors. */ public int getNumberOfMajors() { - return -this.numberOfMajors; + return this.numberOfMajors; } /** @@ -42,7 +62,7 @@ public int getNumberOfMajors() { * @return The name of the department chair. */ public String getDepartmentChair() { - return "this.departmentChair"; + return this.departmentChair; } /** @@ -50,7 +70,7 @@ public String getDepartmentChair() { * * @return A HashMap containing courses offered by the department. */ - public HashMap getCourseSelection() { + public Map getCourseSelection() { return this.courses; } @@ -64,8 +84,12 @@ public void addPersonToMajor() { /** * Decreases the number of majors in the department by one if it's greater than zero. */ - public void dropPersonFromMajor() { - numberOfMajors--; + public boolean dropPersonFromMajor() { + if (numberOfMajors > 0) { + numberOfMajors--; + return true; + } + return false; } /** @@ -98,20 +122,21 @@ public void createCourse(String courseId, String instructorName, String courseLo * * @return A string representing the department. */ + @Override public String toString() { StringBuilder result = new StringBuilder(); for (Map.Entry entry : courses.entrySet()) { String key = entry.getKey(); Course value = entry.getValue(); - result.append(deptCode).append(" ").append(key).append(": ").append(value.toString()) - .append("\n"); + result.append(deptCode).append(" ").append(key) + .append(": ").append(value.toString()).append("\n"); } - return "result.toString()"; + return result.toString(); } @Serial private static final long serialVersionUID = 234567L; - private HashMap courses; + private Map courses; private String departmentChair; private String deptCode; private int numberOfMajors; diff --git a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/IndividualProjectApplication.java b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/IndividualProjectApplication.java index 80860423..2a5f0964 100644 --- a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/IndividualProjectApplication.java +++ b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/IndividualProjectApplication.java @@ -1,302 +1,311 @@ package dev.coms4156.project.individualproject; import jakarta.annotation.PreDestroy; -import java.util.*; -import org.springframework.boot.*; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + /** * Class contains all the startup logic for the application. - * - * DO NOT MODIFY ANYTHING BELOW THIS POINT WITH REGARD TO FUNCTIONALITY + * + *

DO NOT MODIFY ANYTHING BELOW THIS POINT WITH REGARD TO FUNCTIONALITY * YOU MAY MAKE STYLE/REFACTOR MODIFICATIONS AS NEEDED */ @SpringBootApplication public class IndividualProjectApplication implements CommandLineRunner { - /** - * The main launcher for the service all it does - * is make a call to the overridden run method. - * - * @param args A {@code String[]} of any potential - * runtime arguments - */ - public static void main(String[] args) { - SpringApplication.run(IndividualProjectApplication.class, args); - } + private static final Logger logger = Logger.getLogger( + IndividualProjectApplication.class.getName() + ); + + /** + * The main launcher for the service all it does + * is make a call to the overridden run method. + * + * @param args A {@code String[]} of any potential + * runtime arguments + */ + public static void main(String[] args) { + SpringApplication.run(IndividualProjectApplication.class, args); + } - /** - * This contains all the setup logic, it will mainly be focused - * on loading up and creating an instance of the database based - * off a saved file or will create a fresh database if the file - * is not present. - * - * @param args A {@code String[]} of any potential runtime args - */ - public void run(String[] args) { - for (String arg : args) { - if (arg.equals("setup")) { - myFileDatabase = new MyFileDatabase(1, "./data.txt"); - resetDataFile(); - System.out.println("System Setup"); - return; - } - } - myFileDatabase = new MyFileDatabase(0, "./data.txt"); - System.out.println("Start up"); - } + /** + * This contains all the setup logic, it will mainly be focused + * on loading up and creating an instance of the database based + * off a saved file or will create a fresh database if the file + * is not present. + * + * @param args A {@code String[]} of any potential runtime args + */ + @Override + public void run(String... args) { + for (String arg : args) { + if ("setup".equals(arg)) { + myFileDatabase = new MyFileDatabase(1, "./data.txt"); + resetDataFile(); + logger.log(Level.INFO, "System Setup"); + return; + } + } + myFileDatabase = new MyFileDatabase(0, "./data.txt"); + logger.log(Level.INFO, "Start up"); + } - /** - * Overrides the database reference, used when testing. - * - * @param testData A {@code MyFileDatabase} object referencing test data. - */ - public static void overrideDatabase(MyFileDatabase testData) { - myFileDatabase = testData; - saveData = false; - } + /** + * Overrides the database reference, used when testing. + * + * @param testData A {@code MyFileDatabase} object referencing test data. + */ + public static void overrideDatabase(MyFileDatabase testData) { + myFileDatabase = testData; + saveData = false; + } - /** - * Allows for data to be reset in event of errors. - */ - public void resetDataFile() { - String[] times = {"11:40-12:55", "4:10-5:25", "10:10-11:25", "2:40-3:55"}; - String[] locations = {"417 IAB", "309 HAV", "301 URIS"}; + /** + * Allows for data to be reset in event of errors. + */ + public void resetDataFile() { + String[] times = {"11:40-12:55", "4:10-5:25", "10:10-11:25", "2:40-3:55"}; + String[] locations = {"417 IAB", "309 HAV", "301 URIS"}; - //data for coms dept - Course coms1004 = new Course("Adam Cannon", locations[0], times[0], 400); - coms1004.setEnrolledStudentCount(249); - Course coms3134 = new Course("Brian Borowski", locations[2], times[1], 250); - coms3134.setEnrolledStudentCount(242); - Course coms3157 = new Course("Jae Lee", locations[0], times[1], 400); - coms3157.setEnrolledStudentCount(311); - Course coms3203 = new Course("Ansaf Salleb-Aouissi", locations[2], times[2], 250); - coms3203.setEnrolledStudentCount(215); - Course coms3261 = new Course("Josh Alman", locations[0], times[3], 150); - coms3261.setEnrolledStudentCount(140); - Course coms3251 = new Course("Tony Dear", "402 CHANDLER", "1:10-3:40", 125); - coms3251.setEnrolledStudentCount(99); - Course coms3827 = new Course("Daniel Rubenstein", "207 Math", times[2], 300); - coms3827.setEnrolledStudentCount(283); - Course coms4156 = new Course("Gail Kaiser", "501 NWC", times[2], 120); - coms4156.setEnrolledStudentCount(109); - HashMap courses = new HashMap<>(); - courses.put("1004", coms1004); - courses.put("3134", coms3134); - courses.put("3157", coms3157); - courses.put("3203", coms3203); - courses.put("3261", coms3261); - courses.put("3251", coms3251); - courses.put("3827", coms3827); - courses.put("4156", coms4156); - Department compSci = new Department("COMS", courses, "Luca Carloni", 2700); - HashMap mapping = new HashMap<>(); - mapping.put("COMS", compSci); + //data for coms dept + Course coms1004 = new Course("Adam Cannon", locations[0], times[0], 400); + coms1004.setEnrolledStudentCount(249); + Course coms3134 = new Course("Brian Borowski", locations[2], times[1], 250); + coms3134.setEnrolledStudentCount(242); + Course coms3157 = new Course("Jae Lee", locations[0], times[1], 400); + coms3157.setEnrolledStudentCount(311); + Course coms3203 = new Course("Ansaf Salleb-Aouissi", locations[2], times[2], 250); + coms3203.setEnrolledStudentCount(215); + Course coms3261 = new Course("Josh Alman", locations[0], times[3], 150); + coms3261.setEnrolledStudentCount(140); + Course coms3251 = new Course("Tony Dear", "402 CHANDLER", "1:10-3:40", 125); + coms3251.setEnrolledStudentCount(99); + Course coms3827 = new Course("Daniel Rubenstein", "207 Math", times[2], 300); + coms3827.setEnrolledStudentCount(283); + Course coms4156 = new Course("Gail Kaiser", "501 NWC", times[2], 120); + coms4156.setEnrolledStudentCount(109); + HashMap courses = new HashMap<>(); + courses.put("1004", coms1004); + courses.put("3134", coms3134); + courses.put("3157", coms3157); + courses.put("3203", coms3203); + courses.put("3261", coms3261); + courses.put("3251", coms3251); + courses.put("3827", coms3827); + courses.put("4156", coms4156); + Department compSci = new Department("COMS", courses, "Luca Carloni", 2700); + HashMap mapping = new HashMap<>(); + mapping.put("COMS", compSci); - //data for econ dept - Course econ1105 = new Course("Waseem Noor", locations[1], times[3], 210); - econ1105.setEnrolledStudentCount(187); - Course econ2257 = new Course("Tamrat Gashaw", "428 PUP", times[2], 125); - econ2257.setEnrolledStudentCount(63); - Course econ3211 = new Course("Murat Yilmaz", "310 FAY", times[1], 96); - econ3211.setEnrolledStudentCount(81); - Course econ3213 = new Course("Miles Leahey", "702 HAM", times[1], 86); - econ3213.setEnrolledStudentCount(77); - Course econ3412 = new Course("Thomas Piskula", "702 HAM", times[0], 86); - econ3412.setEnrolledStudentCount(81); - Course econ4415 = new Course("Evan D Sadler", locations[1], times[2], 110); - econ4415.setEnrolledStudentCount(63); - Course econ4710 = new Course("Matthieu Gomez", "517 HAM", "8:40-9:55", 86); - econ4710.setEnrolledStudentCount(37); - Course econ4840 = new Course("Mark Dean", "142 URIS", times[3], 108); - econ4840.setEnrolledStudentCount(67); + //data for econ dept + Course econ1105 = new Course("Waseem Noor", locations[1], times[3], 210); + econ1105.setEnrolledStudentCount(187); + Course econ2257 = new Course("Tamrat Gashaw", "428 PUP", times[2], 125); + econ2257.setEnrolledStudentCount(63); + Course econ3211 = new Course("Murat Yilmaz", "310 FAY", times[1], 96); + econ3211.setEnrolledStudentCount(81); + Course econ3213 = new Course("Miles Leahey", "702 HAM", times[1], 86); + econ3213.setEnrolledStudentCount(77); + Course econ3412 = new Course("Thomas Piskula", "702 HAM", times[0], 86); + econ3412.setEnrolledStudentCount(81); + Course econ4415 = new Course("Evan D Sadler", locations[1], times[2], 110); + econ4415.setEnrolledStudentCount(63); + Course econ4710 = new Course("Matthieu Gomez", "517 HAM", "8:40-9:55", 86); + econ4710.setEnrolledStudentCount(37); + Course econ4840 = new Course("Mark Dean", "142 URIS", times[3], 108); + econ4840.setEnrolledStudentCount(67); - courses = new HashMap<>(); - courses.put("1105", econ1105); - courses.put("2257", econ2257); - courses.put("3211", econ3211); - courses.put("3213", econ3213); - courses.put("3412", econ3412); - courses.put("4415", econ4415); - courses.put("4710", econ4710); - courses.put("4840", econ4840); + courses = new HashMap<>(); + courses.put("1105", econ1105); + courses.put("2257", econ2257); + courses.put("3211", econ3211); + courses.put("3213", econ3213); + courses.put("3412", econ3412); + courses.put("4415", econ4415); + courses.put("4710", econ4710); + courses.put("4840", econ4840); - Department econ = new Department("ECON", courses, "Michael Woodford", 2345); - mapping.put("ECON", econ); + Department econ = new Department("ECON", courses, "Michael Woodford", 2345); + mapping.put("ECON", econ); - //data for ieor dept - Course ieor2500 = new Course("Uday Menon", "627 MUDD", times[0], 50); - ieor2500.setEnrolledStudentCount(52); - Course ieor3404 = new Course("Christopher J Dolan", "303 MUDD", times[2], 73); - ieor3404.setEnrolledStudentCount(80); - Course ieor3658 = new Course("Daniel Lacker", "310 FAY", times[2], 96); - ieor3658.setEnrolledStudentCount(87); - Course ieor4102 = new Course("Antonius B Dieker", "209 HAM", times[2], 110); - ieor4102.setEnrolledStudentCount(92); - Course ieor4106 = new Course("Kaizheng Wang", "501 NWC", times[2], 150); - ieor4106.setEnrolledStudentCount(161); - Course ieor4405 = new Course("Yuri Faenza", "517 HAV", times[0], 80); - ieor4405.setEnrolledStudentCount(19); - Course ieor4511 = new Course("Michael Robbins", "633 MUDD", "9:00-11:30", 150); - ieor4511.setEnrolledStudentCount(50); - Course ieor4540 = new Course("Krzysztof M Choromanski", "633 MUDD", "7:10-9:40", 60); - ieor4540.setEnrolledStudentCount(33); + //data for ieor dept + Course ieor2500 = new Course("Uday Menon", "627 MUDD", times[0], 50); + ieor2500.setEnrolledStudentCount(52); + Course ieor3404 = new Course("Christopher J Dolan", "303 MUDD", times[2], 73); + ieor3404.setEnrolledStudentCount(80); + Course ieor3658 = new Course("Daniel Lacker", "310 FAY", times[2], 96); + ieor3658.setEnrolledStudentCount(87); + Course ieor4102 = new Course("Antonius B Dieker", "209 HAM", times[2], 110); + ieor4102.setEnrolledStudentCount(92); + Course ieor4106 = new Course("Kaizheng Wang", "501 NWC", times[2], 150); + ieor4106.setEnrolledStudentCount(161); + Course ieor4405 = new Course("Yuri Faenza", "517 HAV", times[0], 80); + ieor4405.setEnrolledStudentCount(19); + Course ieor4511 = new Course("Michael Robbins", "633 MUDD", "9:00-11:30", 150); + ieor4511.setEnrolledStudentCount(50); + Course ieor4540 = new Course("Krzysztof M Choromanski", "633 MUDD", "7:10-9:40", 60); + ieor4540.setEnrolledStudentCount(33); - courses = new HashMap<>(); - courses.put("2500", ieor2500); - courses.put("3404", ieor3404); - courses.put("3658", ieor3658); - courses.put("4102", ieor4102); - courses.put("4106", ieor4106); - courses.put("4405", ieor4405); - courses.put("4511", ieor4511); - courses.put("4540", ieor4540); + courses = new HashMap<>(); + courses.put("2500", ieor2500); + courses.put("3404", ieor3404); + courses.put("3658", ieor3658); + courses.put("4102", ieor4102); + courses.put("4106", ieor4106); + courses.put("4405", ieor4405); + courses.put("4511", ieor4511); + courses.put("4540", ieor4540); - Department ieor = new Department("IEOR", courses, "Jay Sethuraman", 67); - mapping.put("IEOR", ieor); + Department ieor = new Department("IEOR", courses, "Jay Sethuraman", 67); + mapping.put("IEOR", ieor); - //data for chem dept - Course chem1403 = new Course("Ruben M Savizky", locations[1], "6:10-7:25", 120); - chem1403.setEnrolledStudentCount(100); - Course chem1500 = new Course("Joseph C Ulichny", "302 HAV", "6:10-9:50", 46); - chem1500.setEnrolledStudentCount(50); - Course chem2045 = new Course("Luis M Campos", "209 HAV", "1:10-2:25", 50); - chem2045.setEnrolledStudentCount(29); - Course chem2444 = new Course("Christopher Eckdahl", locations[1], times[0], 150); - chem2444.setEnrolledStudentCount(150); - Course chem2494 = new Course("Talha Siddiqui", "202 HAV", "1:10-5:00", 24); - chem2494.setEnrolledStudentCount(18); - Course chem3080 = new Course("Milan Delor", "209 HAV", times[2], 60); - chem3080.setEnrolledStudentCount(18); - Course chem4071 = new Course("Jonathan S Owen", "320 HAV", "8:40-9:55", 42); - chem4071.setEnrolledStudentCount(29); - Course chem4102 = new Course("Dalibor Sames", "320 HAV", times[2], 28); - chem4102.setEnrolledStudentCount(27); + //data for chem dept + Course chem1403 = new Course("Ruben M Savizky", locations[1], "6:10-7:25", 120); + chem1403.setEnrolledStudentCount(100); + Course chem1500 = new Course("Joseph C Ulichny", "302 HAV", "6:10-9:50", 46); + chem1500.setEnrolledStudentCount(50); + Course chem2045 = new Course("Luis M Campos", "209 HAV", "1:10-2:25", 50); + chem2045.setEnrolledStudentCount(29); + Course chem2444 = new Course("Christopher Eckdahl", locations[1], times[0], 150); + chem2444.setEnrolledStudentCount(150); + Course chem2494 = new Course("Talha Siddiqui", "202 HAV", "1:10-5:00", 24); + chem2494.setEnrolledStudentCount(18); + Course chem3080 = new Course("Milan Delor", "209 HAV", times[2], 60); + chem3080.setEnrolledStudentCount(18); + Course chem4071 = new Course("Jonathan S Owen", "320 HAV", "8:40-9:55", 42); + chem4071.setEnrolledStudentCount(29); + Course chem4102 = new Course("Dalibor Sames", "320 HAV", times[2], 28); + chem4102.setEnrolledStudentCount(27); - courses = new HashMap<>(); - courses.put("1403", chem1403); - courses.put("1500", chem1500); - courses.put("2045", chem2045); - courses.put("2444", chem2444); - courses.put("2494", chem2494); - courses.put("3080", chem3080); - courses.put("4071", chem4071); - courses.put("4102", chem4102); + courses = new HashMap<>(); + courses.put("1403", chem1403); + courses.put("1500", chem1500); + courses.put("2045", chem2045); + courses.put("2444", chem2444); + courses.put("2494", chem2494); + courses.put("3080", chem3080); + courses.put("4071", chem4071); + courses.put("4102", chem4102); - Department chem = new Department("CHEM", courses, "Laura J. Kaufman", 250); - mapping.put("CHEM", chem); + Department chem = new Department("CHEM", courses, "Laura J. Kaufman", 250); + mapping.put("CHEM", chem); - //data for phys dept - Course phys1001 = new Course("Szabolcs Marka", "301 PUP", times[3], 150); - phys1001.setEnrolledStudentCount(131); - Course phys1201 = new Course("Eric Raymer", "428 PUP", times[3], 145); - phys1201.setEnrolledStudentCount(130); - Course phys1602 = new Course("Kerstin M Perez", "428 PUP", times[2], 140); - phys1602.setEnrolledStudentCount(77); - Course phys2802 = new Course("Yury Levin", "329 PUP", "10:10-12:00", 60); - phys2802.setEnrolledStudentCount(23); - Course phys3008 = new Course("William A Zajc", "329 PUP", times[2], 75); - phys3008.setEnrolledStudentCount(60); - Course phys4003 = new Course("Frederik Denef", "214 PUP", times[1], 50); - phys4003.setEnrolledStudentCount(19); - Course phys4018 = new Course("James W McIver", "307 PUP", times[3], 30); - phys4018.setEnrolledStudentCount(18); - Course phys4040 = new Course("James C Hill", "214 PUP", times[1], 50); - phys4040.setEnrolledStudentCount(31); + //data for phys dept + Course phys1001 = new Course("Szabolcs Marka", "301 PUP", times[3], 150); + phys1001.setEnrolledStudentCount(131); + Course phys1201 = new Course("Eric Raymer", "428 PUP", times[3], 145); + phys1201.setEnrolledStudentCount(130); + Course phys1602 = new Course("Kerstin M Perez", "428 PUP", times[2], 140); + phys1602.setEnrolledStudentCount(77); + Course phys2802 = new Course("Yury Levin", "329 PUP", "10:10-12:00", 60); + phys2802.setEnrolledStudentCount(23); + Course phys3008 = new Course("William A Zajc", "329 PUP", times[2], 75); + phys3008.setEnrolledStudentCount(60); + Course phys4003 = new Course("Frederik Denef", "214 PUP", times[1], 50); + phys4003.setEnrolledStudentCount(19); + Course phys4018 = new Course("James W McIver", "307 PUP", times[3], 30); + phys4018.setEnrolledStudentCount(18); + Course phys4040 = new Course("James C Hill", "214 PUP", times[1], 50); + phys4040.setEnrolledStudentCount(31); - courses = new HashMap<>(); - courses.put("2802", phys2802); - courses.put("3008", phys3008); - courses.put("4003", phys4003); - courses.put("4018", phys4018); - courses.put("4040", phys4040); - courses.put("1602", phys1602); - courses.put("1001", phys1001); - courses.put("1201", phys1201); + courses = new HashMap<>(); + courses.put("2802", phys2802); + courses.put("3008", phys3008); + courses.put("4003", phys4003); + courses.put("4018", phys4018); + courses.put("4040", phys4040); + courses.put("1602", phys1602); + courses.put("1001", phys1001); + courses.put("1201", phys1201); - Department phys = new Department("PHYS", courses, "Dmitri N. Basov", 43); - mapping.put("PHYS", phys); + Department phys = new Department("PHYS", courses, "Dmitri N. Basov", 43); + mapping.put("PHYS", phys); - //data for elen dept - Course elen1201 = new Course("David G Vallancourt", "301 PUP", times[1], 120); - elen1201.setEnrolledStudentCount(108); - Course elen3082 = new Course("Kenneth Shepard", "1205 MUDD", "4:10-6:40", 32); - elen3082.setEnrolledStudentCount(30); - Course elen3331 = new Course("David G Vallancourt", "203 MATH", times[0], 80); - elen3331.setEnrolledStudentCount(54); - Course elen3401 = new Course("Keren Bergman", "829 MUDD", times[3], 40); - elen3401.setEnrolledStudentCount(25); - Course elen3701 = new Course("Irving Kalet", "333 URIS", times[3], 50); - elen3701.setEnrolledStudentCount(24); - Course elen4510 = new Course("Mohamed Kamaludeen", "903 SSW", "7:00-9:30", 30); - elen4510.setEnrolledStudentCount(22); - Course elen4702 = new Course("Alexei Ashikhmin", "332 URIS", "7:00-9:30", 50); - elen4702.setEnrolledStudentCount(5); - Course elen4830 = new Course("Christine P Hendon", "633 MUDD", "10:10-12:40", 60); - elen4830.setEnrolledStudentCount(22); + //data for elen dept + Course elen1201 = new Course("David G Vallancourt", "301 PUP", times[1], 120); + elen1201.setEnrolledStudentCount(108); + Course elen3082 = new Course("Kenneth Shepard", "1205 MUDD", "4:10-6:40", 32); + elen3082.setEnrolledStudentCount(30); + Course elen3331 = new Course("David G Vallancourt", "203 MATH", times[0], 80); + elen3331.setEnrolledStudentCount(54); + Course elen3401 = new Course("Keren Bergman", "829 MUDD", times[3], 40); + elen3401.setEnrolledStudentCount(25); + Course elen3701 = new Course("Irving Kalet", "333 URIS", times[3], 50); + elen3701.setEnrolledStudentCount(24); + Course elen4510 = new Course("Mohamed Kamaludeen", "903 SSW", "7:00-9:30", 30); + elen4510.setEnrolledStudentCount(22); + Course elen4702 = new Course("Alexei Ashikhmin", "332 URIS", "7:00-9:30", 50); + elen4702.setEnrolledStudentCount(5); + Course elen4830 = new Course("Christine P Hendon", "633 MUDD", "10:10-12:40", 60); + elen4830.setEnrolledStudentCount(22); - courses = new HashMap<>(); - courses.put("1201", elen1201); - courses.put("3082", elen3082); - courses.put("3331", elen3331); - courses.put("3401", elen3401); - courses.put("3701", elen3701); - courses.put("4510", elen4510); - courses.put("4702", elen4702); - courses.put("4830", elen4830); + courses = new HashMap<>(); + courses.put("1201", elen1201); + courses.put("3082", elen3082); + courses.put("3331", elen3331); + courses.put("3401", elen3401); + courses.put("3701", elen3701); + courses.put("4510", elen4510); + courses.put("4702", elen4702); + courses.put("4830", elen4830); - Department elen = new Department("ELEN", courses, "Ioannis Kymissis", 250); - mapping.put("ELEN", elen); + Department elen = new Department("ELEN", courses, "Ioannis Kymissis", 250); + mapping.put("ELEN", elen); - //data for psyc dept - Course psyc1001 = new Course("Patricia G Lindemann", "501 SCH", "1:10-2:25", 200); - psyc1001.setEnrolledStudentCount(191); - Course psyc1610 = new Course("Christopher Baldassano", "200 SCH", times[2], 45); - psyc1610.setEnrolledStudentCount(42); - Course psyc2235 = new Course("Katherine T Fox-Glassman", "501 SCH", times[0], 125); - psyc2235.setEnrolledStudentCount(128); - Course psyc2620 = new Course("Jeffrey M Cohen", "303 URIS", "1:10-3:40", 60); - psyc2620.setEnrolledStudentCount(55); - Course psyc3212 = new Course("Mayron Piccolo", "200 SCH", "2:10-4:00", 15); - psyc3212.setEnrolledStudentCount(15); - Course psyc3445 = new Course("Mariam Aly", "405 SCH", "2:10-4:00", 12); - psyc3445.setEnrolledStudentCount(12); - Course psyc4236 = new Course("Trenton Jerde", "405 SCH", "6:10-8:00", 18); - psyc4236.setEnrolledStudentCount(17); - Course psyc4493 = new Course("Jennifer Blaze", "200 SCH", "2:10-4:00", 15); - psyc4493.setEnrolledStudentCount(9); + //data for psyc dept + Course psyc1001 = new Course("Patricia G Lindemann", "501 SCH", "1:10-2:25", 200); + psyc1001.setEnrolledStudentCount(191); + Course psyc1610 = new Course("Christopher Baldassano", "200 SCH", times[2], 45); + psyc1610.setEnrolledStudentCount(42); + Course psyc2235 = new Course("Katherine T Fox-Glassman", "501 SCH", times[0], 125); + psyc2235.setEnrolledStudentCount(128); + Course psyc2620 = new Course("Jeffrey M Cohen", "303 URIS", "1:10-3:40", 60); + psyc2620.setEnrolledStudentCount(55); + Course psyc3212 = new Course("Mayron Piccolo", "200 SCH", "2:10-4:00", 15); + psyc3212.setEnrolledStudentCount(15); + Course psyc3445 = new Course("Mariam Aly", "405 SCH", "2:10-4:00", 12); + psyc3445.setEnrolledStudentCount(12); + Course psyc4236 = new Course("Trenton Jerde", "405 SCH", "6:10-8:00", 18); + psyc4236.setEnrolledStudentCount(17); + Course psyc4493 = new Course("Jennifer Blaze", "200 SCH", "2:10-4:00", 15); + psyc4493.setEnrolledStudentCount(9); - courses = new HashMap<>(); - courses.put("1001", psyc1001); - courses.put("1610", psyc1610); - courses.put("2235", psyc2235); - courses.put("2620", psyc2620); - courses.put("3212", psyc3212); - courses.put("3445", psyc3445); - courses.put("4236", psyc4236); - courses.put("4493", psyc4493); + courses = new HashMap<>(); + courses.put("1001", psyc1001); + courses.put("1610", psyc1610); + courses.put("2235", psyc2235); + courses.put("2620", psyc2620); + courses.put("3212", psyc3212); + courses.put("3445", psyc3445); + courses.put("4236", psyc4236); + courses.put("4493", psyc4493); - Department psyc = new Department("PSYC", courses, "Nim Tottenham", 437); - mapping.put("PSYC", psyc); + Department psyc = new Department("PSYC", courses, "Nim Tottenham", 437); + mapping.put("PSYC", psyc); - myFileDatabase.setMapping(mapping); - } + myFileDatabase.setMapping(mapping); + } - /** - * This contains all the overheading teardown logic, it will - * mainly be focused on saving all the created user data to a - * file, so it will be ready for the next setup. - */ - @PreDestroy - public void onTermination() { - System.out.println("Termination"); - if (saveData) { - myFileDatabase.saveContentsToFile(); - } - } + /** + * This contains all the overheading teardown logic, it will + * mainly be focused on saving all the created user data to a + * file, so it will be ready for the next setup. + */ + @PreDestroy + public void onTermination() { + logger.log(Level.INFO, "Termination"); + if (saveData) { + myFileDatabase.saveContentsToFile(); + } + } - //Database Instance - public static MyFileDatabase myFileDatabase; - private static boolean saveData = true; -} + //Database Instance + public static MyFileDatabase myFileDatabase; + private static boolean saveData = true; +} \ No newline at end of file diff --git a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/MyFileDatabase.java b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/MyFileDatabase.java index 1f61f893..43448cb3 100644 --- a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/MyFileDatabase.java +++ b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/MyFileDatabase.java @@ -1,13 +1,24 @@ package dev.coms4156.project.individualproject; -import java.io.*; -import java.util.*; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * This class represents a file-based database containing department mappings. */ public class MyFileDatabase { + private static final Logger logger = Logger.getLogger( + MyFileDatabase.class.getName() + ); + /** * Constructs a MyFileDatabase object and loads up the data structure with * the contents of the file. @@ -27,7 +38,7 @@ public MyFileDatabase(int flag, String filePath) { * * @param mapping the mapping of department names to Department objects */ - public void setMapping(HashMap mapping) { + public void setMapping(Map mapping) { this.departmentMapping = mapping; } @@ -36,16 +47,19 @@ public void setMapping(HashMap mapping) { * * @return the deserialized department mapping */ - public HashMap deSerializeObjectFromFile() { + public Map deSerializeObjectFromFile() { try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath))) { Object obj = in.readObject(); - if (obj instanceof HashMap) { - return (HashMap) obj; + if (obj instanceof Map) { + return (Map) obj; } else { throw new IllegalArgumentException("Invalid object type in file."); } + } catch (FileNotFoundException e) { + logger.log(Level.WARNING, "File not found: {0}", filePath); + return null; } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "An exception occurred while saving contents to file", e); return null; } } @@ -57,9 +71,9 @@ public HashMap deSerializeObjectFromFile() { public void saveContentsToFile() { try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath))) { out.writeObject(departmentMapping); - System.out.println("Object serialized successfully."); + logger.log(Level.INFO, "Object serialized successfully."); } catch (IOException e) { - e.printStackTrace(); + logger.log(Level.SEVERE, "An exception occurred while saving contents to file", e); } } @@ -68,7 +82,7 @@ public void saveContentsToFile() { * * @return the department mapping */ - public HashMap getDepartmentMapping() { + public Map getDepartmentMapping() { return this.departmentMapping; } @@ -92,5 +106,5 @@ public String toString() { private String filePath; /** The mapping of department names to Department objects. */ - private HashMap departmentMapping; + private Map departmentMapping; } diff --git a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/RouteController.java b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/RouteController.java index 09f504dc..6c4a5dcc 100644 --- a/IndividualProject/src/main/java/dev/coms4156/project/individualproject/RouteController.java +++ b/IndividualProject/src/main/java/dev/coms4156/project/individualproject/RouteController.java @@ -1,8 +1,15 @@ package dev.coms4156.project.individualproject; -import java.util.HashMap; -import org.springframework.http.*; -import org.springframework.web.bind.annotation.*; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; /** * This class contains all the API routes for the system. @@ -10,6 +17,10 @@ @RestController public class RouteController { + private static final Logger logger = Logger.getLogger( + RouteController.class.getName() + ); + /** * Redirects to the homepage. * @@ -34,14 +45,14 @@ public String index() { @GetMapping(value = "/retrieveDept", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity retrieveDepartment(@RequestParam(value = "deptCode") String deptCode) { try { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); if (!departmentMapping.containsKey(deptCode.toUpperCase())) { - return new ResponseEntity<>("Department Not Found", HttpStatus.OK); + return new ResponseEntity<>("Department Not Found", HttpStatus.NOT_FOUND); } else { return new ResponseEntity<>(departmentMapping.get(deptCode.toUpperCase()).toString(), - HttpStatus.NOT_FOUND); + HttpStatus.OK); } } catch (Exception e) { @@ -64,20 +75,21 @@ public ResponseEntity retrieveDepartment(@RequestParam(value = "deptCode") St * proper response. */ @GetMapping(value = "/retrieveCourse", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity retrieveCourse(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode) { + public ResponseEntity retrieveCourse(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { try { boolean doesDepartmentExists = retrieveDepartment(deptCode).getStatusCode() == HttpStatus.OK; if (doesDepartmentExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); if (!coursesMapping.containsKey(Integer.toString(courseCode))) { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); } else { return new ResponseEntity<>(coursesMapping.get(Integer.toString(courseCode)).toString(), - HttpStatus.FORBIDDEN); + HttpStatus.OK); } } @@ -87,6 +99,45 @@ public ResponseEntity retrieveCourse(@RequestParam(value = "deptCode") String } } + /** + * Displays the details of all courses that have the desired courseCode (across all departments) + * to the user or displays the proper error message in response to the request. + * + * @param courseCode A {@code int} representing the course the user wishes + * to retrieve. + * + * @return A {@code ResponseEntity} object containing the HTTP status code and an + * accompanying message (e.g., the courses as a String or indicating that + * no courses were found). + */ + @GetMapping(value = "/retrieveCourses", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity retrieveCourses(@RequestParam(value = "courseCode") int courseCode) { + try { + StringBuilder result = new StringBuilder(); + + Map departmentMapping; + departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); + + for (Map.Entry entry : departmentMapping.entrySet()) { + ResponseEntity response = retrieveCourse(entry.getKey(), courseCode); + + if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { + result.append(entry.getKey()).append(" ").append(courseCode) + .append(response.getBody()).append("\n"); + } + } + + if (result.length() > 0) { + return new ResponseEntity<>(result.toString(), HttpStatus.OK); + } else { + return new ResponseEntity<>("No Courses Found", HttpStatus.NOT_FOUND); + } + + } catch (Exception e) { + return handleException(e); + } + } + /** * Displays whether the course has at minimum reached its enrollmentCapacity. * @@ -101,15 +152,16 @@ public ResponseEntity retrieveCourse(@RequestParam(value = "deptCode") String * response. */ @GetMapping(value = "/isCourseFull", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity isCourseFull(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode) { + public ResponseEntity isCourseFull(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); @@ -138,12 +190,12 @@ public ResponseEntity getMajorCtFromDept(@RequestParam(value = "deptCode") St try { boolean doesDepartmentExists = retrieveDepartment(deptCode).getStatusCode() == HttpStatus.OK; if (doesDepartmentExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); return new ResponseEntity<>("There are: " + -departmentMapping.get(deptCode) .getNumberOfMajors() + " majors in the department", HttpStatus.OK); } - return new ResponseEntity<>("Department Not Found", HttpStatus.FORBIDDEN); + return new ResponseEntity<>("Department Not Found", HttpStatus.NOT_FOUND); } catch (Exception e) { return handleException(e); } @@ -164,7 +216,7 @@ public ResponseEntity identifyDeptChair(@RequestParam(value = "deptCode") Str try { boolean doesDepartmentExists = retrieveDepartment(deptCode).getStatusCode() == HttpStatus.OK; if (doesDepartmentExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); return new ResponseEntity<>(departmentMapping.get(deptCode).getDepartmentChair() + " is " + "the department chair.", HttpStatus.OK); @@ -189,15 +241,16 @@ public ResponseEntity identifyDeptChair(@RequestParam(value = "deptCode") Str * proper response. */ @GetMapping(value = "/findCourseLocation", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity findCourseLocation(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode) { + public ResponseEntity findCourseLocation(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); @@ -226,15 +279,16 @@ public ResponseEntity findCourseLocation(@RequestParam(value = "deptCode") St * response. */ @GetMapping(value = "/findCourseInstructor", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity findCourseInstructor(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode) { + public ResponseEntity findCourseInstructor(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); @@ -263,19 +317,20 @@ public ResponseEntity findCourseInstructor(@RequestParam(value = "deptCode") * indicating the proper response. */ @GetMapping(value = "/findCourseTime", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity findCourseTime(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode) { + public ResponseEntity findCourseTime(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); - return new ResponseEntity<>("The course meets at: " + "some time ", + return new ResponseEntity<>("The course meets at: " + requestedCourse.getCourseTimeSlot(), HttpStatus.OK); } else { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); @@ -299,7 +354,7 @@ public ResponseEntity addMajorToDept(@RequestParam(value = "deptCode") String try { boolean doesDepartmentExists = retrieveDepartment(deptCode).getStatusCode() == HttpStatus.OK; if (doesDepartmentExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); Department specifiedDept = departmentMapping.get(deptCode); @@ -326,12 +381,17 @@ public ResponseEntity removeMajorFromDept(@RequestParam(value = "deptCode") S try { boolean doesDepartmentExists = retrieveDepartment(deptCode).getStatusCode() == HttpStatus.OK; if (doesDepartmentExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); Department specifiedDept = departmentMapping.get(deptCode); - specifiedDept.dropPersonFromMajor(); - return new ResponseEntity<>("Attribute was updated or is at minimum", HttpStatus.OK); + boolean successfullyDropped = specifiedDept.dropPersonFromMajor(); + if (successfullyDropped) { + return new ResponseEntity<>("Person successfully dropped from major.", HttpStatus.OK); + } else { + return new ResponseEntity<>("Drop unsuccessful. No person to drop.", + HttpStatus.BAD_REQUEST); + } } return new ResponseEntity<>("Department Not Found", HttpStatus.NOT_FOUND); } catch (Exception e) { @@ -339,6 +399,49 @@ public ResponseEntity removeMajorFromDept(@RequestParam(value = "deptCode") S } } + /** + * Attempts to enroll a student in a course if possible. + * + * @param deptCode A {@code String} representing the department. + * + * @param courseCode A {@code int} representing the course within the department. + * + * @return A {@code ResponseEntity} object containing an HTTP status code + * response with a message indicating if the student has been + * enrolled or not or if the course is not found. + */ + @PatchMapping(value = "/enrollStudentInCourse", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity enrollStudentInCourse(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { + try { + boolean doesCourseExists; + doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; + + if (doesCourseExists) { + Map departmentMapping; + departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); + Map courseMapping; + courseMapping = departmentMapping.get(deptCode).getCourseSelection(); + + Course requestedCourse = courseMapping.get(Integer.toString(courseCode)); + boolean isStudentEnrolled = requestedCourse.enrollStudent(); + + if (isStudentEnrolled) { + return new ResponseEntity<>("Student has been enrolled.", HttpStatus.OK); + } else { + return new ResponseEntity<>("Course is full. Student has not been enrolled.", + HttpStatus.BAD_REQUEST); + } + + } else { + return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); + } + + } catch (Exception e) { + return handleException(e); + } + } + /** * Attempts to drop a student from the specified course. * @@ -351,15 +454,16 @@ public ResponseEntity removeMajorFromDept(@RequestParam(value = "deptCode") S * code in tune with what has happened. */ @PatchMapping(value = "/dropStudentFromCourse", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity dropStudent(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode) { + public ResponseEntity dropStudent(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); @@ -368,7 +472,7 @@ public ResponseEntity dropStudent(@RequestParam(value = "deptCode") String de if (isStudentDropped) { return new ResponseEntity<>("Student has been dropped.", HttpStatus.OK); } else { - return new ResponseEntity<>("Student has not been dropped.", HttpStatus.BAD_REQUEST); + return new ResponseEntity<>("Drop unsuccessful. No person to drop.", HttpStatus.BAD_REQUEST); } } else { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); @@ -378,22 +482,37 @@ public ResponseEntity dropStudent(@RequestParam(value = "deptCode") String de } } - + /** + * Sets the enrollment count for a specific course in a department. + * + * @param deptCode the department code for the course + * @param courseCode the course code for the specific course + * @param count the enrollment count to set + * @return a ResponseEntity containing the result of the operation + */ @PatchMapping(value = "/setEnrollmentCount", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity setEnrollmentCount(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode, @RequestParam(value = "count") int count) { + public ResponseEntity setEnrollmentCount(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode, + @RequestParam(value = "count") int count) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); - requestedCourse.setEnrolledStudentCount(count); - return new ResponseEntity<>("Attributed was updated successfully.", HttpStatus.OK); + boolean setEnrolled = requestedCourse.setEnrolledStudentCount(count); + + if (setEnrolled) { + return new ResponseEntity<>("New enrollment count has been set.", HttpStatus.OK); + } else { + return new ResponseEntity<>("Enrollment count requested is negative.", + HttpStatus.BAD_REQUEST); + } } else { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); } @@ -402,6 +521,41 @@ public ResponseEntity setEnrollmentCount(@RequestParam(value = "deptCode") St } } + /** + * Sets the number of majors count for a specific department. + * + * @param deptCode the department code for the course + * @param count the enrollment count to set + * @return a ResponseEntity containing the result of the operation + */ + @PatchMapping(value = "/setNumberOfMajors", produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity setNumberOfMajors(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "count") int count) { + try { + boolean doesDeptExist; + doesDeptExist = retrieveDepartment(deptCode).getStatusCode() == HttpStatus.OK; + + if (doesDeptExist) { + Map departmentMapping; + departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); + Department dept = departmentMapping.get(deptCode); + + boolean successfullySet = dept.setNumberOfMajors(count); + + if (successfullySet) { + return new ResponseEntity<>("New number of majors has been set.", HttpStatus.OK); + } else { + return new ResponseEntity<>("Number of majors is negative.", + HttpStatus.BAD_REQUEST); + } + } else { + return new ResponseEntity<>("Dept Not Found", HttpStatus.NOT_FOUND); + } + } catch (Exception e) { + return handleException(e); + } + } + /** * Endpoint for changing the time of a course. * This method handles PATCH requests to change the time of a course identified by @@ -415,20 +569,22 @@ public ResponseEntity setEnrollmentCount(@RequestParam(value = "deptCode") St * successful, or an error message if the course is not found */ @PatchMapping(value = "/changeCourseTime", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity changeCourseTime(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode, @RequestParam(value = "time") String time) { + public ResponseEntity changeCourseTime(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode, + @RequestParam(value = "time") String time) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); requestedCourse.reassignTime(time); - return new ResponseEntity<>("Attributed was updated successfully.", HttpStatus.OK); + return new ResponseEntity<>("Attribute was updated successfully.", HttpStatus.OK); } else { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); } @@ -451,20 +607,22 @@ public ResponseEntity changeCourseTime(@RequestParam(value = "deptCode") Stri * successful, or an error message if the course is not found */ @PatchMapping(value = "/changeCourseTeacher", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity changeCourseTeacher(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode, @RequestParam(value = "teacher") String teacher) { + public ResponseEntity changeCourseTeacher(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode, + @RequestParam(value = "teacher") String teacher) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); requestedCourse.reassignInstructor(teacher); - return new ResponseEntity<>("Attributed was updated successfully.", HttpStatus.OK); + return new ResponseEntity<>("Attribute was updated successfully.", HttpStatus.OK); } else { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); } @@ -473,22 +631,32 @@ public ResponseEntity changeCourseTeacher(@RequestParam(value = "deptCode") S } } - + /** + * Changes the location of a specified course within a department. + * + * @param deptCode the department code of the course + * @param courseCode the course code of the course + * @param location the new location to assign to the course + * @return a ResponseEntity with a success message if the course location was updated, + * or an error message if the course was not found or an exception occurred + */ @PatchMapping(value = "/changeCourseLocation", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity changeCourseLocation(@RequestParam(value = "deptCode") String deptCode, @RequestParam(value = "courseCode") int courseCode, @RequestParam(value = "location") String location) { + public ResponseEntity changeCourseLocation(@RequestParam(value = "deptCode") String deptCode, + @RequestParam(value = "courseCode") int courseCode, + @RequestParam(value = "location") String location) { try { boolean doesCourseExists; doesCourseExists = retrieveCourse(deptCode, courseCode).getStatusCode() == HttpStatus.OK; if (doesCourseExists) { - HashMap departmentMapping; + Map departmentMapping; departmentMapping = IndividualProjectApplication.myFileDatabase.getDepartmentMapping(); - HashMap coursesMapping; + Map coursesMapping; coursesMapping = departmentMapping.get(deptCode).getCourseSelection(); Course requestedCourse = coursesMapping.get(Integer.toString(courseCode)); requestedCourse.reassignLocation(location); - return new ResponseEntity<>("Attributed was updated successfully.", HttpStatus.OK); + return new ResponseEntity<>("Attribute was updated successfully.", HttpStatus.OK); } else { return new ResponseEntity<>("Course Not Found", HttpStatus.NOT_FOUND); } @@ -498,9 +666,11 @@ public ResponseEntity changeCourseLocation(@RequestParam(value = "deptCode") } private ResponseEntity handleException(Exception e) { - System.out.println(e.toString()); - return new ResponseEntity<>("An Error has occurred", HttpStatus.OK); + if (logger.isLoggable(Level.SEVERE)) { + logger.log(Level.SEVERE, "An exception occurred: {0}", e.toString()); + } + logger.log(Level.SEVERE, "Stack trace: ", e); + return new ResponseEntity<>("An Error has occurred", HttpStatus.INTERNAL_SERVER_ERROR); } - } \ No newline at end of file diff --git a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CourseUnitTests.java b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CourseUnitTests.java index 4edd00f9..3395f0ca 100644 --- a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CourseUnitTests.java +++ b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CourseUnitTests.java @@ -1,13 +1,25 @@ package dev.coms4156.project.individualproject; -import org.junit.jupiter.api.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; -import static org.junit.jupiter.api.Assertions.*; - +/** + * Unit tests for the Course class. + * This class contains tests to verify the behavior and functionality of the Course class. + * It uses Spring's testing framework to set up the environment and run the tests. + */ @SpringBootTest @ContextConfiguration +@TestMethodOrder(OrderAnnotation.class) public class CourseUnitTests { @BeforeAll @@ -15,13 +27,79 @@ public static void setupCourseForTesting() { testCourse = new Course("Griffin Newbold", "417 IAB", "11:40-12:55", 250); } - @Test + @Order(1) public void toStringTest() { String expectedResult = "\nInstructor: Griffin Newbold; Location: 417 IAB; Time: 11:40-12:55"; assertEquals(expectedResult, testCourse.toString()); } + @Test + @Order(2) + public void enrollDropWhenFullTest() { + testCourse.setEnrolledStudentCount(250); + + assertTrue(testCourse.isCourseFull()); + assertFalse(testCourse.enrollStudent()); + assertTrue(testCourse.dropStudent()); + assertTrue(testCourse.enrollStudent()); + } + + @Test + @Order(3) + public void enrollDropWhenEmptyTest() { + testCourse.setEnrolledStudentCount(0); + + assertFalse(testCourse.dropStudent()); + assertTrue(testCourse.enrollStudent()); + } + + @Test + public void reassignInstructorTest() { + String expectedResult = "Griffin Newbold"; + assertEquals(expectedResult, testCourse.getInstructorName()); + + testCourse.reassignInstructor("Spongebob Squarepants"); + expectedResult = "Spongebob Squarepants"; + assertEquals(expectedResult, testCourse.getInstructorName()); + } + + @Test + public void reassignLocationTest() { + String expectedResult = "417 IAB"; + assertEquals(expectedResult, testCourse.getCourseLocation()); + + testCourse.reassignLocation("Bikini Bottom"); + expectedResult = "Bikini Bottom"; + assertEquals(expectedResult, testCourse.getCourseLocation()); + } + + @Test + public void reassignTimeTest() { + String expectedResult = "11:40-12:55"; + assertEquals(expectedResult, testCourse.getCourseTimeSlot()); + + testCourse.reassignTime("10:00-12:00"); + expectedResult = "10:00-12:00"; + assertEquals(expectedResult, testCourse.getCourseTimeSlot()); + } + + @Test + @Order(4) + public void enrollWhenOneSeatLeftTest() { + testCourse.setEnrolledStudentCount(249); + assertTrue(testCourse.enrollStudent()); + assertTrue(testCourse.isCourseFull()); + } + + @Test + @Order(5) + public void dropWhenOneStudentLeftTest() { + testCourse.setEnrolledStudentCount(1); + assertTrue(testCourse.dropStudent()); + assertFalse(testCourse.isCourseFull()); + } + /** The test course instance used for testing. */ public static Course testCourse; } diff --git a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CustomLoggerHandler.java b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CustomLoggerHandler.java new file mode 100644 index 00000000..d0eb0825 --- /dev/null +++ b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/CustomLoggerHandler.java @@ -0,0 +1,41 @@ +package dev.coms4156.project.individualproject; + +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.StreamHandler; + +/** + * Custom logger handler that captures the most recent log record. + * Helpful for unit testing. + * + *

This handler overrides the {@code publish()} method to store the latest + * {@code LogRecord} instance, allowing tests or other classes to retrieve + * the most recent log message. It extends the {@code StreamHandler} class. + * + *

The {@code getLatestLogRecord()} method provides access to the most + * recent log record. + */ +public class CustomLoggerHandler extends StreamHandler { + public Logger logger; + private LogRecord latestLogRecord; + + @Override + public void publish(LogRecord record) { + latestLogRecord = record; + super.publish(record); + } + + @Override + public void flush() { + super.flush(); + } + + @Override + public void close() throws SecurityException { + super.close(); + } + + public LogRecord getLatestLogRecord() { + return latestLogRecord; + } +} diff --git a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/DatabaseUnitTests.java b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/DatabaseUnitTests.java new file mode 100644 index 00000000..97e13e47 --- /dev/null +++ b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/DatabaseUnitTests.java @@ -0,0 +1,179 @@ +package dev.coms4156.project.individualproject; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +/** + * Unit tests for the Database class. + * This class contains tests to verify the behavior and functionality of the Database class. + * It uses Spring's testing framework to set up the environment and run the tests. + */ +@SpringBootTest +@ContextConfiguration +@TestMethodOrder(OrderAnnotation.class) +public class DatabaseUnitTests { + + private static final String VALID_FILE_PATH = "./data.txt"; + private static final String NON_EXISTENT_FILE_PATH = "./nonexistent.txt"; + + @Nested + @TestMethodOrder(OrderAnnotation.class) + class FlagZeroTests { + + @BeforeAll + public static void setupDatabaseFlagZeroTest() { + // Initialize with a valid file path + testDatabase = new MyFileDatabase(0, VALID_FILE_PATH); + } + + @Test + @Order(1) + public void deserializeValidFileTest() { + assertNotNull(testDatabase.getDepartmentMapping()); + } + + @Test + @Order(2) + public void handleNonExistentFileTest() { + MyFileDatabase nonExistentDatabase = new MyFileDatabase(0, NON_EXISTENT_FILE_PATH); + assertNull(nonExistentDatabase.getDepartmentMapping(), + "Non-existent file should return null"); + } + + @Test + @Order(3) + public void saveContentsToFileTest() { + testDatabase.saveContentsToFile(); + + File file = new File(VALID_FILE_PATH); + assertTrue(file.exists(), "File should exist"); + } + + @Test + @Order(4) + public void deserializeFileAfterSaveTest() { + MyFileDatabase reloadedDatabase = new MyFileDatabase(0, VALID_FILE_PATH); + Map loadedMapping = reloadedDatabase.getDepartmentMapping(); + + assertNotNull(loadedMapping); + assertEquals(loadedMapping.size(), testDatabase.getDepartmentMapping().size(), + "Reloaded mapping should have the same size as saved mapping"); + } + + @Test + @Order(5) + public void invalidObjectTypeInFileTest() { + testDatabase.setMapping(null); + testDatabase.saveContentsToFile(); + assertNull(testDatabase.getDepartmentMapping(), "Should return null when mapping is null."); + } + } + + @Nested + @TestMethodOrder(OrderAnnotation.class) + class FlagOneTests { + + @BeforeAll + public static void setupDatabaseFlagOneTest() { + testDatabase = new MyFileDatabase(1, "./dummy-data.txt"); + } + + @Test + @Order(1) + public void setMappingOneTest() { + // anthropology department + Course anth1 = new Course("Margaret Mead", "101 MIL", "10:00-11:15", 120); + anth1.setEnrolledStudentCount(110); + Course anth2 = new Course("Franz Boas", "201 MIL", "1:00-2:15", 80); + anth2.setEnrolledStudentCount(70); + + HashMap anthropologyCourses = new HashMap<>(); + anthropologyCourses.put("1001", anth1); + anthropologyCourses.put("1002", anth2); + + // linguistics department + Course ling1 = new Course("Noam Chomsky", "101 NWC", "9:00-10:15", 90); + ling1.setEnrolledStudentCount(85); + Course ling2 = new Course("Ferdinand de Saussure", "202 NWC", "11:00-12:15", 100); + ling2.setEnrolledStudentCount(95); + + HashMap linguisticsCourses = new HashMap<>(); + linguisticsCourses.put("1101", ling1); + linguisticsCourses.put("2202", ling2); + + // music department + Course music1 = new Course("Ludwig van Beethoven", "101 DOD", "3:00-4:15", 150); + music1.setEnrolledStudentCount(140); + Course music2 = new Course("Johann Sebastian Bach", "202 DOD", "4:30-5:45", 120); + music2.setEnrolledStudentCount(110); + + HashMap musicCourses = new HashMap<>(); + musicCourses.put("3001", music1); + musicCourses.put("3002", music2); + + // final map + HashMap departmentMapping = new HashMap<>(); + departmentMapping.put("ANTH", new Department("ANTH", anthropologyCourses, + "Claude Lévi-Strauss", 600)); + departmentMapping.put("LING", new Department("LING", linguisticsCourses, + "Edward Sapir", 700)); + departmentMapping.put("MUSI", new Department("MUSI", musicCourses, + "Wolfgang Amadeus Mozart", 500)); + + testDatabase.setMapping(departmentMapping); + } + + @Test + @Order(2) + public void newMappingOneTest() { + Map map = testDatabase.getDepartmentMapping(); + assertTrue(map.containsKey("ANTH")); + assertFalse(map.containsKey("ELEN")); + assertFalse(map.containsKey("COMS")); + assertFalse(map.containsKey("HIST")); + + assertEquals(map.get("ANTH").getCourseSelection().get("1001") + .getCourseLocation(), "101 MIL"); + assertEquals(map.get("LING").getCourseSelection().get("1101") + .getCourseTimeSlot(), "9:00-10:15"); + assertEquals(map.get("MUSI").getCourseSelection().size(), 2); + + assertEquals(map.get("MUSI").getCourseSelection().get("3002") + .getCourseLocation(), "202 DOD"); + } + + @Test + @Order(3) + public void toStringOneTest() { + System.out.println("Flag 1 - MyFileDatabase content:"); + System.out.println(testDatabase.toString()); + } + + @Test + @Order(4) + public void saveContentsOneToFileTest() { + testDatabase.saveContentsToFile(); + } + + } + + /** The test course instance used for testing. */ + public static MyFileDatabase testDatabase; +} + diff --git a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/DepartmentUnitTests.java b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/DepartmentUnitTests.java new file mode 100644 index 00000000..069b66e6 --- /dev/null +++ b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/DepartmentUnitTests.java @@ -0,0 +1,140 @@ +package dev.coms4156.project.individualproject; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.HashMap; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; + +/** + * Unit tests for the Department class. + * This class contains tests to verify the behavior and functionality of the Department class. + * It uses Spring's testing framework to set up the environment and run the tests. + */ +@SpringBootTest +@ContextConfiguration +@TestMethodOrder(OrderAnnotation.class) +public class DepartmentUnitTests { + + /** + * Sets up test data for a {@link Department} with two {@link Course} objects. + * + *

This method runs once before all tests, creating two courses and adding them to + * a department. The department is stored in {@code testDepartment} for use in tests.

+ */ + @BeforeAll + public static void setupDepartmentForTesting() { + // Creating Courses + Course course1 = new Course("Henry Yuen", "HAV 209", "10:10-11:25", 80); + Course course2 = new Course("Suman Jana", "MUDD 1127", "1:10-2:25", 65); + + // Create a HashMap of courses + HashMap courses = new HashMap<>(); + courses.put("W4281", course1); + courses.put("W4181", course2); + + testDepartment = new Department("COMS", courses, "John Doe", 0); + } + + @Test + @Order(1) + public void toStringTest() { + String expectedResult = "COMS W4281: \nInstructor: Henry Yuen; Location: HAV 209; " + + "Time: 10:10-11:25\nCOMS W4181: \nInstructor: Suman Jana; " + + "Location: MUDD 1127; Time: 1:10-2:25\n"; + assertEquals(expectedResult, testDepartment.toString()); + } + + @Test + @Order(2) + public void dropPersonFromMajorTest1() { + int expectedResult = 0; + assertEquals(expectedResult, testDepartment.getNumberOfMajors()); + + testDepartment.dropPersonFromMajor(); + assertEquals(expectedResult, testDepartment.getNumberOfMajors()); + } + + @Test + @Order(3) + public void addPersonToMajorTest() { + int expectedResult = 0; + assertEquals(expectedResult, testDepartment.getNumberOfMajors()); + + expectedResult = 1; + testDepartment.addPersonToMajor(); + assertEquals(expectedResult, testDepartment.getNumberOfMajors()); + } + + @Test + @Order(4) + public void dropPersonFromMajorTest2() { + int expectedResult = 1; + assertEquals(expectedResult, testDepartment.getNumberOfMajors()); + + expectedResult = 0; + testDepartment.dropPersonFromMajor(); + assertEquals(expectedResult, testDepartment.getNumberOfMajors()); + } + + @Test + @Order(5) + public void addCreateCourseTest() { + testDepartment.createCourse("W4156", "Gail Kaiser", "Zoom", "10:10-11:25", 200); + + String result = testDepartment.toString(); + + String course1 = "COMS W4281: \nInstructor: Henry Yuen; Location: HAV 209; Time: 10:10-11:25"; + assertTrue(result.contains(course1)); + + String course2 = "COMS W4181: \nInstructor: Suman Jana; Location: MUDD 1127; Time: 1:10-2:25"; + assertTrue(result.contains(course2)); + + String course3 = "COMS W4156: \nInstructor: Gail Kaiser; Location: Zoom; Time: 10:10-11:25"; + assertTrue(result.contains(course3)); + } + + @Test + @Order(6) + public void addCourseTest() { + Course newCourse = new Course("Jeffrey Ullman", "Zoom", "3:10-4:25", 150); + testDepartment.addCourse("CS345", newCourse); + + assertTrue(testDepartment.getCourseSelection().containsKey("CS345")); + assertEquals("Jeffrey Ullman", + testDepartment.getCourseSelection().get("CS345").getInstructorName()); + } + + @Test + @Order(7) + public void testAddAndDropMajors() { + testDepartment.addPersonToMajor(); + testDepartment.addPersonToMajor(); + assertEquals(2, testDepartment.getNumberOfMajors()); + + testDepartment.dropPersonFromMajor(); + assertEquals(1, testDepartment.getNumberOfMajors()); + + testDepartment.dropPersonFromMajor(); + assertEquals(0, testDepartment.getNumberOfMajors()); + } + + @Test + @Order(8) + public void testNoCoursesInDepartment() { + Department emptyDepartment = new Department("PHYS", new HashMap<>(), "Albert Einstein", 50); + + assertEquals(0, emptyDepartment.getCourseSelection().size()); + assertEquals("Albert Einstein", emptyDepartment.getDepartmentChair()); + } + + /** The test course instance used for testing. */ + public static Department testDepartment; +} + diff --git a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/IndividualProjectApplicationUnitTests.java b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/IndividualProjectApplicationUnitTests.java new file mode 100644 index 00000000..3698cf7d --- /dev/null +++ b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/IndividualProjectApplicationUnitTests.java @@ -0,0 +1,92 @@ +package dev.coms4156.project.individualproject; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * Unit tests for the IndividualProjectApplication class. + * This class contains tests to verify the behavior and functionality of the + * IndividualProjectApplication class. + * It uses Spring's testing framework to set up the environment and run the tests. + */ +@SpringBootTest +public class IndividualProjectApplicationUnitTests { + + public static IndividualProjectApplication testApplication; + public static MyFileDatabase testDatabase; + private Logger logger; + private CustomLoggerHandler logHandler; + + /** + * Sets up the test environment before each test. + * + *

This method initializes a new instance of the {@code IndividualProjectApplication} + * class and captures the logger output by using the {@code CustomLoggerHandler} class + * This allows the test to verify log messages + * generated during the execution of the application. + */ + @BeforeEach + public void setup() { + testApplication = new IndividualProjectApplication(); + testDatabase = Mockito.mock(MyFileDatabase.class); + + logger = Logger.getLogger(IndividualProjectApplication.class.getName()); + logHandler = new CustomLoggerHandler(); + logger.addHandler(logHandler); + logger.setLevel(java.util.logging.Level.ALL); + } + + @Test + public void testOverride() { + IndividualProjectApplication.overrideDatabase(testDatabase); + assertSame(testDatabase, IndividualProjectApplication.myFileDatabase, + "myFileDatabase should refer to the mock object after overrideDatabase is called."); + } + + @Test + public void testRunWithSetupArgument() { + testApplication.run("setup"); + assertEquals("Jennifer Blaze", IndividualProjectApplication.myFileDatabase + .getDepartmentMapping().get("PSYC") + .getCourseSelection().get("4493") + .getInstructorName()); + + LogRecord record = logHandler.getLatestLogRecord(); + assertTrue(record.getMessage().contains("System Setup"), "Log should contain 'System Setup'"); + } + + @Test + public void testRunWithoutSetupArgument() { + testApplication.run(); + + LogRecord record = logHandler.getLatestLogRecord(); + assertTrue(record.getMessage().contains("Start up"), "Log should contain 'Start up'"); + } + + @Test + public void testResetDataFile() { + IndividualProjectApplication.overrideDatabase(testDatabase); + testApplication.resetDataFile(); + verify(testDatabase, times(1)).setMapping(anyMap()); + } + + @Test + public void testOnTerminationSaveDataFalse() { + IndividualProjectApplication.overrideDatabase(testDatabase); + testApplication.onTermination(); + verify(testDatabase, times(0)).saveContentsToFile(); + } + + +} diff --git a/IndividualProject/src/test/java/dev/coms4156/project/individualproject/RouteControllerUnitTests.java b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/RouteControllerUnitTests.java new file mode 100644 index 00000000..980be2ee --- /dev/null +++ b/IndividualProject/src/test/java/dev/coms4156/project/individualproject/RouteControllerUnitTests.java @@ -0,0 +1,337 @@ +package dev.coms4156.project.individualproject; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +/** + * Unit tests for the RouteController class. + * This class contains tests to verify the behavior and functionality of the + * RouteController class. + * It uses Spring's testing framework to set up the environment and run the tests. + */ +public class RouteControllerUnitTests { + + @Mock + private MyFileDatabase mockFileDatabase; + + @InjectMocks + private RouteController routeController; + + private Map departmentMapping; + + /** + * Sets up the mock environment before each test. + * + *

This method is executed before each test case to initialize mock data. + * It sets up department and course data for three departments (COMS, ECON, and IEOR). + */ + @BeforeEach + public void setup() { + MockitoAnnotations.openMocks(this); + + Map comsCourses = new HashMap<>(); + comsCourses.put("1004", new Course("Adam Cannon", "417 IAB", "11:40-12:55", 400)); + comsCourses.put("3134", new Course("Brian Borowski", "301 URIS", "4:10-5:25", 250)); + comsCourses.put("3157", new Course("Jae Lee", "417 IAB", "4:10-5:25", 400)); + comsCourses.put("4404", new Course("Alex Turner", "505 NWC", "10:10-11:25", 400)); + + Map econCourses = new HashMap<>(); + econCourses.put("1105", new Course("Abraham Lincoln", "309 HAV", "2:40-3:55", 200)); + econCourses.put("2257", new Course("Thomas Jefferson", "428 PUP", "10:10-11:25", 125)); + econCourses.put("3412", new Course("Charles Leclerc", "702 HAM", "8:40-9:55", 100)); + econCourses.put("4404", new Course("Julian Casablancas", "125 DOD", "11:40-12:55", 100)); + + Map ieorCourses = new HashMap<>(); + ieorCourses.put("1004", new Course("Tame Impala", "627 MUDD", "11:40-12:55", 150)); + ieorCourses.put("2500", new Course("Oscar Piastri", "303 MUDD", "4:10-5:25", 100)); + ieorCourses.put("4511", new Course("Lando Norris", "633 MUDD", "2:40-3:55", 200)); + ieorCourses.put("4404", new Course("Mac DeMarco", "702 DOD", "11:40-12:55", 100)); + + comsCourses.get("3157").setEnrolledStudentCount(425); + econCourses.get("2257").setEnrolledStudentCount(2); + + departmentMapping = new HashMap<>(); + + Department comsDept = new Department("COMS", comsCourses, "Luca Carloni", 2700); + departmentMapping.put("COMS", comsDept); + + Department econDept = new Department("ECON", econCourses, "Alex Turner", 2500); + departmentMapping.put("ECON", econDept); + + Department ieorDept = new Department("IEOR", ieorCourses, "Julian Casablancas", 1800); + departmentMapping.put("IEOR", ieorDept); + + when(mockFileDatabase.getDepartmentMapping()).thenReturn(departmentMapping); + + IndividualProjectApplication.myFileDatabase = mockFileDatabase; + } + + @Test + public void testRetrieveDepartmentExists() { + ResponseEntity response = routeController.retrieveDepartment("COMS"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(departmentMapping.get("COMS").toString(), response.getBody()); + } + + @Test + public void testRetrieveDepartmentNotExists() { + ResponseEntity response = routeController.retrieveDepartment("MATH"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testRetrieveCourseDepartmentExists() { + ResponseEntity response = routeController.retrieveCourse("ECON", 2257); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testRetrieveCourseDepartmentNotExists() { + ResponseEntity response = routeController.retrieveCourse("MATH", 2222); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testRetrieveCourseExistsDepartmentNotExist() { + ResponseEntity response = routeController.retrieveCourse("MATH", 4511); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testRetrieveCourseNotExistsDepartmentExist() { + ResponseEntity response = routeController.retrieveCourse("IEOR", 8888); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testIsCourseFullExists() { + ResponseEntity response = routeController.isCourseFull("ECON", 3412); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testIsCourseFullNotExists() { + ResponseEntity response = routeController.isCourseFull("MATH", 3999); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testGetMajorCtFromDeptExist() { + ResponseEntity response = routeController.getMajorCtFromDept("IEOR"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testGetMajorCtFromDeptNotExist() { + ResponseEntity response = routeController.getMajorCtFromDept("HIST"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testIdentifyDeptChairExists() { + ResponseEntity response = routeController.identifyDeptChair("COMS"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testIdentifyDeptChairNotExists() { + ResponseEntity response = routeController.identifyDeptChair("ANTH"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testFindCourseLocationExist() { + ResponseEntity response = routeController.findCourseLocation("COMS", 3134); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testFindCourseLocationNotExist() { + ResponseEntity response = routeController.findCourseLocation("SOCI", 3134); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testFindCourseInstructorExist() { + ResponseEntity response = routeController.findCourseInstructor("IEOR", 2500); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testFindCourseInstructorNotExist() { + ResponseEntity response = routeController.findCourseInstructor("PHIL", 9999); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testFindCourseTimeExist() { + ResponseEntity response = routeController.findCourseTime("IEOR", 2500); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testFindCourseTimeNotExist() { + ResponseEntity response = routeController.findCourseTime("PHIL", 9999); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testAddMajorToDeptExist() { + ResponseEntity response = routeController.addMajorToDept("IEOR"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testAddMajorToDeptNotExist() { + ResponseEntity response = routeController.addMajorToDept("PHIL"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testRemoveMajorFromDeptExist() { + ResponseEntity response = routeController.removeMajorFromDept("IEOR"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testRemoveMajorFromDeptInvalid() { + ResponseEntity response = routeController.setNumberOfMajors("IEOR", 0); + response = routeController.removeMajorFromDept("IEOR"); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + public void testRemoveMajorFromDeptNotExist() { + ResponseEntity response = routeController.removeMajorFromDept("PHIL"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testSetNumberOfMajors() { + ResponseEntity response = routeController.setNumberOfMajors("BIOL", 100); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + + response = routeController.setNumberOfMajors("IEOR", -1); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + + response = routeController.setNumberOfMajors("IEOR", 500); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + + @Test + public void testDropStudentExist() { + ResponseEntity response = routeController.dropStudent("IEOR", 2500); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testDropStudentInvalid() { + ResponseEntity response = routeController.setEnrollmentCount("IEOR", 2500, 0); + response = routeController.dropStudent("IEOR", 2500); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + public void testDropStudentNotExist() { + ResponseEntity response = routeController.dropStudent("PHIL", 9999); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testSetEnrollmentCountExist() { + ResponseEntity response = routeController.setEnrollmentCount("IEOR", 2500, 300); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testInvalidEnrollmentCount() { + ResponseEntity response = routeController.setEnrollmentCount("IEOR", 2500, -1); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + public void testSetEnrollmentCountNotExist() { + ResponseEntity response = routeController.setEnrollmentCount("PHIL", 9999, 250); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testChangeCourseTimeExist() { + ResponseEntity response = routeController.changeCourseTime("IEOR", 2500, "4:00-6:00"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testChangeCourseTimeNotExist() { + ResponseEntity response = routeController.changeCourseTime("PHIL", 9999, "4:00-6:00"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testChangeCourseTeacherExist() { + ResponseEntity response = routeController.changeCourseTeacher("IEOR", 2500, "Kevin Parker"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testChangeCourseTeacherNotExist() { + ResponseEntity response = routeController.changeCourseTeacher("PHIL", 9999, "Jakob Ogawa"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testChangeCourseLocationExist() { + ResponseEntity response = routeController.changeCourseTeacher("IEOR", 2500, "203 SCH"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + + @Test + public void testChangeCourseLocationNotExist() { + ResponseEntity response = routeController.changeCourseTeacher("PHIL", 9999, "701 HAM"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testRetrieveCourses() { + ResponseEntity response = routeController.retrieveCourses(1004); + assertEquals(HttpStatus.OK, response.getStatusCode()); + + System.out.println(response.getBody()); + + response = routeController.retrieveCourses(4404); + assertEquals(HttpStatus.OK, response.getStatusCode()); + + response = routeController.retrieveCourses(3134); + assertEquals(HttpStatus.OK, response.getStatusCode()); + + response = routeController.retrieveCourses(2233); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + + response = routeController.retrieveCourses(5774); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } + + @Test + public void testEnrollStudentInCourse() { + ResponseEntity response = routeController.enrollStudentInCourse("PHIL", 9999); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + + response = routeController.enrollStudentInCourse("COMS", 3157); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + + response = routeController.enrollStudentInCourse("ECON", 2257); + assertEquals(HttpStatus.OK, response.getStatusCode()); + } + +} diff --git a/README.md b/README.md index 93e4ba9b..9a8d0dd8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,92 @@ -# Welcome Students of 4156 +# COMS 4156: Individual Project + +Developed on MacOS and ARM 64. Fall 2024. + +## Building and Running a Local Instance + +To build and run this project, you need to: +1. Clone this repo. +2. Install Maven 3.9.5 (Follow the download and installation instructions here: [https://maven.apache.org/docs/3.9.5/release-notes.html](url)). +3. Install JDK. This project was developed using JDK 17, so this version is recommended. Download the JDK for your device here: [https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html](url). +4. Install any IDE that can run a Java project. This project was developed using VS Code (Download here: [https://code.visualstudio.com/download](url)). +5. Set up the project by running the following command in the [IndividualProject](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/tree/main/IndividualProject) directory by running the command below. Terminate execution (^C) as soon as you see `System Setup`. +``` +mvn spring-boot:run -Dspring-boot.run.arguments="setup" +``` + +## Running a Cloud based Instance + +To deploy your app on the cloud, you can use Google Cloud Platform's App Engine (overview here: [https://cloud.google.com/appengine/docs/an-overview-of-app-engine](url)). To begin, you need to: +1. Download and install the gcloud CLI (link: [https://cloud.google.com/sdk/docs/install](url)) +2. Run the following on your terminal for authentication and setting up your SDK: +``` +gcloud init +``` +3. Create a project on Google Cloud Console +4. Navigate to the [IndividualProject directory](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/tree/main/IndividualProject) in your local copy of the repo and create an `app.yaml` file containing the following: +``` +entrypoint: java -jar target/IndividualProject-0.0.1-SNAPSHOT.jar +runtime: java17 +instance_class: F1 +``` +5. Make sure that you have set up the project as described in step 5 of [this section](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/edit/docs-and-maintenance/README.md#building-and-running-a-local-instance). +6. Run the following command, replacing `PROJECT_ID` with your GCP Project ID: +``` +gcloud config set project PROJECT_ID +``` +7. Run the following command in your terminal to deploy: +``` +gcloud app deploy +``` +8. Running the following command will tell you what URL your app has been deployed in: +``` +gcloud app browse +``` +9. Test your app using [Postman](https://www.postman.com/). You can find a video demo of this app being tested on Postman in the [Video-Demo-Link.txt](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/blob/main/Video-Demo-Link.txt) of this repo's root directory. + +Voila - Your app is now fully deployed on the cloud! + +You can see what the app instance on Google Cloud looks like in the [Google-Cloud-Instance.png](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/blob/main/Google-Cloud-Instance.png) image found in the root directory of this repo. + +## Running Unit Tests + +The unit tests for this project can be found in the [IndividualProject/src/test/java/dev/coms4156/project/individualproject](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/tree/main/IndividualProject/src/test/java/dev/coms4156/project/individualproject) directory. In that directory, you will find unit tests for each of the classes developed for this project. + +To run the unit tests, you must first build the project ([see here](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/edit/docs-and-maintenance/README.md#building-and-running-a-local-instance)) and run the following command in the [IndividualProject](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/tree/main/IndividualProject) directory +``` +mvn clean test +``` + +Running the command above will show you if all the unit tests are successful or any errors that occurred. + +## Style Checking Report + +Style checking was done using the checkstyle tool on Maven. You can run checkstyle using the following command: +``` +mvn checkstyle:check +``` + +Below is the checkstyle report as of 09/27/24, showing that there are 0 checkstyle violations: +Screenshot 2024-09-27 at 00 09 52 + +## Static Code Analysis Report + +PMD is used for the static code analysis report (install here: [https://pmd.github.io/](url)). Run PMD with the following command, +``` +mvn pmd:check +``` + +Below is the PMD report as of 09/26/24: + +Screenshot 2024-09-26 at 23 58 51 + +## Branch Coverage Reporting + +This project uses JaCoco to analyze branch coverage for the unit tests. Run the unit tests first ([see here](https://github.com/elifia-muthia/4156-Miniproject-2024-Students-Java/edit/docs-and-maintenance/README.md#running-unit-tests)), and then run the following command: +``` +mvn jacoco:report +``` + +A target directory will be created (if it doesn't exist already) in IndividualProject and you can find the coverage report in IndividualProject/target/site/jacoco/index.html. Below is the coverage report as of 09/26/24: +Screenshot 2024-09-26 at 23 54 12 -Please follow the assignment specifications on Courseworks when completing this project. diff --git a/Video-Demo-Link.txt b/Video-Demo-Link.txt new file mode 100644 index 00000000..bfa7efe2 --- /dev/null +++ b/Video-Demo-Link.txt @@ -0,0 +1 @@ +https://youtu.be/4d7UBi2qjPw diff --git a/bugs.txt b/bugs.txt new file mode 100644 index 00000000..d17a2f22 --- /dev/null +++ b/bugs.txt @@ -0,0 +1,100 @@ + +Warnings from mvn checkstyle:check + +1. AvoidStarImport + - Course.java + - CourseUnitTests.java + - Department.java + - IndividualProjectApplication.java + - MyFileDatabase.java + - RouteController.java + +2. MissingJavadocType + - Course.java + - CourseUnitTests.java + +3. MissingJavadocMethod + - RouteController.java + +4. CommentsIndentation + - Course.java + +5. Indentation + - Course.java + - IndividualProjectApplication.java + +6. LineLength + - Course.java + - RouteController.java + +7. CustomImportOrder + - CourseUnitTests.java + +8. FileTabCharacter + - IndividualProjectApplication.java + +9. JavadocParagraph Formatting + - IndividualProjectApplication.java + +--- + +PMD Errors + +1. Course.java: + - MissingOverride: The method toString() is missing an @Override annotation. (Line 59-61) + +2. Department.java: + - LooseCoupling: Avoid using implementation types like HashMap; use the interface instead. (Line 22) + - LooseCoupling: Avoid using implementation types like HashMap; use the interface instead. (Line 53) + - MissingOverride: The method toString() is missing an @Override annotation. (Line 101-110) + - LooseCoupling: Avoid using implementation types like HashMap; use the interface instead. (Line 114) + - UnusedPrivateField: Avoid unused private fields such as departmentChair. (Line 115) + +3. IndividualProjectApplication.java: + - UseVarargs: Consider using varargs for methods or constructors that take an array as the last parameter. (Line 36) + - MissingOverride: The method run(String) is missing an @Override annotation. (Line 36-47) + - PositionLiteralsFirstInComparisons: Position literals first in String comparisons. (Line 38) + - SystemPrintln: System.out.println is used, replace with logging. (Lines 41, 46, 292) + +4. MyFileDatabase.java: + - LooseCoupling: Avoid using implementation types like HashMap; use the interface instead. (Line 30) + - LooseCoupling: Avoid using implementation types like HashMap; use the interface instead. (Line 39) + - AvoidPrintStackTrace: Avoid printStackTrace(), use a logger instead. (Lines 48, 62) + - SystemPrintln: System.out.println is used, replace with logging. (Line 60) + - LooseCoupling: Avoid using implementation types like HashMap; use the interface instead. (Lines 71, 95) + +5. RouteController.java: + - UnusedLocalVariable: Avoid unused local variables such as requestedCourse. (Line 277) + - SystemPrintln: System.out.println is used, replace with logging. (Line 501) + +--- + +Bugs found from unit testing (not listed from PMD violations above) + +1. Course.java + - Updated logic for enrollStudent(): return true if the student is successfully enrolled, false otherwise + - Updated logic for dropStudent(): return true if the student is successfully dropped, false otherwise + - Fixed return value for getCourseLocation(): return courseLocation not instructorName + - Fixed return value for getInstructorName(): return instructorName not courseLocation + - Changed the condition for isCourseFull(): now considers enrollmentStudentCount == enrollmentCapacity as full + +2. Department.java + - Fixed typo in return value for getNumberOfMajors() + - getDepartmentChair() initially returns the literal string "this.departmentChair" instead of its value + - Update logic for dropPersonFromMajor(): Decreases the number of majors in the department by one if it's greater than zero + - toString() initially returns the literal string "result.toString()" instead of its value + +3. IndividualProjectApplication.java + ** Changes made to this file doesn't modify any of the logic, only fixes the style to resolve PMD violations: + - Replaces all System.out statements with Logger + - Added @Override to run() + - Replace arg.equals("setup") to "setup".equals(arg) in run() because of PositionLiteralsFirstInComparisons violations + +4. MyFileDatabase.java + - deSerializeObjectFromFile(): Added catch statement for FileNotFoundException + +5. RouteController.java + - retrieveDepartment(): HttpStatus.OK and HttpStatus.NOT_FOUND was flipped + - retrieveCourse(): replace HttpStatus.FORBIDDEN with HttpStatus.OK + - getMajorCtFromDept(): replace HttpStatus.FORBIDDEN with HttpStatus.NOT_FOUND + - findCourseTime(): replace return value from "some time" to requestedCourse.getCourseTimeSlot() \ No newline at end of file diff --git a/citations.txt b/citations.txt new file mode 100644 index 00000000..bd1c13d8 --- /dev/null +++ b/citations.txt @@ -0,0 +1,61 @@ +I1 +--- +Part 1: Code Clean Up + +- https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html +I wasn't familiar with writing doc comments for javadoc so I used the link above as a guide. + +- https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716 +I used the link above as reference on how I should make meaningful commit messages. + +--- +Part 2 & 3: Test Suite Creation & Debugging + +- https://mkyong.com/maven/maven-jacoco-code-coverage-example/ +- https://mvnrepository.com/artifact/org.jacoco/jacoco-maven-plugin/0.8.11 +I used the 2 links above to figure out how to modify my pom.xml file to integrate the correct +version of jacoco that I can use for this project. + +- https://www.baeldung.com/junit-5-test-order +- https://www.geeksforgeeks.org/junit-5-test-execution-order/ +I wanted to decide the order that the tests will be executed so I used this as reference. + +- https://www.baeldung.com/junit-5-nested-test-classes +I wanted to make 2 separate nested classes in the unit tests to simulate different parameters +and scenarios. + +- https://docs.oracle.com/javase/8/docs/api/java/util/logging/package-summary.html +- https://www.digitalocean.com/community/tutorials/logger-in-java-logging-example +PMD didn't like the usage of System.out so I had to replace everything with java Logger and +this was reference on how to use it. I also wanted to read from my logs so I created a +CustomLoggerHandler class to handle these scenarios, as shown in the second link above. + +- https://www.baeldung.com/mockito-junit-5-extension +- https://www.digitalocean.com/community/tutorials/mockito-injectmocks-mocks-dependency-injection +- https://www.baeldung.com/mockito-annotations +I had to use mockito to test the IndividualProjectApplication.java and RouteController.java files +so I used the links above as reference on how to use it. + +--- +--- +I3 +--- +Part 1: Github Actions + +- https://github.blog/enterprise-software/ci-cd/build-ci-cd-pipeline-github-actions-four-steps/ +Setting up Github Actions + +- https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-java-with-maven +Writing yaml file for workflow + +--- +Part 3: Deploying to Google Cloud + +- https://cloud.google.com/billing/docs/how-to/edu-grants +Redeeming coupon + +- https://cloud.google.com/appengine/docs/flexible/monitoring-and-alerting-latency +To get the monitoring metrics dashboard for App Engine + +- https://cloud.google.com/appengine/docs/standard/reference/app-yaml?tab=java +Setting up app.yaml file diff --git a/honesty.txt b/honesty.txt new file mode 100644 index 00000000..71a7cc10 --- /dev/null +++ b/honesty.txt @@ -0,0 +1,14 @@ +I, Elifia Muthia (em3308), have read and understood the following: + +CS department's Policies and Procedures on Academic Honesty +The Course Specific Academic Honesty Policies +The assignment specs outlining the consequences of not submitting this pledge and other aspects of the policy +I affirm that I will abide by all the policies stated in the relevant materials from above. I understand that the relevant policies apply to: individual assignments, group projects, and individual examinations. + +I also affirm that I understand that all course materials, with the exception of the individual/group project, are subject to the appropriate copyrights and thus will not post them on any public forum or publicly hosted repository, this includes but is not limited to: GitHub, stackoverflow, chegg etc. + +I also affirm that I will be 100% honest when evaluating the performance of myself and my teammates when prompted by an assignment or member of the teaching staff. + +Finally I affirm that I will not attempt to find any loopholes in these policies for the benefit of myself or others enrolled in the course presently or possibly in the future. + +Signed: Elifia Muthia (em3308) September 10, 2024 \ No newline at end of file