Skip to content

Commit 80646e3

Browse files
committed
Added ability to edit and delete assignments
1 parent 96836fb commit 80646e3

11 files changed

Lines changed: 291 additions & 112 deletions

File tree

client/res/edu/rpi/aris/gui/submit/add_assignment.fxml renamed to client/res/edu/rpi/aris/gui/submit/assignment_dialog.fxml

File renamed without changes.

client/src/edu/rpi/aris/Main.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import edu.rpi.aris.gui.Aris;
44
import edu.rpi.aris.gui.GuiConfig;
5-
import edu.rpi.aris.gui.submit.AddAssignmentDialog;
65
import edu.rpi.aris.net.client.Client;
76
import javafx.application.Platform;
87
import javafx.scene.control.Alert;

client/src/edu/rpi/aris/gui/submit/Assignment.java

Lines changed: 125 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,57 @@
55
import edu.rpi.aris.net.NetUtil;
66
import edu.rpi.aris.net.client.Client;
77
import javafx.application.Platform;
8+
import javafx.beans.binding.Bindings;
89
import javafx.beans.property.SimpleBooleanProperty;
10+
import javafx.beans.property.SimpleLongProperty;
911
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;
1415
import javafx.scene.layout.VBox;
16+
import javafx.stage.Modality;
17+
import org.apache.commons.lang3.tuple.Triple;
1518
import org.apache.logging.log4j.LogManager;
1619
import org.apache.logging.log4j.Logger;
1720

1821
import java.io.IOException;
1922
import java.net.URLDecoder;
23+
import java.net.URLEncoder;
2024
import java.text.DateFormat;
2125
import java.text.ParseException;
26+
import java.time.LocalDateTime;
27+
import java.time.ZoneId;
2228
import java.util.*;
2329

