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 bb9d9d3d84834b8532f8aa3f216879622fff5798
parent b351e20ec739e7fbcd49e397de9f79e477e48db2
Author: jamisonv93 <47033011+jamisonv93@users.noreply.github.com>
Date:   Sun, 29 Mar 2020 10:58:26 -0400

Jamison/gui change (#46)

* refactor

* style guide

* refactor

* Add API package and interface

* Add API call method and Adapter

* Change which file TIME and DAY use

* change field in Semester class

* update UI design; remove generateSchedules bug; refactor

* merge sfar branch; add lib package with necessary jar file

* implement CRN pop up box

* implement show CRN pop up

* lib package and jar file

Diffstat:
MScheduleCreator/API/ApiAdapter.java | 4++--
MScheduleCreator/API/ApiInterface.java | 4++--
MScheduleCreator/Tests.java | 1-
MScheduleCreator/controllers/CoursesController.java | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
AScheduleCreator/lib/json-20190722.jar | 0
MScheduleCreator/models/Course.java | 30+++++++++---------------------
MScheduleCreator/models/Schedule.java | 7+++----
MScheduleCreator/models/Section.java | 13++++++++-----
MScheduleCreator/models/Semester.java | 162++++++++++++++++++++++++++++++-------------------------------------------------
MScheduleCreator/resources/views/select_courses.fxml | 54++++++++++++++++++++++++++++++++++++++----------------
10 files changed, 424 insertions(+), 299 deletions(-)

diff --git a/ScheduleCreator/API/ApiAdapter.java b/ScheduleCreator/API/ApiAdapter.java @@ -14,10 +14,10 @@ public class ApiAdapter implements ApiInterface { protected static final ApiInterface thisApi = new YesNoApi(); /** - * + * * @param _decision The decision the API returns can be forced to a specific choice * this is left blank here, so the API makes its own choice. - * @return + * @return */ @Override public String decisionForce(String _decision) { diff --git a/ScheduleCreator/API/ApiInterface.java b/ScheduleCreator/API/ApiInterface.java @@ -11,10 +11,10 @@ package ScheduleCreator.API; public interface ApiInterface { /** * The API has only one field that is also optional, the field is left blank. - * + * * @param _decision The decision the API returns can be forced to a specific choice * this is left blank here, so the API makes its own choice. - * @return + * @return */ public String decisionForce(String _decision); diff --git a/ScheduleCreator/Tests.java b/ScheduleCreator/Tests.java @@ -19,7 +19,6 @@ public class Tests { //regen databse Admin.regenDB(); - //test api call //ApiAdapter testAPI = new ApiAdapter(); //System.out.println("API returned "+testAPI.decisionForce("")); diff --git a/ScheduleCreator/controllers/CoursesController.java b/ScheduleCreator/controllers/CoursesController.java @@ -26,13 +26,20 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.geometry.VPos; import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.ListView; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.Tab; +import javafx.scene.control.TabPane; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.scene.shape.Rectangle; /** @@ -40,7 +47,7 @@ import javafx.scene.shape.Rectangle; * * @author Jamison Valentine, Ilyass Sfar, Nick Econopouly, Nathan Tolodzieki * - * Last Updated: 3/21/2020 + * Last Updated: 3/28/2020 */ public class CoursesController implements Initializable { @@ -59,25 +66,22 @@ public class CoursesController implements Initializable { @FXML protected TextField searchField; @FXML - protected GridPane scheduleGrid; + protected GridPane scheduleGridPane; @FXML - protected Label scheduleLabel; + protected Label scheduleLabel, onlineClassesLabel; + @FXML + protected TabPane sectionTabPane; + @FXML protected VBox CRNContainer, CRNPane; - // list of courses for current semester + // 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 Course currentCourse; protected int NUM_ROWS; protected int NUM_COLS; - protected double ROW_HEIGHT; - protected double COL_WIDTH; protected int currentScheduleIndex; BorderPane[][] grid; @@ -86,123 +90,181 @@ public class CoursesController implements Initializable { @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(); + this.loadSemesters(); + NUM_ROWS = scheduleGridPane.getRowConstraints().size(); + NUM_COLS = scheduleGridPane.getColumnConstraints().size(); grid = new BorderPane[NUM_ROWS][NUM_COLS]; - drawGrid(); + this.drawGrid(); + this.CRNPane.toFront(); } catch (IOException ex) { Logger.getLogger(CoursesController.class.getName()).log(Level.SEVERE, null, ex); } - } - public void addSelectedCourse(ActionEvent _event) throws Exception { + /** + * Called when "Add Course" button is clicked; Add selected course to the + * listview and saves in database. + * + * @param _event + */ + public void addSelectedCourse(ActionEvent _event) { if (this.availableCourses.getFocusModel().getFocusedItem() != null) { String selectedCourse = this.availableCourses.getFocusModel().getFocusedItem().toString(); - if (this.currentSemester.addCourse(selectedCourse)) { + Course newCourse = new Course(selectedCourse, this.currentSemester.getName()); + if (this.currentSemester.addCourse(newCourse)) { this.selectedCoursesListView.getItems().add(selectedCourse); this.currentSemester.generateSchedules(); + this.createNewTab(newCourse); } - regenerateSchedules(); + this.regenerateSchedules(); } } - public void switchSemester(ActionEvent _event) throws Exception { - String currentSemesterString = semesterComboBox.getValue(); + public void setSections(ActionEvent _event) { + + int index = this.sectionTabPane.getSelectionModel().getSelectedIndex(); + + Course course; + Tab tab; + for (int i = 0; i < this.sectionTabPane.getTabs().size(); i++) { + tab = this.sectionTabPane.getTabs().get(i); + course = this.currentSemester.getSelectedCourses().get(i); + List<Section> selected = new ArrayList(); + VBox container = (VBox)((ScrollPane) tab.getContent()).getContent(); + for (int j = 0; j < container.getChildren().size(); j++) { + VBox entry = (VBox) container.getChildren().get(j); + CheckBox checkBox = (CheckBox) entry.getChildren().get(0); + if (checkBox.isSelected()) { + selected.add(course.getSections().get(j)); + } + } + this.currentSemester.getSelectedSections().put(course, selected); + } + this.regenerateSchedules(); + } - switch (formatSemester(currentSemesterString)) { + /** + * Called when a different semester is chosen and loads appropriate info. + * + * @param _event + */ + public void switchSemester(ActionEvent _event) { - case "spring2020": - this.currentSemester = spring2020; - break; - case "summer2020": - this.currentSemester = summer2020; - break; - case "fall2020": - this.currentSemester = fall2020; - break; - } + String currentSemesterString = semesterComboBox.getValue(); + this.currentSemester = new Semester(semesterDirName(currentSemesterString)); - loadAllCourses(this.currentSemester.getName()); - loadSelectedCourses(this.currentSemester.getName()); + this.loadAllCourses(); + this.loadSelectedCourses(); + //loadSelectedSections(); + //Renders the first generated schedule if there is at least 1 selected course. if (this.currentSemester.getSelectedCourses().size() > 0) { - loadSchedule(this.currentSemester.getSchedules().get(0)); + this.loadSchedule(this.currentSemester.getSchedules().get(0)); } + } + + /** + * Formats given string to appropriate semester directory name + * + * @param _semester + * @return + */ + public String semesterDirName(String _semester) { + String[] temp = _semester.split(" "); + + String formattedSemester = temp[0].toLowerCase() + temp[1]; + + return formattedSemester; } - public void clearCalendar() { + /** + * Clears the current displayed schedule. + */ + public void clearScheduleGrid() { for (BorderPane entry : entries) { - scheduleGrid.getChildren().remove(entry); + scheduleGridPane.getChildren().remove(entry); } entries.clear(); } + /** + * Removes the selected course from the listview and the database. + * + * @param _event + */ // 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 { + public void removeSelectedCourse(ActionEvent _event) { if (this.selectedCoursesListView.getSelectionModel().getSelectedItem() != null) { - Object itemToRemove = this.selectedCoursesListView.getSelectionModel().getSelectedItem(); - this.selectedCoursesListView.getItems().remove(itemToRemove); - - String courseToDelete = ((String) itemToRemove).trim(); - this.currentSemester.removeCourse(courseToDelete); - - if (this.focusedCourse != null && this.focusedCourse.getFullText().equalsIgnoreCase(courseToDelete)) this.sectionListView.getItems().clear();; + int index = this.selectedCoursesListView.getSelectionModel().getSelectedIndex(); + this.sectionTabPane.getTabs().remove(index); + String courseToRemove = ((String) this.selectedCoursesListView.getSelectionModel().getSelectedItem()).trim(); + this.selectedCoursesListView.getItems().remove(courseToRemove); + this.currentSemester.removeCourse(courseToRemove); + + // Clears the section listview if the focus is on the course to be removed. + if (this.focusedCourse != null && this.focusedCourse.getFullText().equalsIgnoreCase(courseToRemove)) { + this.sectionListView.getItems().clear(); + } this.currentSemester.generateSchedules(); - regenerateSchedules(); + this.regenerateSchedules(); } - } + /** + * Generates all possible schedules consisting of selected sections. + */ public void regenerateSchedules() { this.currentSemester.generateSchedules(); - clearCalendar(); + this.clearScheduleGrid(); if (this.currentSemester.getNumberOfSchedules() == 0) { scheduleLabel.setText("0/0"); } else if (this.currentSemester.getNumberOfSchedules() > 0) { - loadSchedule(this.currentSemester.getSchedules().get(0)); + this.loadSchedule(this.currentSemester.getSchedules().get(0)); scheduleLabel.setText("1/" + this.currentSemester.getNumberOfSchedules()); } - } + /** + * Gets sections for a selected course and adds them to the sections + * listview. + * + * @param _event + */ public void loadCourseSections(ActionEvent _event) { - List<Section> courseSections = new ArrayList(); + //Do nothing if no course has been selected. + if (this.selectedCoursesListView.getSelectionModel().getSelectedItem() == null) { + return; + } - if (this.selectedCoursesListView.getSelectionModel().getSelectedItem() != null) { - String currentSelection = this.selectedCoursesListView.getSelectionModel().getSelectedItem().toString(); + String currentSelection = this.selectedCoursesListView.getSelectionModel().getSelectedItem().toString(); - for (Course course : this.currentSemester.getSelectedCourses()) { - if (course.getFullText().equals(currentSelection)) { + //Get the corresponding course to reference for sections + for (Course course : this.currentSemester.getSelectedCourses()) { + if (course.getFullText().equals(currentSelection)) { - this.focusedCourse = course; - courseSections = course.getSections(); - break; - } + this.focusedCourse = course; + break; } + } - List<String> listCellLabels = new ArrayList(); - - for (Section section : courseSections) { - listCellLabels.add(section.toString()); - } + List<String> listCellLabels = new ArrayList(); - this.sectionListView.setItems(FXCollections.observableList(listCellLabels)); + for (Section section : this.focusedCourse.getSections()) { + listCellLabels.add(section.toString()); } + this.sectionListView.setItems(FXCollections.observableList(listCellLabels)); + } - public void loadAllCourses(String _semester) throws Exception { + public void loadAllCourses() { // intermediary ObservableList of the courses ObservableList<String> OList = FXCollections.observableList(this.currentSemester.getAllCourses()); @@ -262,7 +324,6 @@ public class CoursesController implements Initializable { this.courseList.setPredicate(s -> s.toLowerCase().contains(filter)); } }); - } public void loadSemesters() throws IOException { @@ -285,50 +346,150 @@ public class CoursesController implements Initializable { this.semesterComboBox.setItems(FXCollections.observableList(newList)); } - public void loadSelectedCourses(String _semester) throws Exception { - List<String> courses = Translator.getSelectedCourses(_semester); - this.selectedCoursesListView.setItems(FXCollections.observableList(courses)); - regenerateSchedules(); + public void loadSelectedCourses() { + this.sectionTabPane.getTabs().clear(); + this.selectedCoursesListView.setItems(FXCollections.observableList(this.currentSemester.getSelectedCourseStrings())); + for (Course course : this.currentSemester.getSelectedCourses()) { + this.createNewTab(course); + } + + this.regenerateSchedules(); } - public String formatSemester(String _semester) { - //Format current semester to pass as argument in appropriate Translator methods + /** + * Creates new tab that display associated course sections. + * @param _course + */ + public void createNewTab(Course _course) { + Tab tab = new Tab(); + tab.setText(_course.getID()); + VBox content = new VBox(); + ScrollPane pane = new ScrollPane(); + for (Section section : _course.getSections()) { + VBox sectionEntry = new VBox(); + sectionEntry.setPrefHeight(30); + sectionEntry.setMinHeight(30); + VBox.setVgrow(content, Priority.NEVER); + sectionEntry.setAlignment(Pos.CENTER_LEFT); + sectionEntry.setStyle("-fx-border-color: grey; -fx-border-width: 0 0 .5 0;"); + CheckBox checkBox = new CheckBox(); + checkBox.setSelected(true); + VBox.setMargin(checkBox, new Insets(0, 0, 0, 10)); + checkBox.setText(section.toString()); + sectionEntry.getChildren().add(checkBox); + content.getChildren().add(sectionEntry); + } + pane.setContent(content); + tab.setContent(pane); + this.sectionTabPane.getTabs().add(tab); + } - String[] temp = _semester.split(" "); + /** + * If there are no selections, force select all; + * if there are any selections, unselect all of them. + * + * @param _event + */ + public void selectAll(ActionEvent _event) { + if (this.sectionTabPane.getSelectionModel().getSelectedItem() == null) return; + int index = this.sectionTabPane.getSelectionModel().getSelectedIndex(); + Tab currentTab = this.sectionTabPane.getTabs().get(index); + + if (this.allUnselected(currentTab)) setSelectAll(true, currentTab); + else setSelectAll(false, currentTab); + } - String formattedSemester = temp[0].toLowerCase() + temp[1]; + /** + * + * @param _tab + */ + public boolean allUnselected(Tab _tab) { + + VBox container = (VBox)((ScrollPane) _tab.getContent()).getContent(); + for (int j = 0; j < container.getChildren().size(); j++) { + VBox entry = (VBox) container.getChildren().get(j); + CheckBox checkBox = (CheckBox) entry.getChildren().get(0); + if (checkBox.isSelected()) { + return false; + } + } + return true; + } - return formattedSemester; + public void setSelectAll(boolean _option, Tab _tab) { + + VBox container = (VBox)((ScrollPane) _tab.getContent()).getContent(); + for (int j = 0; j < container.getChildren().size(); j++) { + VBox entry = (VBox) container.getChildren().get(j); + CheckBox checkBox = (CheckBox) entry.getChildren().get(0); + checkBox.setSelected(_option); + } } public void drawGrid() { - for (int i = 1; i <= NUM_ROWS - 1; i++) { - for (int j = 1; j <= NUM_COLS - 1; j++) { + for (int i = 1; i < NUM_ROWS; i++) { + for (int j = 1; j < NUM_COLS; j++) { BorderPane region = new BorderPane(); region.setStyle(("-fx-border-color: black; -fx-border-width: .5;")); grid[i][j] = region; - scheduleGrid.add(region, j, i); - + scheduleGridPane.add(region, j, i); } } + } + public void showCRNs(ActionEvent _event) { + this.CRNContainer.getChildren().clear(); + for (Section section : this.currentSemester.getSchedules().get(this.currentScheduleIndex).getAddedSections()) { + this.CRNContainer.getChildren().add(new Label(section.getCRN())); + } + + this.CRNPane.setVisible(true); + this.scheduleGridPane.toBack(); + this.CRNPane.toFront(); } - public void addSection(ActionEvent _event) { -// if (this.focusedCourse != null) { -// int secIndex = this.sectionListView.getSelectionModel().getSelectedIndex(); -// Section focusedSection = this.focusedCourse.getSections().get(secIndex); -// this.currentSemester.addSelectedSection(focusedCourse, focusedSection); -// this.currentSemester.generateSchedules(); -// loadSchedule(this.currentSemester.getSchedules().get(0)); -// } + public void hideCRNs() { + this.CRNPane.setVisible(false); } public void addEntry(Section _section, int _numberOfCampusCourses) { + String color = assignColor(_numberOfCampusCourses); + + int row = (int) _section.getStartTime() / 100 - 7; + double topMargin = (_section.getStartTime() % 100) / 60; + for (Integer col : getDays(_section)) { + Label label = new Label(_section.getCourseID() + " - " + _section.getSectionNumber()); + BorderPane entryContainer = new BorderPane(); + entryContainer.paddingProperty().set(new Insets(grid[row][col].heightProperty().multiply(topMargin).doubleValue(), 0, 0, 0)); + StackPane pane = new StackPane(); + + Rectangle rect = new Rectangle(); + rect.setStyle("-fx-fill:" + color + "; -fx-stroke: black; -fx-stroke-line-cap: round; -fx-arc-height: 10; -fx-arc-width: 10;"); + label.setAlignment(Pos.CENTER); + + pane.setStyle(""); + pane.getChildren().addAll(rect, label); + entryContainer.setTop(pane); + + scheduleGridPane.getChildren().add(entryContainer); + GridPane.setConstraints(entryContainer, col, row, 1, GridPane.REMAINING, HPos.CENTER, VPos.TOP); + BorderPane region = grid[row][col]; + rect.heightProperty().bind(region.heightProperty().subtract(2).multiply(_section.getDurationHours())); + rect.widthProperty().bind(region.widthProperty().subtract(2)); + entries.add(entryContainer); + } + } + /** + * Returns a list of meeting days for particular section. + * + * @param _section + * @return + */ + public List<Integer> getDays(Section _section) { char[] daysString = _section.getDays().toCharArray(); ArrayList<Integer> days = new ArrayList(); for (char day : daysString) { @@ -350,79 +511,71 @@ public class CoursesController implements Initializable { break; } + } + return days; + } - String color = ""; - switch (_numberOfCampusCourses) { - case 1: - //green - color = "#ccffcc"; - break; - case 2: - //blue - color = "#b3e1ff"; - break; - case 3: - //red - color = "#ffb3b3 "; - break; - case 4: - //yellow - color = "#e6e600"; - break; - case 5: - //orange - color = "#ffda75"; - break; - case 6: - color = "#ff6666"; - break; - - default: - color = "lightblue"; - - } - - int row = (int) _section.getStartTime() / 100 - 7; - double topMargin = (_section.getStartTime() % 100 ) / 60; - for (Integer col : days) { - Label label = new Label(_section.getCourseID() + " - " + _section.getSectionNumber()); - BorderPane entryContainer = new BorderPane(); - entryContainer.paddingProperty().set(new Insets(grid[row][col].heightProperty().multiply(topMargin).doubleValue(), 0, 0, 0)); - StackPane pane = new StackPane(); - - Rectangle rect = new Rectangle(); - rect.setStyle("-fx-fill:" + color + "; -fx-stroke: black; -fx-stroke-line-cap: round; -fx-arc-height: 10; -fx-arc-width: 10;"); - label.setAlignment(Pos.CENTER); - - pane.setStyle(""); - pane.getChildren().addAll(rect, label); - entryContainer.setTop(pane); - - scheduleGrid.getChildren().add(entryContainer); - GridPane.setConstraints(entryContainer, col, row, 1, GridPane.REMAINING, HPos.CENTER, VPos.TOP); - BorderPane region = grid[row][col]; - rect.heightProperty().bind(region.heightProperty().subtract(2).multiply(_section.getDurationHours())); - rect.widthProperty().bind(region.widthProperty().subtract(2)); - entries.add(entryContainer); - - } + /** + * Returns a different color for the next course on the grid. + * + * @param _numberOfCampusCourses + * @return + */ + public String assignColor(int _numberOfCampusCourses) { + String color = ""; + switch (_numberOfCampusCourses) { + case 1: + //green + color = "#ccffcc"; + break; + case 2: + //blue + color = "#b3e1ff"; + break; + case 3: + //red + color = "#ffb3b3 "; + break; + case 4: + //yellow + color = "#e6e600"; + break; + case 5: + //orange + color = "#ffda75"; + break; + case 6: + color = "#ff6666"; + break; + default: + color = "lightblue"; } + + return color; } public void loadSchedule(Schedule _schedule) { - clearCalendar(); + this.hideCRNs(); + clearScheduleGrid(); int numberOfCampusCourses = 0; + int onlineCourses = 0; + StringBuilder label = new StringBuilder("Online Classes: "); for (Section section : _schedule.getAddedSections()) { if (!section.isOnline()) { addEntry(section, ++numberOfCampusCourses); } + else { + if (onlineCourses >= 1) label.append(" | "); + label.append(section.getID()); + onlineCourses++; + } } scheduleLabel.setText(this.currentScheduleIndex + 1 + "/" + this.currentSemester.getNumberOfSchedules()); + onlineClassesLabel.setText(label.toString()); } public void loadNextSchedule(ActionEvent _event) { - if (this.currentSemester != null) { if (this.currentScheduleIndex < this.currentSemester.getSchedules().size() - 1) { this.currentScheduleIndex++; @@ -432,7 +585,6 @@ public class CoursesController implements Initializable { } public void loadPrevSchedule(ActionEvent _event) { - if (this.currentScheduleIndex > 0) { this.currentScheduleIndex--; loadSchedule(this.currentSemester.getSchedules().get(this.currentScheduleIndex)); diff --git a/ScheduleCreator/lib/json-20190722.jar b/ScheduleCreator/lib/json-20190722.jar Binary files differ. diff --git a/ScheduleCreator/models/Course.java b/ScheduleCreator/models/Course.java @@ -5,7 +5,7 @@ package ScheduleCreator.models; * * @author Jamison Valentine * - * Last Updated: 3/17/2020 + * Last Updated: 3/27/2020 */ import ScheduleCreator.Translator; @@ -27,20 +27,23 @@ public class Course { this.courseNumber = _name.substring(4, 7); this.abbreviation = _name.substring(0, 4); this.id = this.abbreviation + this.courseNumber; - loadSectionsFromFile( _semester); + this.loadSectionsFromFile( _semester); + } + + //================= GETTERS =============== + + public String getID() { + return this.id; } public String getFullText() { return this.fullText; - } public String getabbreviation() { return abbreviation; } - //================= GETTERS =============== - public String getCourseNumber() { return courseNumber; } @@ -49,7 +52,7 @@ public class Course { return this.sections; } - public void loadSectionsFromFile(String _semester) { + private void loadSectionsFromFile(String _semester) { List<String> sectionStrings = Translator.getSections(this.id, _semester); this.sections = new ArrayList(); Pattern p = Pattern.compile(".*([0-9]{5}).*- ([0-9]{2})\\s*(\\S* [ap]m - \\S* [ap]m)\\s*(\\S*)(.*)=(.*)"); @@ -74,13 +77,6 @@ public class Course { daysAndTimes = m.group(4).trim() + " " + m.group(3).trim(); location = m.group(5).trim(); instructor = m.group(6).trim(); -// System.out.println("CRN: " + CRN); -// System.out.println("Section #: " + sectionNumber); -// System.out.println("Times: " + daysAndTimes); -// System.out.println("Days: " + location); -// System.out.println("Location: " + location); -// System.out.println("Instructor: " + instructor); -// System.out.println("This is not an online course"); Section newSection = new Section(this.id, sectionNumber, daysAndTimes, location, instructor, CRN, false); this.sections.add(newSection); @@ -89,17 +85,9 @@ public class Course { CRN = m2.group(1).trim(); sectionNumber = m2.group(2).trim(); instructor = m2.group(4).trim(); -// System.out.println("CRN: " + CRN); -// System.out.println("Section #: " + sectionNumber); -// System.out.println("Online class"); -// System.out.println("Instructor: " + instructor); 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,7 +1,6 @@ package ScheduleCreator.models; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -9,7 +8,7 @@ import java.util.List; * * @author Jamison Valentine * - * Last Updated: 3/31/2020 + * Last Updated: 3/28/2020 */ public class Schedule { @@ -35,10 +34,10 @@ public class Schedule { } if (sameDay) { - if (existingSection.endTime >= _newSection.startTime && existingSection.startTime < _newSection.startTime) { + if (existingSection.endTime >= _newSection.startTime && existingSection.startTime <= _newSection.startTime) { return false; } - if (existingSection.startTime <= _newSection.endTime && existingSection.endTime > _newSection.endTime) { + if (existingSection.startTime <= _newSection.endTime && existingSection.endTime >= _newSection.endTime) { return false; } } diff --git a/ScheduleCreator/models/Section.java b/ScheduleCreator/models/Section.java @@ -7,11 +7,12 @@ import java.util.Scanner; * * @author Jamison Valentine * - * Last Updated: 3/21/2020 + * Last Updated: 3/27/2020 */ public class Section { protected final String courseID; + protected final String id; protected final String location; protected final String instructor; protected final String daysAndTimes; @@ -29,15 +30,17 @@ public class Section { this.daysAndTimes = _daysAndTimes; this.CRN = _CRN; this.sectionNumber = _sectionNumber; + this.id = _courseID + "-" + _sectionNumber; this.isOnline = _isOnline; setTimes(); } - /** - * - * @param _daysAndTimes is a string similar to 11:00 am - 12:15 pm - */ + //================= GETTERS =============== + public String getID() { + return this.id; + } + public double getDurationHours() { double difference = this.endTime - this.startTime; diff --git a/ScheduleCreator/models/Semester.java b/ScheduleCreator/models/Semester.java @@ -2,95 +2,57 @@ package ScheduleCreator.models; import ScheduleCreator.Translator; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; /** * This class models a semester, which is a collection of sections. * - * Can be a literal real-world semester, or another collection of sections (like courses the user saves for later). - * * @author Nick Econopouly, Jamison Valentine * - * Last Updated: 3/31/2020 + * Last Updated: 3/28/2020 */ - -public class Semester { +public class Semester { protected final String name; protected final List<String> allCourses; protected List<Schedule> schedules; - protected HashMap<Course,List<Section>> selectedSections; - protected int numberOfSchedules; - /** - * Methods to implement - * clearCalendar(); - * clearSelectedSection(); - */ - - - /** - * - * @param _name - */ + protected LinkedHashMap<Course, List<Section>> selectedSections; + + public Semester(String _name) { this.name = _name; this.allCourses = Translator.getCourses(this.name); this.schedules = new ArrayList(); - this.selectedSections = new HashMap(); - loadSelectedCoursesFromFile(); + this.selectedSections = new LinkedHashMap(); + this.loadSelectedCoursesFromFile(); + this.generateSchedules(); } - //WORK IN PROGRESS 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 { + } else { List<Section> sectionList = this.selectedSections.get(_course); if (!sectionList.contains(_section)) { - sectionList.add(_section) ; + sectionList.add(_section); } } - } - public HashMap<Course,List<Section>> getSelectedSections() { + public LinkedHashMap<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.) - // and only after all sections have been imported - } - - public Boolean addCourse(String _course) { - Boolean contains = false; - - for (Course course: this.selectedSections.keySet()) { - - if (course.getFullText().equalsIgnoreCase(_course)) { - - contains = true; - break; - } - } - - if (!contains) { - - this.selectedSections.put(new Course(_course, this.name), new ArrayList<Section>()); - - Translator.saveCourse(_course, this.name); + public Boolean addCourse(Course _course) { + if (!this.selectedSections.keySet().contains(_course)) { + this.selectedSections.put(_course, _course.getSections()); + Translator.saveCourse(_course.getFullText(), this.name); return true; - - } else { - return false; } - + return false; } public List<Schedule> getSchedules() { @@ -98,9 +60,8 @@ public class Semester { } public void generateSchedules() { - List<Schedule> list = generateSchedules(new ArrayList<Course>(this.selectedSections.keySet())); + List<Schedule> list = generateSchedules(new ArrayList(this.selectedSections.keySet())); this.schedules = list; - this.numberOfSchedules = this.schedules.size(); } private List<Schedule> generateSchedules(List<Course> selectedCourses) { @@ -108,34 +69,38 @@ public class Semester { List<Schedule> validSchedules = new ArrayList(); //if there are no remaining, return an empty list - if (selectedCourses.isEmpty()) return validSchedules; + if (selectedCourses.isEmpty()) { + return validSchedules; + } - //Select first course in the remaining course. + //Select first course in the remaining list of coursec. 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 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()) { + for (Section section : this.selectedSections.get(course)) { Schedule newSchedule = new Schedule(); newSchedule.addSection(section); validSchedules.add(newSchedule); } - } - - //If there is more than one course in the list + } //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 (Section section : this.selectedSections.get(course)) { for (Schedule schedule : generateSchedules(remainingCourses)) { - if (schedule.addSection(section)) validSchedules.add(schedule); + if (schedule.addSection(section)) { + validSchedules.add(schedule); + } } } } @@ -143,45 +108,21 @@ public class Semester { return validSchedules; } -// ============================== Getters ============================ - - public String getName() { - return this.name; - } - - public int getNumberOfSchedules() { - return this.numberOfSchedules; - } - - public List<String> getAllCourses() { - return this.allCourses; - } - - //WORK IN PROGRESS - public List<Section> getAvailableSections(Course _course) { - ArrayList<Section> list = new ArrayList(); - return list; - } - - public void loadSelectedCoursesFromFile() { + private void loadSelectedCoursesFromFile() { List<String> list = Translator.getSelectedCourses(this.name); if (!list.isEmpty()) { - for (String courseName: list) { - this.selectedSections.put(new Course(courseName, this.name), new ArrayList<Section>()); + for (String courseName : list) { + Course course = new Course(courseName, this.name); + this.selectedSections.put(new Course(courseName, this.name), course.getSections()); } } - } - public void removeCourse(String _course) throws Exception { - Course courseToRemove; - - for (Course course: this.selectedSections.keySet()) { + public void removeCourse(String _course) { + for (Course course : this.selectedSections.keySet()) { if (_course.equalsIgnoreCase(course.getFullText())) { - - courseToRemove = course; this.selectedSections.remove(course); break; } @@ -190,8 +131,30 @@ public class Semester { Translator.removeCourse(_course, this.name); } + @Override + public boolean equals(Object _obj) { + Semester otherSemester = (Semester) _obj; + return this.name.equalsIgnoreCase(otherSemester.getName()); + } + +// ============================== Getters ============================ + public String getName() { + return this.name; + } + + public int getNumberOfSchedules() { + return this.schedules.size(); + } + + public List<String> getAllCourses() { + return this.allCourses; + } + public List<Course> getSelectedCourses() { - return new ArrayList<Course>(this.selectedSections.keySet()); + return new ArrayList(this.selectedSections.keySet()); } -}- \ No newline at end of file + public List<String> getSelectedCourseStrings() { + return Translator.getSelectedCourses(this.name); + } +} diff --git a/ScheduleCreator/resources/views/select_courses.fxml b/ScheduleCreator/resources/views/select_courses.fxml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> +<?import javafx.scene.text.*?> <?import javafx.scene.canvas.*?> <?import javafx.scene.*?> <?import javafx.scene.shape.*?> @@ -38,13 +39,16 @@ <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="26.0" prefWidth="138.0" promptText="Choose Semester" GridPane.columnSpan="2" GridPane.hgrow="ALWAYS" /> <ListView fx:id="selectedCoursesListView" maxHeight="1.7976931348623157E308" prefHeight="114.0" prefWidth="454.0" GridPane.columnSpan="2" GridPane.rowIndex="4" /> - <Button fx:id="getSectionsButton" maxWidth="1.7976931348623157E308" 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" /> + <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#removeSelectedCourse" prefHeight="72.0" prefWidth="172.0" text="Remove" GridPane.columnSpan="2" GridPane.hgrow="NEVER" GridPane.rowIndex="5" GridPane.vgrow="NEVER" /> + <Button mnemonicParsing="false" onAction="#selectAll" prefHeight="79.0" prefWidth="517.0" text="Select/Deselect All" GridPane.hgrow="NEVER" GridPane.rowIndex="7" GridPane.vgrow="NEVER" /> + <Button fx:id="sectionsButton" mnemonicParsing="false" onAction="#setSections" prefHeight="43.0" prefWidth="248.0" text="Submit" 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" /> + <TabPane fx:id="sectionTabPane" prefHeight="200.0" prefWidth="200.0" style="-fx-border-color: grey;" tabClosingPolicy="UNAVAILABLE" GridPane.columnSpan="2" GridPane.rowIndex="6"> + <tabs> + + </tabs> + </TabPane> </children> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" maxWidth="469.0" minWidth="10.0" prefWidth="289.0" /> @@ -56,17 +60,17 @@ <rowConstraints> <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 minHeight="10.0" prefHeight="134.0" vgrow="ALWAYS" /> - <RowConstraints maxHeight="375.0" minHeight="0.0" prefHeight="30.0" /> - <RowConstraints minHeight="10.0" prefHeight="122.0" vgrow="ALWAYS" /> - <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="30.0" /> + <RowConstraints maxHeight="123.0" minHeight="10.0" prefHeight="105.0" vgrow="ALWAYS" /> + <RowConstraints maxHeight="375.0" minHeight="0.0" prefHeight="30.0" vgrow="NEVER" /> + <RowConstraints maxHeight="132.0" minHeight="10.0" prefHeight="94.0" vgrow="ALWAYS" /> + <RowConstraints fillHeight="false" maxHeight="30.0" minHeight="10.0" prefHeight="30.0" vgrow="NEVER" /> <RowConstraints minHeight="10.0" prefHeight="112.0" vgrow="ALWAYS" /> <RowConstraints maxHeight="305.0" minHeight="10.0" prefHeight="30.0" /> </rowConstraints> </GridPane> <GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" GridPane.columnIndex="2"> <children> - <GridPane fx:id="scheduleGrid" gridLinesVisible="false" maxHeight="1.7976931348623157E308" maxWidth="1050.0" prefHeight="822.0" prefWidth="872.0" GridPane.columnSpan="2" GridPane.vgrow="ALWAYS"> + <GridPane fx:id="scheduleGridPane" gridLinesVisible="false" maxHeight="1.7976931348623157E308" maxWidth="1050.0" prefHeight="822.0" prefWidth="872.0" GridPane.columnSpan="2" GridPane.vgrow="ALWAYS"> <children> <Label text="Monday" GridPane.columnIndex="1" GridPane.halignment="CENTER" /> <Label text="Tuesday" GridPane.columnIndex="2" GridPane.halignment="CENTER" /> @@ -87,6 +91,21 @@ <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" /> + <VBox fx:id="CRNPane" minHeight="-Infinity" minWidth="-Infinity" prefHeight="96.0" prefWidth="287.0" style="-fx-background-color: white;" visible="false" GridPane.columnIndex="2" GridPane.columnSpan="3" GridPane.hgrow="NEVER" GridPane.rowIndex="6" GridPane.rowSpan="4" GridPane.vgrow="NEVER"> + <children> + <ToolBar prefHeight="35.0" prefWidth="314.0"> + <items> + <Button mnemonicParsing="false" onAction="#hideCRNs" text="Close" /> + </items> + </ToolBar> + <VBox fx:id="CRNContainer" alignment="CENTER" prefHeight="83.0" prefWidth="340.0"> + <children> + <Label alignment="CENTER" text="First" /> + <Label text="First" /> + </children> + </VBox> + </children> + </VBox> </children> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> @@ -116,7 +135,7 @@ </GridPane> <GridPane GridPane.columnSpan="2" GridPane.rowIndex="1"> <children> - <BorderPane prefHeight="200.0" prefWidth="200.0"> + <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.rowIndex="1"> <center> <Button mnemonicParsing="false" onAction="#loadPrevSchedule" text="Previous" BorderPane.alignment="CENTER" /> </center> @@ -124,7 +143,7 @@ <Insets left="100.0" /> </padding> </BorderPane> - <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2"> + <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="2" GridPane.rowIndex="1"> <center> <Button mnemonicParsing="false" onAction="#loadNextSchedule" text=" Next " BorderPane.alignment="CENTER"> <BorderPane.margin> @@ -133,11 +152,12 @@ </Button> </center> </BorderPane> - <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1"> + <BorderPane prefHeight="200.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1"> <center> <Label fx:id="scheduleLabel" alignment="CENTER" maxWidth="1.7976931348623157E308" text="##" BorderPane.alignment="CENTER" /> </center> </BorderPane> + <Label fx:id="onlineClassesLabel" text="Online Classes: #" textFill="#da0e0e" GridPane.columnIndex="1" GridPane.columnSpan="2" /> </children> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> @@ -145,9 +165,11 @@ <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" /> </columnConstraints> <rowConstraints> - <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="22.0" minHeight="10.0" prefHeight="16.66668701171875" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="27.33331298828125" minHeight="10.0" prefHeight="27.33331298828125" vgrow="SOMETIMES" /> </rowConstraints> </GridPane> + <Button mnemonicParsing="false" onAction="#showCRNs" text="Show CRNs" GridPane.columnSpan="2" GridPane.halignment="CENTER" GridPane.rowIndex="2" /> </children> <columnConstraints> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> @@ -155,8 +177,8 @@ </columnConstraints> <rowConstraints> <RowConstraints fx:id="heightRestricted" minHeight="10.0" prefHeight="489.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="44.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="0.0" prefHeight="40.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="57.3333740234375" minHeight="10.0" prefHeight="57.3333740234375" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="32.0" minHeight="0.0" prefHeight="26.6666259765625" vgrow="SOMETIMES" /> </rowConstraints> </GridPane> </children>