Schedule-Creator

Java GUI to create schedules for UNCG students
git clone git://git.wrycode.com/wrycode/archive/Schedule-Creator.git
Log | Files | Refs | README

commit 1bd18ccd11e19f5670c3f989cc8b906c943bd679
parent 6a80e6d34b6cfdd26a3c2810a4567314efdcdefe
Author: jamisonv93 <47033011+jamisonv93@users.noreply.github.com>
Date:   Fri, 20 Mar 2020 19:47:26 -0400

Set up for adding entries to the calendar/schedule in the GUI (#42)

* rebuild DB with included Fall 2020 files

* Change default size of primary view; addLoadSelectedCourses() to switchSemester() in CoursesController

* fix addSelectedCourse & getSelectedCourses; delete User Model

* minor changes, style guide, comments

* add Semester model

* small change in Semester class

* small change in Semester class

* use models in controller methods to handle data; add constraint to prevent selected given class more than once

* reset Course Combo Box after selecting cousre or switching combo box

* Fix trailing whitespace issue Admin.generateCourseList

* remove unnecessary occurences of trim(); fix removeCourse bug

* refactor Semester class

* restructure GUI components, implement getSections, more model implementation

* implement search feature; more model integration

* remove debugging helper statements

* remove trailing whitespace

* implement Sections.setTImes

* fix search bug; adjust button heights

* add drawGrid() method

* gui changes

* remove test rect

* remove debug statements

* fix section time bug

* add implement Section.getDays(); CourseController.addEntry() prototyping

* remove debug statements

* implement CourseController.clearCalendar()

* fix getSection bug when no course selected

* set min width and height for application window

* comment and refactor

* Add a ListView of courses that gets filtered by search bar

* Make top course always selected in list while searching

* Replace old course adding UI with search bar

* Add back Add Course button

* Add small clarification comments

* add prev and next buttons for different schedules

* implement schedule generation algorithm; continued work on GUI schedule prototype

* fix grid row spacing bug

* fix bug in schedule generation algorithm

* resolve null exception bugs when clicking buttons

* make searh bar case insensitive

* adjust default sizes of view components

Co-authored-by: Nick Econopouly <wry@mm.st>
Diffstat:
MScheduleCreator/App.java | 5++++-
MScheduleCreator/controllers/CoursesController.java | 300++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
MScheduleCreator/controllers/PrimaryController.java | 10+++++-----
MScheduleCreator/models/Course.java | 4++--
MScheduleCreator/models/Schedule.java | 49+++++++++++++++++++++++++++++++++++++++++++++----
MScheduleCreator/models/Section.java | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
MScheduleCreator/models/Semester.java | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
MScheduleCreator/resources/views/select_courses.fxml | 111+++++++++++++++++++++++++++++++++++++++++++------------------------------------
8 files changed, 520 insertions(+), 167 deletions(-)

diff --git a/ScheduleCreator/App.java b/ScheduleCreator/App.java @@ -5,7 +5,7 @@ package ScheduleCreator; * * @author Jamison Valentine * - * Last Updated: 3/16/2020 + * Last Updated: 3/18/2020 */ import java.io.IOException; @@ -34,6 +34,9 @@ public class App extends Application { // sets primary scene primaryStage.setScene(primaryScene); + primaryStage.setMinHeight(560); + primaryStage.setMinWidth(1090); + // shows window on screen primaryStage.show(); } diff --git a/ScheduleCreator/controllers/CoursesController.java b/ScheduleCreator/controllers/CoursesController.java @@ -2,6 +2,7 @@ package ScheduleCreator.controllers; import ScheduleCreator.Translator; import ScheduleCreator.models.Course; +import ScheduleCreator.models.Schedule; import ScheduleCreator.models.Section; import ScheduleCreator.models.Semester; import java.io.IOException; @@ -13,33 +14,39 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javafx.application.Platform; import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; +import javafx.geometry.HPos; +import javafx.geometry.Pos; +import javafx.geometry.VPos; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.TextField; +import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.RowConstraints; +import javafx.scene.layout.StackPane; +import javafx.scene.shape.Rectangle; /** * This class controls interactions in the Courses View. * * @author Jamison Valentine, Ilyass Sfar, Nick Econopouly, Nathan Tolodzieki * - * Last Updated: 3/16/2020 + * Last Updated: 3/18/2020 */ public class CoursesController implements Initializable { @FXML - protected Button semesterButton; - @FXML protected ComboBox<String> semesterComboBox; @FXML - protected ComboBox<String> courseComboBox; + protected ListView availableCourses; @FXML protected ListView selectedCourses; @FXML @@ -49,52 +56,62 @@ public class CoursesController implements Initializable { @FXML protected Button removeCourseButton; @FXML - protected Button searchButton; - @FXML protected TextField searchField; @FXML - protected RowConstraints topRow; - @FXML protected GridPane scheduleGrid; + @FXML + protected Label scheduleLabel; + + // list of courses for current semester + FilteredList<String> courseList; + //ObservableList<String> courseList = FXCollections.observableArrayList(); protected Semester currentSemester; protected Semester spring2020 = new Semester("spring2020"); protected Semester summer2020 = new Semester("summer2020"); protected Semester fall2020 = new Semester("fall2020"); + protected Course focusedCourse; + + protected int NUM_ROWS; + protected int NUM_COLS; + protected double ROW_HEIGHT; + protected double COL_WIDTH; + protected int currentScheduleIndex; + + BorderPane[][] grid; + List<BorderPane> entries = new ArrayList(); + @Override public void initialize(URL url, ResourceBundle rb) { try { loadSemesters(); + NUM_ROWS = scheduleGrid.getRowConstraints().size(); + NUM_COLS = scheduleGrid.getColumnConstraints().size(); + ROW_HEIGHT = scheduleGrid.getRowConstraints().get(0).getPrefHeight(); + COL_WIDTH = scheduleGrid.getColumnConstraints().get(0).getPrefWidth(); + grid = new BorderPane[NUM_ROWS][NUM_COLS]; + drawGrid(); } catch (IOException ex) { Logger.getLogger(CoursesController.class.getName()).log(Level.SEVERE, null, ex); } - } - - public void displaySchedule(ActionEvent _event) { - double blockHeight = scheduleGrid.getHeight() / 13; - HBox block = new HBox(); - block.setPrefHeight(blockHeight); - GridPane.setConstraints(block, 0, 0, 0, 2); } public void addSelectedCourse(ActionEvent _event) throws Exception { - String selectedCourse = this.courseComboBox.getValue(); - this.courseComboBox.setValue("-"); - - if (selectedCourse != null && selectedCourse != "-") { - - if (currentSemester.addCourse(selectedCourse)) { + if (this.availableCourses.getFocusModel().getFocusedItem() != null) { + String selectedCourse = this.availableCourses.getFocusModel().getFocusedItem().toString(); + if (this.currentSemester.addCourse(selectedCourse)) { this.selectedCourses.getItems().add(selectedCourse); + this.currentSemester.generateSchedules(); } + regenerateSchedules(); } } public void switchSemester(ActionEvent _event) throws Exception { String currentSemesterString = semesterComboBox.getValue(); - this.courseComboBox.setValue("-"); switch (formatSemester(currentSemesterString)) { @@ -106,72 +123,145 @@ public class CoursesController implements Initializable { break; case "fall2020": this.currentSemester = fall2020; - break; } loadAllCourses(this.currentSemester.getName()); loadSelectedCourses(this.currentSemester.getName()); + if (this.currentSemester.getSelectedCourses().size() > 0) { + loadSchedule(this.currentSemester.getSchedules().get(0)); + } + } public void clearCalendar() { - System.out.println("Dummy function to clear the calendar for when we switch semesters"); + for (BorderPane entry : entries) { + scheduleGrid.getChildren().remove(entry); + } } protected void clearSectionList() { System.out.println("Dummy function to clear the list of available sections for when we switch semesters"); } - public void search(ActionEvent _event) { - String searchString = this.searchField.getText(); - List<String> filteredList = new ArrayList(); + // TODO: connect "delete" while in the selectedCourses ListView to this method and + // allow for selecting and deleting multiple courses + public void removeSelectedCourse(ActionEvent _event) throws Exception { + if (this.focusedCourse != null) { + Object itemToRemove = this.selectedCourses.getSelectionModel().getSelectedItem(); + this.selectedCourses.getItems().remove(itemToRemove); - if (this.currentSemester != null) { - - for (String course : this.currentSemester.getAllCourses()) { - if (course.toLowerCase().contains(searchString.toLowerCase())) { - filteredList.add(course); - } - } + String courseToDelete = ((String) itemToRemove).trim(); + this.currentSemester.removeCourse(courseToDelete); + this.currentSemester.generateSchedules(); + regenerateSchedules(); } - this.courseComboBox.setItems(FXCollections.observableList(filteredList)); - } - public void removeSelectedCourse(ActionEvent _event) throws Exception { + } - Object itemToRemove = this.selectedCourses.getSelectionModel().getSelectedItem(); - this.selectedCourses.getItems().remove(itemToRemove); + public void regenerateSchedules() { + this.currentSemester.generateSchedules(); + clearCalendar(); - String courseToDelete = (String) itemToRemove; - this.currentSemester.removeCourse(courseToDelete.trim()); + if (this.currentSemester.getNumberOfSchedules() == 0) { + scheduleLabel.setText("0/0"); + } else if (this.currentSemester.getNumberOfSchedules() > 0) { + loadSchedule(this.currentSemester.getSchedules().get(0)); + scheduleLabel.setText("1/" + this.currentSemester.getNumberOfSchedules()); + } } public void loadCourseSections(ActionEvent _event) { List<Section> courseSections = new ArrayList(); - String currentSelection = this.selectedCourses.getFocusModel().getFocusedItem().toString(); - for (Course course : this.currentSemester.getSelectedCourses()) { - if (course.getFullText().equals(currentSelection)) { - courseSections = course.getSections(); + if (this.selectedCourses.getFocusModel().getFocusedItem() != null) { + String currentSelection = this.selectedCourses.getFocusModel().getFocusedItem().toString(); + + for (Course course : this.currentSemester.getSelectedCourses()) { + if (course.getFullText().equals(currentSelection)) { + + this.focusedCourse = course; + courseSections = course.getSections(); + break; + } } - } - List<String> listCellLabels = new ArrayList(); + List<String> listCellLabels = new ArrayList(); - for (Section section : courseSections) { - listCellLabels.add(section.toString()); - } + for (Section section : courseSections) { + listCellLabels.add(section.toString()); + } - this.sectionListView.setItems(FXCollections.observableList(listCellLabels)); + this.sectionListView.setItems(FXCollections.observableList(listCellLabels)); + } } public void loadAllCourses(String _semester) throws Exception { - this.courseComboBox.setItems(FXCollections.observableList(this.currentSemester.getAllCourses())); + + // intermediary ObservableList of the courses + ObservableList<String> OList = FXCollections.observableList(this.currentSemester.getAllCourses()); + + // create FilteredList that we'll actually use + this.courseList = new FilteredList<>(OList, s -> true); + + // connect availableCourses ListView to the courseList + this.availableCourses.setItems(this.courseList); + + // TODO: make up and down arrow on the keyboard scroll the search results + /* searchField.setOnKeyPressed(new javafx.event.EventHandler<KeyEvent>() { + public void handle(KeyEvent event) { + int i = 0; + switch (event.getCode()) { + case UP: + i = 1; + break; + case DOWN: + i = -1; + break; + } + Platform.runLater(new Runnable() { + @Override + public void run() { + availableCourses.getFocusModel().focus(availableCourses.getSelectionModel().getSelectedIndex() + 1); + } + }); + } + }); + */ + // Connect search bar filtering to the courseList FilteredList (this uses lambdas, it's adapted from + // https://stackoverflow.com/questions/28448851/how-to-use-javafx-filteredlist-in-a-listview + // and https://stackoverflow.com/questions/45045631/filter-items-within-listview-in-javafx ) + searchField.textProperty().addListener(obs -> { + + // select the top entry whenever the search term changes, but use Platform.runLater() + // so that JavaFX doesn't try to update the selection while it's still building the ListView. + // See https://stackoverflow.com/questions/11088612/javafx-select-item-in-listview for some context + Platform.runLater(new Runnable() { + @Override + public void run() { + // Note: we can't use "this" keyword here + availableCourses.getSelectionModel().select(0); + availableCourses.getFocusModel().focus(0); + } + }); + + String filter = searchField.getText().toLowerCase(); + // when there's nothing entered yet + if (filter == null || filter.length() == 0) { + // show all courses + this.courseList.setPredicate(s -> true); + // otherwise + } else { + // filter based on the contents of the search bar + this.courseList.setPredicate(s -> s.toLowerCase().contains(filter)); + } + }); + } public void loadSemesters() throws IOException { @@ -197,6 +287,8 @@ public class CoursesController implements Initializable { public void loadSelectedCourses(String _semester) throws Exception { List<String> courses = Translator.getSelectedCourses(_semester); this.selectedCourses.setItems(FXCollections.observableList(courses)); + regenerateSchedules(); + } public String formatSemester(String _semester) { @@ -208,4 +300,104 @@ public class CoursesController implements Initializable { return formattedSemester; } + + public void drawGrid() { + + for (int i = 1; i <= NUM_ROWS - 1; i++) { + for (int j = 1; j <= NUM_COLS - 1; j++) { + BorderPane region = new BorderPane(); + region.setStyle(("-fx-border-color: black; -fx-border-width: .5;")); + grid[i][j] = region; + scheduleGrid.add(region, j, i); + + } + } + + } + + public void addSection(ActionEvent _event) { + if (this.focusedCourse != null) { + int secIndex = this.sectionListView.getFocusModel().getFocusedIndex(); + Section focusedSection = this.focusedCourse.getSections().get(secIndex); + this.currentSemester.addSelectedSection(focusedCourse, focusedSection); + this.currentSemester.generateSchedules(); + loadSchedule(this.currentSemester.getSchedules().get(0)); + } + } + + public void addEntry(Section _section) { + + char[] daysString = _section.getDays().toCharArray(); + ArrayList<Integer> days = new ArrayList(); + for (char day : daysString) { + switch (day) { + case 'T': + days.add(2); + break; + case 'M': + days.add(1); + break; + case 'W': + days.add(3); + break; + case 'R': + days.add(4); + break; + case 'F': + days.add(5); + break; + + } + + int row = (int) _section.getStartTime() / 100 - 7; + for (Integer col : days) { + BorderPane region = grid[row][col]; + Label label = new Label(_section.getCourseID() + " - " + _section.getSectionNumber()); + BorderPane cont = new BorderPane(); + StackPane pane = new StackPane(); + + Rectangle rect = new Rectangle(); + rect.setStyle("-fx-fill:lightblue;"); + label.setAlignment(Pos.CENTER); + + pane.setStyle("-fx-border-color:blue;"); + pane.getChildren().addAll(rect, label); + cont.setTop(pane); + + scheduleGrid.getChildren().add(cont); + GridPane.setConstraints(cont, col, row, 1, GridPane.REMAINING, HPos.CENTER, VPos.TOP); + rect.heightProperty().bind(region.heightProperty().subtract(2).multiply(_section.getDurationHours())); + rect.widthProperty().bind(region.widthProperty().subtract(2)); + entries.add(cont); + + } + + } + } + + public void loadSchedule(Schedule _schedule) { + clearCalendar(); + for (Section section : _schedule.getAddedSections()) { + addEntry(section); + } + scheduleLabel.setText(this.currentScheduleIndex + 1 + "/" + this.currentSemester.getNumberOfSchedules()); + } + + public void loadNextSchedule(ActionEvent _event) { + + if (this.currentSemester != null) { + if (this.currentScheduleIndex < this.currentSemester.getSchedules().size() - 1) { + this.currentScheduleIndex++; + loadSchedule(this.currentSemester.getSchedules().get(this.currentScheduleIndex)); + } + } + } + + public void loadPrevSchedule(ActionEvent _event) { + + if (this.currentScheduleIndex > 0) { + this.currentScheduleIndex--; + loadSchedule(this.currentSemester.getSchedules().get(this.currentScheduleIndex)); + } + } } diff --git a/ScheduleCreator/controllers/PrimaryController.java b/ScheduleCreator/controllers/PrimaryController.java @@ -5,8 +5,9 @@ package ScheduleCreator.controllers; * * @author Jamison Valentine * - * Last Updated: 3/16/2020 + * Last Updated: 3/18/2020 */ + import javafx.event.ActionEvent; import javafx.fxml.FXMLLoader; import javafx.scene.Node; @@ -25,11 +26,9 @@ public class PrimaryController implements Initializable { Parent root = FXMLLoader.load(getClass().getResource("/ScheduleCreator/resources/views/select_courses.fxml")); Scene classViewScene = new Scene(root); - //Get information from primary stage + //Get window object and refresh to show the new scene Stage window = (Stage) ((Node) _event.getSource()).getScene().getWindow(); window.setScene(classViewScene); - window.setMinHeight(600); - window.setMinWidth(1000); window.show(); } @@ -38,7 +37,7 @@ public class PrimaryController implements Initializable { Parent root = FXMLLoader.load(getClass().getResource("/ScheduleCreator/resources/views/primary.fxml")); Scene primaryScene = new Scene(root); - //Get information from primary stage + //Get window object and refresh to show the new scene Stage window = (Stage) ((Node) _event.getSource()).getScene().getWindow(); window.setScene(primaryScene); window.show(); @@ -49,6 +48,7 @@ public class PrimaryController implements Initializable { Parent root = FXMLLoader.load(getClass().getResource("/ScheduleCreator/resources/views/registration_screen.fxml")); Scene scene = new Scene(root); + //Get window object and refresh to show the new scene Stage stage = (Stage) ((Node) _event.getSource()).getScene().getWindow(); stage.setScene(scene); stage.show(); diff --git a/ScheduleCreator/models/Course.java b/ScheduleCreator/models/Course.java @@ -81,7 +81,7 @@ public class Course { // System.out.println("Location: " + location); // System.out.println("Instructor: " + instructor); // System.out.println("This is not an online course"); - Section newSection = new Section(sectionNumber, daysAndTimes, location, instructor, CRN, false); + Section newSection = new Section(this.id, sectionNumber, daysAndTimes, location, instructor, CRN, false); this.sections.add(newSection); } @@ -93,7 +93,7 @@ public class Course { // System.out.println("Section #: " + sectionNumber); // System.out.println("Online class"); // System.out.println("Instructor: " + instructor); - Section newSection = new Section(sectionNumber, daysAndTimes, location, instructor, CRN, true); + Section newSection = new Section(this.id, sectionNumber, daysAndTimes, location, instructor, CRN, true); this.sections.add(newSection); } // else System.out.println("Does not match"); diff --git a/ScheduleCreator/models/Schedule.java b/ScheduleCreator/models/Schedule.java @@ -1,19 +1,21 @@ package ScheduleCreator.models; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * This class keeps track of added course sections and produces CRNs. * * @author Jamison Valentine - * + * * Last Updated: 3/16/2020 */ - public class Schedule { - protected List<Section> sections; + protected List<Section> addedSections; protected String totalCredits; + //checkConflicts //addSection //removeSection @@ -21,9 +23,48 @@ public class Schedule { //saveSchedule //resetSchedule //generateRandomSchedules - public Schedule() { + addedSections = new ArrayList(); + } + + public Boolean addSection(Section _newSection) { + if (this.addedSections.size() > 0 && !_newSection.isOnline) { + + List<Section> campusSections = new ArrayList(); + for (Section section : addedSections) if (!section.isOnline) campusSections.add(section); + for (Section existingSection : campusSections) { + + Boolean sameDay = false; + for (char day : _newSection.getDays().toCharArray()) { + if (existingSection.getDays().contains("" + day)) sameDay = true; + } + + if (sameDay) { + if (existingSection.endTime >= _newSection.startTime && existingSection.startTime < _newSection.startTime) { + return false; + } + if (existingSection.startTime <= _newSection.endTime && existingSection.endTime > _newSection.endTime) { + return false; + } + } + } + } + + this.addedSections.add(_newSection); + return true; } + public List<Section> getAddedSections() { + return this.addedSections; + } + + @Override + public String toString() { + StringBuilder string = new StringBuilder(); + for (Section section : addedSections) { + string.append("(" + section.getCourseID() + " - " + section.getSectionNumber() + ")\n"); + } + return string.toString(); + } } diff --git a/ScheduleCreator/models/Section.java b/ScheduleCreator/models/Section.java @@ -7,70 +7,80 @@ import java.util.Scanner; * * @author Jamison Valentine * - * Last Updated: 3/17/2020 + * Last Updated: 3/18/2020 */ - public class Section { + protected final String courseID; protected final String location; protected final String instructor; protected final String daysAndTimes; - protected int startTime; - protected int endTime; + protected String days; + protected double startTime; + protected double endTime; protected final String CRN; protected final String sectionNumber; protected final Boolean isOnline; - public Section(String _sectionNumber, String _daysAndTimes, String _location, String _instructor, String _CRN, Boolean _isOnline) { + public Section(String _courseID, String _sectionNumber, String _daysAndTimes, String _location, String _instructor, String _CRN, Boolean _isOnline) { + this.courseID = _courseID; this.location = _location; this.instructor = _instructor; this.daysAndTimes = _daysAndTimes; this.CRN = _CRN; this.sectionNumber = _sectionNumber; this.isOnline = _isOnline; - if (!this.isOnline) setTimes(_daysAndTimes); - } - - public void setTimes(String _daysAndTimes) { - Scanner input = new Scanner(_daysAndTimes); - input.next(); - int start = Integer.parseInt(input.next().replace(":", "")); - if (input.next().equals("pm")) { - start += 1200; - if (start >= 2400) start -= 2400; - } - input.next(); - int end = Integer.parseInt(input.next().replace(":", "")); - if (input.next().equals("pm")) { - end += 1200; - if (end >= 2400) end -= 2400; - } - this.startTime = start; - this.endTime = end; - System.out.println("Start time: " + this.startTime); - System.out.println("End time: " + this.endTime); + setTimes(); } + /** + * + * @param _daysAndTimes is a string similar to 11:00 am - 12:15 pm + */ //================= GETTERS =============== + public double getDurationHours() { + + double difference = this.endTime - this.startTime; + double hours = (int) (difference / 100); + double minutes = difference % 100; + hours += (minutes / 60); + return hours; + } public String getDaysAndTimes() { return daysAndTimes; } + public String getDays() { + return this.days; + } + + public double getStartTime() { + return this.startTime; + } + + public double getEndTime() { + return this.endTime; + } + public String getCRN() { - return CRN; + return this.CRN; } public String getInstructor() { - return instructor; + return this.instructor; } public String getLocation() { - return location; + return this.location; } public String getSectionNumber() { - return sectionNumber; + return this.sectionNumber; + } + + public String getCourseID() { + return this.courseID; } @Override @@ -78,12 +88,39 @@ public class Section { String string = ""; if (!this.isOnline) { - string = this.sectionNumber + " | " + this.daysAndTimes + " | " + this.location + " | "+ this.instructor + " | " + this.CRN; - } - else { - string = this.sectionNumber + " | Online | " + this.instructor + " " + this.CRN; + string = this.sectionNumber + " | " + this.daysAndTimes + " | " + this.location + " | " + this.instructor + " | " + this.CRN; + } else { + string = this.sectionNumber + " | Online | " + this.instructor + " | " + this.CRN; } return string; } -}- \ No newline at end of file +//========================= SETTERS ============================= + public void setTimes() { + + if (this.isOnline) { + this.days = ""; + this.startTime = 0; + this.endTime = 0; + return; + } + Scanner input = new Scanner(this.daysAndTimes); + this.days = input.next(); + int start = Integer.parseInt(input.next().replace(":", "")); + if (input.next().equals("pm")) { + if (start < 1200) { + start += 1200; + } + } + input.next(); + int end = Integer.parseInt(input.next().replace(":", "")); + if (input.next().equals("pm")) { + if (end < 1200) { + end += 1200; + } + } + this.startTime = start; + this.endTime = end; + } + +} diff --git a/ScheduleCreator/models/Semester.java b/ScheduleCreator/models/Semester.java @@ -2,6 +2,7 @@ package ScheduleCreator.models; import ScheduleCreator.Translator; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; /** @@ -18,10 +19,9 @@ public class Semester { protected final String name; protected final List<String> allCourses; - protected ArrayList<Course> selectedCourses; - protected ArrayList<Section> selectedSections; - protected Schedule schedule; - + protected List<Schedule> schedules; + protected HashMap<Course,List<Section>> selectedSections; + protected int numberOfSchedules; /** * Methods to implement * clearCalendar(); @@ -35,15 +35,33 @@ public class Semester { */ public Semester(String _name) { this.name = _name; - loadSelectedCoursesFromFile(); this.allCourses = Translator.getCourses(this.name); + this.schedules = new ArrayList(); + this.selectedSections = new HashMap(); + loadSelectedCoursesFromFile(); } //WORK IN PROGRESS - public void addSelectedSection(Section _section) { - this.selectedSections.add(_section); + public void addSelectedSection(Course _course, Section _section) { + List<Section> list = new ArrayList(); + if (this.selectedSections.get(_course) == null) { + list.add(_section); + this.selectedSections.put(_course, list); + } + else { + List<Section> sectionList = this.selectedSections.get(_course); + if (!sectionList.contains(_section)) { + sectionList.add(_section) ; + } + } + + } + + public HashMap<Course,List<Section>> getSelectedSections() { + return this.selectedSections; } + public void generateCourseList() { // generate the courseList by iterating through the sections. // this should only be performed on an actual semester (fall 2020, etc.) @@ -53,7 +71,7 @@ public class Semester { public Boolean addCourse(String _course) { Boolean contains = false; - for (Course course: this.selectedCourses) { + for (Course course: this.selectedSections.keySet()) { if (course.getFullText().equalsIgnoreCase(_course)) { @@ -64,7 +82,7 @@ public class Semester { if (!contains) { - this.selectedCourses.add(new Course(_course, this.name)); + this.selectedSections.put(new Course(_course, this.name), new ArrayList<Section>()); Translator.saveCourse(_course, this.name); return true; @@ -75,12 +93,66 @@ public class Semester { } + public List<Schedule> getSchedules() { + return this.schedules; + } + + public void generateSchedules() { + List<Schedule> list = generateSchedules(new ArrayList<Course>(this.selectedSections.keySet())); + this.schedules = list; + this.numberOfSchedules = this.schedules.size(); + } + + private List<Schedule> generateSchedules(List<Course> selectedCourses) { + + List<Schedule> validSchedules = new ArrayList(); + + //if there are no remaining, return an empty list + if (selectedCourses.isEmpty()) return validSchedules; + + //Select first course in the remaining course. + Course course = selectedCourses.get(0); + + /** + * Base case for recursion + * + * If there is only one course in the remaining list, construct a new schedule for each section. + **/ + if (selectedCourses.size() == 1) { + for (Section section : course.getSections()) { + Schedule newSchedule = new Schedule(); + newSchedule.addSection(section); + validSchedules.add(newSchedule); + } + } + + //If there is more than one course in the list + else { + + //Remove the current course from the remaining list + List<Course> remainingCourses = new ArrayList(selectedCourses); + remainingCourses.remove(course); + + for (Section section : course.getSections()) { + for (Schedule schedule : generateSchedules(remainingCourses)) { + if (schedule.addSection(section)) validSchedules.add(schedule); + } + } + } + + return validSchedules; + } + // ============================== Getters ============================ public String getName() { return this.name; } + public int getNumberOfSchedules() { + return this.numberOfSchedules; + } + public List<String> getAllCourses() { return this.allCourses; } @@ -94,11 +166,9 @@ public class Semester { public void loadSelectedCoursesFromFile() { List<String> list = Translator.getSelectedCourses(this.name); - - this.selectedCourses = new ArrayList(); if (!list.isEmpty()) { for (String courseName: list) { - this.selectedCourses.add(new Course(courseName, this.name)); + this.selectedSections.put(new Course(courseName, this.name), new ArrayList<Section>()); } } @@ -107,12 +177,12 @@ public class Semester { public void removeCourse(String _course) throws Exception { Course courseToRemove; - for (Course course: this.selectedCourses) { + for (Course course: this.selectedSections.keySet()) { if (_course.equalsIgnoreCase(course.getFullText())) { courseToRemove = course; - this.selectedCourses.remove(courseToRemove); + this.selectedSections.remove(course); break; } } @@ -121,7 +191,7 @@ public class Semester { } public List<Course> getSelectedCourses() { - return this.selectedCourses; + return new ArrayList<Course>(this.selectedSections.keySet()); } } \ No newline at end of file diff --git a/ScheduleCreator/resources/views/select_courses.fxml b/ScheduleCreator/resources/views/select_courses.fxml @@ -1,6 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<?import javafx.scene.effect.*?> +<?import javafx.scene.canvas.*?> +<?import javafx.scene.*?> +<?import javafx.scene.shape.*?> <?import java.lang.*?> <?import javafx.geometry.*?> <?import javafx.scene.control.*?> @@ -10,14 +12,14 @@ <?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.ListView?> -<?import javafx.scene.control.SplitPane?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.RowConstraints?> -<AnchorPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="560.0" prefWidth="1090.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ScheduleCreator.controllers.CoursesController"> +<AnchorPane fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="744.0" prefWidth="1101.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ScheduleCreator.controllers.CoursesController"> <children> <GridPane AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <columnConstraints> @@ -34,34 +36,26 @@ <ColumnConstraints hgrow="SOMETIMES" maxWidth="342.0" minWidth="10.0" prefWidth="155.0" /> </columnConstraints> <rowConstraints> - <RowConstraints maxHeight="214.0" minHeight="10.0" prefHeight="33.0" vgrow="NEVER" /> - <RowConstraints maxHeight="243.0" minHeight="0.0" prefHeight="34.0" vgrow="NEVER" /> - <RowConstraints maxHeight="375.0" minHeight="0.0" prefHeight="22.0" vgrow="NEVER" /> - <RowConstraints maxHeight="338.0" minHeight="10.0" prefHeight="128.0" vgrow="NEVER" /> - <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="22.0" vgrow="NEVER" /> - <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="177.0" vgrow="SOMETIMES" /> - <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="31.0" vgrow="NEVER" /> + <RowConstraints maxHeight="214.0" minHeight="10.0" prefHeight="30.0" vgrow="NEVER" /> + <RowConstraints maxHeight="243.0" minHeight="0.0" prefHeight="35.0" vgrow="NEVER" /> + <RowConstraints maxHeight="375.0" minHeight="10.0" prefHeight="175.0" vgrow="NEVER" /> + <RowConstraints maxHeight="375.0" minHeight="10.0" prefHeight="30.0" vgrow="NEVER" /> + <RowConstraints maxHeight="338.0" minHeight="10.0" prefHeight="133.0" vgrow="NEVER" /> + <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="30.0" vgrow="NEVER" /> + <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="83.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="30.0" vgrow="NEVER" /> </rowConstraints> <children> - <TextField fx:id="searchField" prefHeight="25.0" prefWidth="262.0" promptText="Search" GridPane.rowIndex="1" /> - <HBox maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2"> - <children> - <Button fx:id="courseButton" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#addSelectedCourse" prefHeight="200.0" prefWidth="177.0" text="Add Course" HBox.hgrow="ALWAYS" /> - </children> - </HBox> - <HBox maxHeight="1.7976931348623157E308" prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="2"> - <children> - <ComboBox fx:id="courseComboBox" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" prefWidth="289.0" promptText="-" HBox.hgrow="ALWAYS" /> - </children> - </HBox> - <ComboBox fx:id="semesterComboBox" onAction="#switchSemester" prefHeight="62.0" prefWidth="226.0" promptText="Choose Semester" GridPane.columnIndex="1" GridPane.hgrow="NEVER" /> - <Button fx:id="searchButton" mnemonicParsing="false" onAction="#search" prefHeight="81.0" prefWidth="214.0" text="Search Classes" GridPane.columnIndex="1" GridPane.hgrow="NEVER" GridPane.rowIndex="1" GridPane.vgrow="NEVER" /> - <ListView fx:id="selectedCourses" prefHeight="114.0" prefWidth="454.0" GridPane.columnSpan="2" GridPane.rowIndex="3" /> - <Button fx:id="getSectionsButton" mnemonicParsing="false" onAction="#loadCourseSections" prefHeight="70.0" prefWidth="357.0" text="Get Sections" GridPane.hgrow="NEVER" GridPane.rowIndex="4" GridPane.vgrow="NEVER" /> - <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#removeSelectedCourse" prefHeight="72.0" prefWidth="172.0" text="Remove" GridPane.columnIndex="1" GridPane.hgrow="NEVER" GridPane.rowIndex="4" GridPane.vgrow="NEVER" /> - <ListView fx:id="sectionListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="5" /> - <Button mnemonicParsing="false" prefHeight="79.0" prefWidth="517.0" text="Select/Deselect All" GridPane.hgrow="NEVER" GridPane.rowIndex="6" GridPane.vgrow="NEVER" /> - <Button mnemonicParsing="false" prefHeight="43.0" prefWidth="248.0" text="Add Sections" GridPane.columnIndex="1" GridPane.hgrow="NEVER" GridPane.rowIndex="6" GridPane.vgrow="NEVER" /> + <TextField fx:id="searchField" onAction="#addSelectedCourse" prefHeight="25.0" prefWidth="262.0" promptText="Search" GridPane.columnSpan="2" GridPane.rowIndex="1" /> + <ComboBox fx:id="semesterComboBox" onAction="#switchSemester" prefHeight="33.0" prefWidth="469.0" promptText="Choose Semester" GridPane.columnSpan="2" GridPane.hgrow="ALWAYS" /> + <ListView fx:id="selectedCourses" prefHeight="114.0" prefWidth="454.0" GridPane.columnSpan="2" GridPane.rowIndex="4" /> + <Button fx:id="getSectionsButton" mnemonicParsing="false" onAction="#loadCourseSections" prefHeight="58.0" prefWidth="292.0" text="Get Sections" GridPane.hgrow="NEVER" GridPane.rowIndex="5" GridPane.vgrow="NEVER" /> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#removeSelectedCourse" prefHeight="72.0" prefWidth="172.0" text="Remove" GridPane.columnIndex="1" GridPane.hgrow="NEVER" GridPane.rowIndex="5" GridPane.vgrow="NEVER" /> + <ListView fx:id="sectionListView" prefHeight="200.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="6" /> + <Button mnemonicParsing="false" prefHeight="79.0" prefWidth="517.0" text="Select/Deselect All" GridPane.hgrow="NEVER" GridPane.rowIndex="7" GridPane.vgrow="NEVER" /> + <Button mnemonicParsing="false" onAction="#addSection" prefHeight="43.0" prefWidth="248.0" text="Add Sections" GridPane.columnIndex="1" GridPane.hgrow="NEVER" GridPane.rowIndex="7" GridPane.vgrow="NEVER" /> + <ListView fx:id="availableCourses" prefHeight="184.0" prefWidth="271.0" GridPane.columnSpan="2" GridPane.rowIndex="2" /> + <Button mnemonicParsing="false" onAction="#addSelectedCourse" prefHeight="26.0" prefWidth="703.0" text="Add Course" GridPane.columnSpan="2" GridPane.rowIndex="3" /> </children> <padding> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> @@ -70,14 +64,16 @@ <GridPane GridPane.columnIndex="1"> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> </columnConstraints> <rowConstraints> - <RowConstraints maxHeight="560.0" minHeight="10.0" prefHeight="520.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="560.0" minHeight="10.0" prefHeight="489.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="275.0" minHeight="10.0" prefHeight="44.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="275.0" minHeight="0.0" prefHeight="40.0" vgrow="SOMETIMES" /> </rowConstraints> <children> - <GridPane fx:id="scheduleGrid" gridLinesVisible="true" prefHeight="822.0" prefWidth="872.0" style="-fx-border-color: black;"> + <GridPane fx:id="scheduleGrid" gridLinesVisible="false" prefHeight="822.0" prefWidth="872.0" GridPane.columnSpan="2"> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> @@ -123,27 +119,42 @@ <Label text="7:00 PM" GridPane.halignment="CENTER" GridPane.rowIndex="12" GridPane.valignment="TOP" /> <Label text="8:00 PM" GridPane.halignment="CENTER" GridPane.rowIndex="13" GridPane.valignment="TOP" /> <Label text="9:00 PM" GridPane.halignment="CENTER" GridPane.rowIndex="14" GridPane.valignment="TOP" /> - <GridPane GridPane.rowIndex="1"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - </GridPane> + </children> </GridPane> - <GridPane GridPane.rowIndex="1"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> + <GridPane GridPane.columnSpan="2" GridPane.rowIndex="1"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="12.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <BorderPane prefHeight="200.0" prefWidth="200.0"> + <center> + <Button mnemonicParsing="false" onAction="#loadPrevSchedule" text="Previous" BorderPane.alignment="CENTER" /> + </center> + <padding> + <Insets left="100.0" /> + </padding> + </BorderPane> + <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2"> + <center> + <Button mnemonicParsing="false" onAction="#loadNextSchedule" text=" Next " BorderPane.alignment="CENTER"> + <BorderPane.margin> + <Insets right="100.0" /> + </BorderPane.margin> + </Button> + </center> + </BorderPane> + <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"> + <center> + <Label fx:id="scheduleLabel" alignment="CENTER" maxWidth="1.7976931348623157E308" text="##" BorderPane.alignment="CENTER" /> + </center> + </BorderPane> + </children> </GridPane> </children> </GridPane>