|
5 | 5 | import edu.rpi.aris.net.NetUtil; |
6 | 6 | import edu.rpi.aris.net.client.Client; |
7 | 7 | import javafx.application.Platform; |
| 8 | +import javafx.beans.binding.Bindings; |
8 | 9 | import javafx.beans.property.SimpleBooleanProperty; |
| 10 | +import javafx.beans.property.SimpleLongProperty; |
9 | 11 | import javafx.beans.property.SimpleObjectProperty; |
10 | | -import javafx.scene.control.TitledPane; |
11 | | -import javafx.scene.control.TreeItem; |
12 | | -import javafx.scene.control.TreeTableColumn; |
13 | | -import javafx.scene.control.TreeTableView; |
| 12 | +import javafx.beans.property.SimpleStringProperty; |
| 13 | +import javafx.scene.control.*; |
| 14 | +import javafx.scene.layout.HBox; |
14 | 15 | import javafx.scene.layout.VBox; |
| 16 | +import javafx.stage.Modality; |
| 17 | +import org.apache.commons.lang3.tuple.Triple; |
15 | 18 | import org.apache.logging.log4j.LogManager; |
16 | 19 | import org.apache.logging.log4j.Logger; |
17 | 20 |
|
18 | 21 | import java.io.IOException; |
19 | 22 | import java.net.URLDecoder; |
| 23 | +import java.net.URLEncoder; |
20 | 24 | import java.text.DateFormat; |
21 | 25 | import java.text.ParseException; |
| 26 | +import java.time.LocalDateTime; |
| 27 | +import java.time.ZoneId; |
22 | 28 | import java.util.*; |
23 | 29 |
|
24 | 30 | public class Assignment { |
25 | 31 |
|
26 | 32 | private static Logger logger = LogManager.getLogger(Assignment.class); |
27 | | - private final String assignedBy; |
28 | 33 | private final int id, classId; |
29 | | - private long dueDate; |
30 | | - private String name; |
| 34 | + private final Course course; |
| 35 | + private SimpleLongProperty dueDate = new SimpleLongProperty(); |
| 36 | + private SimpleStringProperty name = new SimpleStringProperty(); |
31 | 37 | private TitledPane titledPane; |
32 | | - private VBox tableBox = new VBox(); |
| 38 | + private VBox tableBox = new VBox(5); |
33 | 39 | private SimpleBooleanProperty loaded = new SimpleBooleanProperty(false); |
34 | 40 | private ArrayList<AssignmentInfo> rootNodes = new ArrayList<>(); |
| 41 | + private HashSet<ProofInfo> proofs = new HashSet<>(); |
35 | 42 |
|
36 | | - public Assignment(String name, String dueDate, String assignedBy, int id, int classId) { |
37 | | - this.name = name; |
| 43 | + public Assignment(String name, String dueDate, String assignedBy, int id, int classId, Course course) { |
| 44 | + this.course = course; |
| 45 | + this.name.set(name); |
38 | 46 | try { |
39 | | - this.dueDate = NetUtil.DATE_FORMAT.parse(dueDate).getTime(); |
| 47 | + this.dueDate.set(NetUtil.DATE_FORMAT.parse(dueDate).getTime()); |
40 | 48 | } catch (ParseException e) { |
41 | 49 | logger.error("Failed to parse due date", e); |
42 | | - this.dueDate = -1; |
| 50 | + this.dueDate.set(-1); |
43 | 51 | } |
44 | | - this.assignedBy = assignedBy; |
45 | 52 | this.id = id; |
46 | 53 | this.classId = classId; |
47 | | - String dateStr = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date(this.dueDate)); |
48 | | - titledPane = new TitledPane(name + "\nDue: " + dateStr + "\nAssigned by: " + assignedBy, tableBox); |
| 54 | + titledPane = new TitledPane("", tableBox); |
| 55 | + titledPane.textProperty().bind(Bindings.createStringBinding(() -> { |
| 56 | + String dateStr = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date(Assignment.this.dueDate.get())); |
| 57 | + return Assignment.this.name.get() + "\nDue: " + dateStr + "\nAssigned by: " + assignedBy; |
| 58 | + }, this.name, this.dueDate)); |
49 | 59 | titledPane.expandedProperty().addListener((observableValue, oldVal, newVal) -> { |
50 | 60 | if (newVal) |
51 | 61 | load(false); |
@@ -111,9 +121,107 @@ private void buildUI() { |
111 | 121 | view.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY); |
112 | 122 | view.setEditable(false); |
113 | 123 | root.setExpanded(true); |
| 124 | + if (AssignmentWindow.instance.getClientInfo().isInstructorProperty().get()) { |
| 125 | + Button editAssignment = new Button("Edit Assignment"); |
| 126 | + Button deleteAssignment = new Button("Delete Assignment"); |
| 127 | + editAssignment.setOnAction(action -> editAssignment()); |
| 128 | + deleteAssignment.setOnAction(action -> deleteAssignment()); |
| 129 | + HBox box = new HBox(5); |
| 130 | + box.getChildren().addAll(editAssignment, deleteAssignment); |
| 131 | + tableBox.getChildren().add(box); |
| 132 | + } |
114 | 133 | tableBox.getChildren().add(view); |
115 | 134 | } |
116 | 135 |
|
| 136 | + private void deleteAssignment() { |
| 137 | + Alert alert = new Alert(Alert.AlertType.WARNING); |
| 138 | + alert.initModality(Modality.WINDOW_MODAL); |
| 139 | + alert.initOwner(AssignmentWindow.instance.getStage()); |
| 140 | + alert.setTitle("Delete Assignment?"); |
| 141 | + alert.setHeaderText("Are you sure you want to delete this assignment?"); |
| 142 | + alert.setContentText("This will also delete any student submissions for this assignment"); |
| 143 | + alert.getButtonTypes().setAll(ButtonType.YES, ButtonType.NO); |
| 144 | + Optional<ButtonType> result = alert.showAndWait(); |
| 145 | + result.ifPresent(type -> { |
| 146 | + if (type != ButtonType.YES) |
| 147 | + return; |
| 148 | + new Thread(() -> { |
| 149 | + Client client = Main.getClient(); |
| 150 | + try { |
| 151 | + client.connect(); |
| 152 | + client.sendMessage(NetUtil.DELETE_ASSIGNMENT); |
| 153 | + client.sendMessage(classId + "|" + id); |
| 154 | + String res = client.readMessage(); |
| 155 | + if (!NetUtil.OK.equals(Client.checkError(res))) |
| 156 | + throw new IOException(res); |
| 157 | + AssignmentWindow.instance.loadAssignments(course, true); |
| 158 | + } catch (IOException e) { |
| 159 | + e.printStackTrace(); |
| 160 | + } finally { |
| 161 | + client.disconnect(); |
| 162 | + } |
| 163 | + }).start(); |
| 164 | + }); |
| 165 | + } |
| 166 | + |
| 167 | + private void editAssignment() { |
| 168 | + ProofList proofList = AssignmentWindow.instance.getProofList(); |
| 169 | + proofList.load(false); |
| 170 | + try { |
| 171 | + AssignmentDialog dialog = new AssignmentDialog(AssignmentWindow.instance.getStage(), proofList, name.get(), new Date(dueDate.get()), proofs); |
| 172 | + Optional<Triple<String, LocalDateTime, Collection<ProofInfo>>> result = dialog.showAndWait(); |
| 173 | + if (!result.isPresent()) |
| 174 | + return; |
| 175 | + Triple<String, LocalDateTime, Collection<ProofInfo>> info = result.get(); |
| 176 | + String newName = info.getLeft().equals(name.get()) ? null : info.getLeft(); |
| 177 | + Date tmpDate = Date.from(info.getMiddle().atZone(ZoneId.systemDefault()).toInstant()); |
| 178 | + Date newDate = tmpDate.equals(new Date(dueDate.get())) ? null : tmpDate; |
| 179 | + Collection<ProofInfo> newProofs = info.getRight(); |
| 180 | + Set<ProofInfo> remove = new HashSet<>(); |
| 181 | + Set<ProofInfo> add = new HashSet<>(); |
| 182 | + for (ProofInfo p : newProofs) |
| 183 | + if (!proofs.contains(p)) |
| 184 | + add.add(p); |
| 185 | + for (ProofInfo p : proofs) |
| 186 | + if (!newProofs.contains(p)) |
| 187 | + remove.add(p); |
| 188 | + if (newName != null || newDate != null || remove.size() > 0 || add.size() > 0) { |
| 189 | + new Thread(() -> { |
| 190 | + Client client = Main.getClient(); |
| 191 | + try { |
| 192 | + client.connect(); |
| 193 | + client.sendMessage(NetUtil.UPDATE_ASSIGNMENT); |
| 194 | + if (newName != null) |
| 195 | + client.sendMessage(NetUtil.RENAME + "|" + classId + "|" + id + "|" + URLEncoder.encode(newName, "UTF-8")); |
| 196 | + if (newDate != null) |
| 197 | + client.sendMessage(NetUtil.CHANGE_DUE + "|" + classId + "|" + id + "|" + NetUtil.DATE_FORMAT.format(newDate)); |
| 198 | + for (ProofInfo p : remove) |
| 199 | + client.sendMessage(NetUtil.REMOVE_PROOF + "|" + classId + "|" + id + "|" + p.getProofId()); |
| 200 | + for (ProofInfo p : add) |
| 201 | + client.sendMessage(NetUtil.ADD_PROOF + "|" + classId + "|" + id + "|" + p.getProofId()); |
| 202 | + client.sendMessage(NetUtil.DONE); |
| 203 | + String res = client.readMessage(); |
| 204 | + if (!NetUtil.OK.equals(Client.checkError(res))) |
| 205 | + throw new IOException(res); |
| 206 | + Platform.runLater(() -> { |
| 207 | + if (newName != null) |
| 208 | + name.set(newName); |
| 209 | + if (newDate != null) |
| 210 | + dueDate.set(newDate.getTime()); |
| 211 | + load(true); |
| 212 | + }); |
| 213 | + } catch (IOException e) { |
| 214 | + e.printStackTrace(); |
| 215 | + } finally { |
| 216 | + client.disconnect(); |
| 217 | + } |
| 218 | + }).start(); |
| 219 | + } |
| 220 | + } catch (IOException e) { |
| 221 | + logger.error("Failed to show assignment dialog", e); |
| 222 | + } |
| 223 | + } |
| 224 | + |
117 | 225 | private void addChildren(AssignmentInfo rootInfo, TreeItem<AssignmentInfo> rootItem) { |
118 | 226 | if (rootInfo.getChildren() != null) { |
119 | 227 | rootInfo.getChildren().sort(Comparator.naturalOrder()); |
@@ -195,6 +303,7 @@ private void loadInstructor(Client client) throws IOException { |
195 | 303 | String name = URLDecoder.decode(split[1], "UTF-8"); |
196 | 304 | String createdBy = URLDecoder.decode(split[2], "UTF-8"); |
197 | 305 | long timestamp = NetUtil.DATE_FORMAT.parse(URLDecoder.decode(split[3], "UTF-8")).getTime(); |
| 306 | + proofs.add(new ProofInfo(pid, name, createdBy, timestamp, true)); |
198 | 307 | for (UserInfo info : users.values()) { |
199 | 308 | ProofInfo proofInfo = new ProofInfo(pid, name, createdBy, timestamp, true); |
200 | 309 | proofMap.computeIfAbsent(info.getUserId(), uid -> new HashMap<>()).put(pid, proofInfo); |
@@ -230,7 +339,7 @@ private void loadInstructor(Client client) throws IOException { |
230 | 339 | } |
231 | 340 |
|
232 | 341 | public String getName() { |
233 | | - return name; |
| 342 | + return name.get(); |
234 | 343 | } |
235 | 344 |
|
236 | 345 | public TitledPane getPane() { |
|
0 commit comments