2430
public class Assignment {
2531

2632
private static Logger logger = LogManager.getLogger(Assignment.class);
27-
private final String assignedBy;
2833
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();
3137
private TitledPane titledPane;
32-
private VBox tableBox = new VBox();
38+
private VBox tableBox = new VBox(5);
3339
private SimpleBooleanProperty loaded = new SimpleBooleanProperty(false);
3440
private ArrayList<AssignmentInfo> rootNodes = new ArrayList<>();
41+
private HashSet<ProofInfo> proofs = new HashSet<>();
3542

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);
3846
try {
39-
this.dueDate = NetUtil.DATE_FORMAT.parse(dueDate).getTime();
47+
this.dueDate.set(NetUtil.DATE_FORMAT.parse(dueDate).getTime());
4048
} catch (ParseException e) {
4149
logger.error("Failed to parse due date", e);
42-
this.dueDate = -1;
50+
this.dueDate.set(-1);
4351
}
44-
this.assignedBy = assignedBy;
4552
this.id = id;
4653
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));
4959
titledPane.expandedProperty().addListener((observableValue, oldVal, newVal) -> {
5060
if (newVal)
5161
load(false);
@@ -111,9 +121,107 @@ private void buildUI() {
111121
view.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
112122
view.setEditable(false);
113123
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+
}
114133
tableBox.getChildren().add(view);
115134
}
116135

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+
117225
private void addChildren(AssignmentInfo rootInfo, TreeItem<AssignmentInfo> rootItem) {
118226
if (rootInfo.getChildren() != null) {
119227
rootInfo.getChildren().sort(Comparator.naturalOrder());
@@ -195,6 +303,7 @@ private void loadInstructor(Client client) throws IOException {
195303
String name = URLDecoder.decode(split[1], "UTF-8");
196304
String createdBy = URLDecoder.decode(split[2], "UTF-8");
197305
long timestamp = NetUtil.DATE_FORMAT.parse(URLDecoder.decode(split[3], "UTF-8")).getTime();
306+
proofs.add(new ProofInfo(pid, name, createdBy, timestamp, true));
198307
for (UserInfo info : users.values()) {
199308
ProofInfo proofInfo = new ProofInfo(pid, name, createdBy, timestamp, true);
200309
proofMap.computeIfAbsent(info.getUserId(), uid -> new HashMap<>()).put(pid, proofInfo);
@@ -230,7 +339,7 @@ private void loadInstructor(Client client) throws IOException {
230339
}
231340

232341
public String getName() {
233-
return name;
342+
return name.get();
234343
}
235344

236345
public TitledPane getPane() {

client/src/edu/rpi/aris/gui/submit/AddAssignmentDialog.java renamed to client/src/edu/rpi/aris/gui/submit/AssignmentDialog.java

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515
import org.apache.commons.lang3.tuple.Triple;
1616

1717
import java.io.IOException;
18+
import java.text.SimpleDateFormat;
1819
import java.time.LocalDateTime;
19-
import java.util.Collection;
20-
import java.util.List;
21-
import java.util.Objects;
20+
import java.time.ZoneId;
21+
import java.util.*;
2222
import java.util.regex.Matcher;
2323
import java.util.regex.Pattern;
2424
import java.util.stream.Collectors;
2525

26-
public class AddAssignmentDialog extends Dialog<Triple<String, LocalDateTime, Collection<ProofInfo>>> {
26+
public class AssignmentDialog extends Dialog<Triple<String, LocalDateTime, Collection<ProofInfo>>> {
2727

2828
private static final Pattern timePattern = Pattern.compile("(?i)(?<hour>1[0-2]|0?[1-9]):(?<min>[0-5][0-9]) *?(?<ap>AM|PM)");
29+
private static final SimpleDateFormat timeFormat = new SimpleDateFormat("h:mm a");
2930
private final ProofList availableProofs;
3031
@FXML
3132
private TextField nameField;
@@ -36,12 +37,13 @@ public class AddAssignmentDialog extends Dialog<Triple<String, LocalDateTime, Co
3637
@FXML
3738
private TextField timeInput;
3839
private Button okBtn;
40+
private boolean edit = false;
3941

40-
public AddAssignmentDialog(Window parent, ProofList availableProofs) throws IOException {
42+
public AssignmentDialog(Window parent, ProofList availableProofs) throws IOException {
4143
this.availableProofs = availableProofs;
4244
initModality(Modality.WINDOW_MODAL);
4345
initOwner(parent);
44-
FXMLLoader loader = new FXMLLoader(AddAssignmentDialog.class.getResource("add_assignment.fxml"));
46+
FXMLLoader loader = new FXMLLoader(AssignmentDialog.class.getResource("assignment_dialog.fxml"));
4547
loader.setController(this);
4648
getDialogPane().getButtonTypes().setAll(ButtonType.OK, ButtonType.CANCEL);
4749
setTitle("Create Assignment");
@@ -59,16 +61,31 @@ public AddAssignmentDialog(Window parent, ProofList availableProofs) throws IOEx
5961
int hour = Integer.parseInt(m.group("hour"));
6062
int min = Integer.parseInt(m.group("min"));
6163
String ap = m.group("ap").toLowerCase();
62-
hour = ap.equals("pm") ? hour + 12 : hour;
63-
if (hour == 24)
64-
hour = 0;
64+
if (hour == 12) {
65+
hour = ap.equals("pm") ? 12 : 0;
66+
} else {
67+
hour = ap.equals("pm") ? hour + 12 : hour;
68+
if (hour == 24)
69+
hour = 0;
70+
}
6571
LocalDateTime date = dueDate.getValue().atTime(hour, min);
6672
List<ProofInfo> list = proofBox.getChildren().stream().map(node -> (ProofInfo) ((ComboBox) ((HBox) node).getChildren().get(0)).getSelectionModel().getSelectedItem()).filter(Objects::nonNull).collect(Collectors.toList());
6773
return new ImmutableTriple<>(name, date, list);
6874
});
6975
}
7076

71-
private void addSelector() {
77+
public AssignmentDialog(Window parent, ProofList availableProofs, String name, Date dueDate, Set<ProofInfo> proofs) throws IOException {
78+
this(parent, availableProofs);
79+
proofBox.getChildren().clear();
80+
for (ProofInfo info : proofs)
81+
addSelector().getSelectionModel().select(info);
82+
nameField.setText(name);
83+
timeInput.setText(timeFormat.format(dueDate));
84+
this.dueDate.setValue(dueDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
85+
edit = true;
86+
}
87+
88+
private ComboBox<ProofInfo> addSelector() {
7289
HBox box = new HBox(5);
7390
ComboBox<ProofInfo> combo = new ComboBox<>();
7491
combo.setPromptText("Select Proof");
@@ -89,19 +106,32 @@ public void changed(ObservableValue<? extends ProofInfo> observable, ProofInfo o
89106
Button delete = new Button("-");
90107
delete.disableProperty().bind(Bindings.createBooleanBinding(() -> combo.getSelectionModel().getSelectedItem() == null, combo.getSelectionModel().selectedItemProperty()));
91108
delete.setOnAction(action -> {
109+
if (edit) {
110+
Alert alert = new Alert(Alert.AlertType.WARNING);
111+
alert.initModality(Modality.WINDOW_MODAL);
112+
alert.initOwner(getDialogPane().getScene().getWindow());
113+
alert.setTitle("Remove Proof?");
114+
alert.setHeaderText("Are you sure you want to remove this proof");
115+
alert.setContentText("This will also delete any student submissions\nfor this proof on this assignment");
116+
alert.getButtonTypes().setAll(ButtonType.NO, ButtonType.YES);
117+
alert.getDialogPane().getScene().getWindow().sizeToScene();
118+
Optional<ButtonType> result = alert.showAndWait();
119+
if (!result.isPresent() || result.get() != ButtonType.YES)
120+
return;
121+
}
92122
proofBox.getChildren().remove(box);
93123
getDialogPane().getScene().getWindow().sizeToScene();
94124
});
95125
HBox.setHgrow(combo, Priority.ALWAYS);
96126
box.getChildren().addAll(combo, delete);
97127
proofBox.getChildren().add(box);
98128
getDialogPane().getScene().getWindow().sizeToScene();
129+
return combo;
99130
}
100131

101132
@FXML
102133
private void initialize() {
103134
okBtn.disableProperty().bind(Bindings.createBooleanBinding(() -> !timePattern.matcher(timeInput.getText()).matches() || nameField.getText().length() == 0 || dueDate.getValue() == null || proofBox.getChildren().size() < 2, timeInput.textProperty(), nameField.textProperty(), dueDate.valueProperty(), proofBox.getChildren()));
104135
addSelector();
105136
}
106-
107137
}

client/src/edu/rpi/aris/gui/submit/AssignmentWindow.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ private void initialize() {
178178
Bindings.bindContent(proofTable.getItems(), proofList.getProofs());
179179
}
180180

181-
private void loadAssignments(Course newCourse, boolean reload) {
181+
public void loadAssignments(Course newCourse, boolean reload) {
182182
Platform.runLater(() -> {
183183
assignments.getPanes().clear();
184184
if (newCourse == null)
@@ -458,7 +458,7 @@ private void refresh() {
458458
@FXML
459459
private void createAssignment() throws IOException {
460460
proofList.load(false);
461-
AddAssignmentDialog dialog = new AddAssignmentDialog(stage, proofList);
461+
AssignmentDialog dialog = new AssignmentDialog(stage, proofList);
462462
Optional<Triple<String, LocalDateTime, Collection<ProofInfo>>> result = dialog.showAndWait();
463463
if (result.isPresent()) {
464464
Triple<String, LocalDateTime, Collection<ProofInfo>> r = result.get();
@@ -489,4 +489,12 @@ public void integrityCheckFailed(String filename) {
489489
alert.getDialogPane().setPrefWidth(500);
490490
alert.showAndWait();
491491
}
492+
493+
public Stage getStage() {
494+
return stage;
495+
}
496+
497+
public ProofList getProofList() {
498+
return proofList;
499+
}
492500
}

client/src/edu/rpi/aris/gui/submit/Course.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public void load(Runnable runnable, boolean reload) {
4343
try {
4444
Platform.runLater(() -> {
4545
try {
46-
assignments.add(new Assignment(URLDecoder.decode(split[0], "UTF-8"), URLDecoder.decode(split[1], "UTF-8"), URLDecoder.decode(split[2], "UTF-8"), Integer.parseInt(split[3]), id));
46+
assignments.add(new Assignment(URLDecoder.decode(split[0], "UTF-8"), URLDecoder.decode(split[1], "UTF-8"), URLDecoder.decode(split[2], "UTF-8"), Integer.parseInt(split[3]), id, this));
4747
} catch (UnsupportedEncodingException e) {
4848
e.printStackTrace();
4949
}

client/src/edu/rpi/aris/gui/submit/ProofInfo.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ public String toString() {
7777
return name;
7878
}
7979

80+
@Override
81+
public boolean equals(Object obj) {
82+
if (!(obj instanceof ProofInfo))
83+
return false;
84+
ProofInfo info = (ProofInfo) obj;
85+
return info.proofId == proofId;
86+
}
87+
88+
@Override
89+
public int hashCode() {
90+
return proofId;
91+
}
92+
8093
public GradingStatus getGradingStatus() {
8194
return gradingStatus;
8295
}

libaris/res/edu/rpi/aris/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.0.38
1+
0.0.39

libaris/src/edu/rpi/aris/net/NetUtil.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public class NetUtil {
6060
public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
6161
public static final String TOO_LARGE = "TOO_LARGE";
6262
public static final String NO_DATA = "NO_DATA";
63+
public static final String RENAME = "RENAME";
64+
public static final String ADD_PROOF = "ADD_PROOF";
65+
public static final String REMOVE_PROOF = "REMOVE_PROOF";
66+
public static final String CHANGE_DUE = "CHANGE_DUE";
6367

6468
/**
6569
* Compares two version strings.

packaging/deb/server/etc/aris.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ db-pass arispass
1515
# The private key for the above certificate. If this is not specified here of on the command line the server will run in self signing mode
1616
# key <key-file-path>
1717

18+
# The domain name of the server. Note this is only required when the server is running in self signing mode
19+
# domain <example.com>
20+
1821
# The name of the postgres database for Aris to use
1922
# db-name aris
2023

0 commit comments

Comments
 (0)