Skip to content

Commit 0c62e8a

Browse files
authored
Merge pull request #47 from JaneliaSciComp/db_cleanup
Admin DB Cleanup for TmWorkspaces
2 parents 2c8658b + ab0b6ee commit 0c62e8a

File tree

7 files changed

+299
-9
lines changed

7 files changed

+299
-9
lines changed

modules/AdministrationGUI/src/main/java/org/janelia/workstation/admin/AdministrationTopComponent.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,37 +77,52 @@ private void setupGUI() {
7777
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
7878

7979
this.topMenu = new JPanel();
80-
BoxLayout layout = new BoxLayout(topMenu, BoxLayout.X_AXIS);
81-
topMenu.setLayout(layout);
80+
topMenu.setLayout(new BoxLayout(topMenu, BoxLayout.Y_AXIS)); // Main panel with vertical layout
8281

82+
// First row with "Users" and "Groups"
83+
JPanel row1 = new JPanel();
84+
row1.setLayout(new BoxLayout(row1, BoxLayout.X_AXIS));
8385
// top level buttons
8486
JButton listUsersButton = new JButton(UIUtils.getClasspathImage(this.getClass(), "/org/janelia/workstation/admin/images/user.png"));
8587
listUsersButton.setText("Users");
8688
listUsersButton.setToolTipText("Show all users");
8789
listUsersButton.setVerticalTextPosition(SwingConstants.BOTTOM);
8890
listUsersButton.setHorizontalTextPosition(SwingConstants.CENTER);
8991
listUsersButton.addActionListener(event -> viewUserList());
90-
topMenu.add(listUsersButton);
92+
row1.add(listUsersButton);
9193

92-
topMenu.add(Box.createHorizontalStrut(20));
94+
row1.add(Box.createHorizontalStrut(20)); // Add space between buttons
9395

9496
JButton listGroupsButton = new JButton(UIUtils.getClasspathImage(this.getClass(), "/org/janelia/workstation/admin/images/group.png"));
9597
listGroupsButton.setText("Groups");
9698
listGroupsButton.addActionListener(event -> viewGroupList());
9799
listGroupsButton.setToolTipText("Show all groups");
98100
listGroupsButton.setVerticalTextPosition(SwingConstants.BOTTOM);
99101
listGroupsButton.setHorizontalTextPosition(SwingConstants.CENTER);
100-
topMenu.add(listGroupsButton);
101-
102-
topMenu.add(Box.createHorizontalStrut(20));
102+
listGroupsButton.addActionListener(event -> viewGroupList());
103+
row1.add(listGroupsButton);
103104

105+
row1.add(Box.createHorizontalStrut(20));
104106
JButton getLogsButton = new JButton(UIUtils.getClasspathImage(this.getClass(), "/org/janelia/workstation/admin/images/logs.png"));
105107
getLogsButton.setText("Logs");
106108
getLogsButton.setToolTipText("Retrieve Logs");
107109
getLogsButton.addActionListener(event -> getLogs());
108110
getLogsButton.setVerticalTextPosition(SwingConstants.BOTTOM);
109111
getLogsButton.setHorizontalTextPosition(SwingConstants.CENTER);
110-
topMenu.add(getLogsButton);
112+
row1.add(getLogsButton);
113+
114+
row1.add(Box.createHorizontalStrut(20));
115+
116+
JButton workspaceCleanupButton = new JButton(UIUtils.getClasspathImage(this.getClass(), "/org/janelia/workstation/admin/images/clean.png"));
117+
workspaceCleanupButton.setText("Cleanup Db");
118+
workspaceCleanupButton.setToolTipText("Manage and delete large workspaces");
119+
workspaceCleanupButton.setVerticalTextPosition(SwingConstants.BOTTOM);
120+
workspaceCleanupButton.setHorizontalTextPosition(SwingConstants.CENTER);
121+
workspaceCleanupButton.addActionListener(event -> databaseCleanup());
122+
row1.add(workspaceCleanupButton);
123+
124+
// Add both rows to the main menu
125+
topMenu.add(row1);
111126

112127
add(topMenu);
113128
add(Box.createVerticalGlue());
@@ -127,6 +142,15 @@ public void objectsInvalidated(DomainObjectInvalidationEvent event) {
127142
}
128143
}
129144

145+
void databaseCleanup() {
146+
DatabaseCleanupPanel panel = new DatabaseCleanupPanel(this);
147+
removeAll();
148+
add(panel);
149+
revalidate();
150+
repaint();
151+
this.currentView = panel;
152+
}
153+
130154
void viewUserList() {
131155
UserManagementPanel panel = new UserManagementPanel(this);
132156
removeAll();
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package org.janelia.workstation.admin;
2+
3+
import org.janelia.model.domain.tiledMicroscope.TmWorkspace;
4+
import org.janelia.model.domain.tiledMicroscope.TmWorkspaceInfo;
5+
import org.janelia.workstation.common.gui.support.DesktopApi;
6+
import org.janelia.workstation.controller.access.TiledMicroscopeDomainMgr;
7+
import org.janelia.workstation.core.util.Refreshable;
8+
import org.janelia.workstation.core.workers.BackgroundWorker;
9+
import org.janelia.workstation.core.workers.SimpleWorker;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
13+
import javax.swing.*;
14+
import javax.swing.table.AbstractTableModel;
15+
import java.awt.*;
16+
import java.util.ArrayList;
17+
import java.util.HashMap;
18+
import java.util.List;
19+
import java.util.Map;
20+
import java.util.concurrent.Callable;
21+
22+
public class DatabaseCleanupPanel extends JPanel implements Refreshable {
23+
private static final Logger log = LoggerFactory.getLogger(DatabaseCleanupPanel.class);
24+
private final AdministrationTopComponent parent;
25+
private JTable workspaceTable;
26+
private WorkspaceTableModel tableModel;
27+
28+
public DatabaseCleanupPanel(AdministrationTopComponent parent) {
29+
this.parent = parent;
30+
refresh();
31+
}
32+
33+
private void setupUI() {
34+
setLayout(new BorderLayout());
35+
removeAll();
36+
37+
JPanel titlePanel = new TitlePanel("Manage Workspaces", "Return To Top Menu",
38+
event -> refresh(),
39+
event -> returnHome());
40+
add(titlePanel, BorderLayout.NORTH);
41+
42+
tableModel = new WorkspaceTableModel();
43+
workspaceTable = new JTable(tableModel);
44+
JScrollPane scrollPane = new JScrollPane(workspaceTable);
45+
add(scrollPane, BorderLayout.CENTER);
46+
47+
JButton refreshButton = new JButton("Load Largest Workspaces");
48+
refreshButton.addActionListener(e -> loadLargestWorkspaces());
49+
50+
JButton deleteButton = new JButton("Delete Selected Workspaces");
51+
deleteButton.addActionListener(e -> deleteSelectedWorkspaces());
52+
53+
JPanel buttonPanel = new JPanel();
54+
buttonPanel.add(refreshButton);
55+
buttonPanel.add(deleteButton);
56+
add(buttonPanel, BorderLayout.SOUTH);
57+
58+
revalidate();
59+
}
60+
61+
private void loadLargestWorkspaces() {
62+
SimpleWorker worker = new SimpleWorker() {
63+
private List<TmWorkspaceInfo> workspaceResults;
64+
65+
@Override
66+
protected void doStuff() {
67+
TiledMicroscopeDomainMgr domainMgr = TiledMicroscopeDomainMgr.getDomainMgr();
68+
workspaceResults = domainMgr.getLargestWorkspaces();
69+
}
70+
71+
@Override
72+
protected void hadSuccess() {
73+
tableModel.clear();
74+
tableModel.setWorkspaces(workspaceResults);
75+
}
76+
77+
@Override
78+
protected void hadError(Throwable error) {
79+
log.error("Error loading workspaces", error);
80+
JOptionPane.showMessageDialog(DatabaseCleanupPanel.this,
81+
"Failed to load workspaces: " + error.getMessage(), "Error",
82+
JOptionPane.ERROR_MESSAGE);
83+
}
84+
};
85+
worker.execute();
86+
}
87+
88+
private void deleteSelectedWorkspaces() {
89+
List<Long> selectedWorkspaces = tableModel.getSelectedWorkspaces();
90+
if (selectedWorkspaces.isEmpty()) {
91+
JOptionPane.showMessageDialog(this, "Please select at least one workspace to delete.", "No Selection", JOptionPane.WARNING_MESSAGE);
92+
return;
93+
}
94+
95+
int confirmation = JOptionPane.showConfirmDialog(this,
96+
"Are you sure you want to delete the selected workspaces?",
97+
"Confirm Deletion", JOptionPane.YES_NO_OPTION);
98+
99+
if (confirmation == JOptionPane.YES_OPTION) {
100+
101+
BackgroundWorker deleter = new BackgroundWorker() {
102+
@Override
103+
public String getName() {
104+
return "Deleting TmWorkspaces";
105+
}
106+
107+
@Override
108+
protected void doStuff() {
109+
TiledMicroscopeDomainMgr domainMgr = TiledMicroscopeDomainMgr.getDomainMgr();
110+
domainMgr.removeWorkspaces(selectedWorkspaces);
111+
}
112+
113+
@Override
114+
protected void hadSuccess() {
115+
loadLargestWorkspaces();
116+
JOptionPane.showMessageDialog(DatabaseCleanupPanel.this,
117+
"Selected workspaces deleted successfully.", "Success", JOptionPane.INFORMATION_MESSAGE);
118+
}
119+
120+
@Override
121+
protected void hadError(Throwable error) {
122+
log.error("Error deleting workspaces", error);
123+
JOptionPane.showMessageDialog(DatabaseCleanupPanel.this,
124+
"Failed to delete workspaces: " + error.getMessage(), "Error",
125+
JOptionPane.ERROR_MESSAGE);
126+
}
127+
};
128+
deleter.executeWithEvents();
129+
}
130+
}
131+
132+
@Override
133+
public void refresh() {
134+
setupUI();
135+
}
136+
137+
private void returnHome() {
138+
parent.viewTopMenu();
139+
}
140+
}
141+
142+
class WorkspaceTableModel extends AbstractTableModel {
143+
private final String[] columnNames = {"Select", "Name", "Owner", "Date Created", "Size"};
144+
private List<TmWorkspaceInfo> workspaceResults = new ArrayList<>();
145+
private final List<Boolean> selected = new ArrayList<>();
146+
147+
public void setWorkspaces(List<TmWorkspaceInfo> workspaceResults) {
148+
this.workspaceResults = workspaceResults;
149+
selected.clear();
150+
for (int i = 0; i < workspaceResults.size(); i++) {
151+
selected.add(false); // Initialize selection state
152+
}
153+
fireTableDataChanged();
154+
}
155+
156+
@Override
157+
public int getRowCount() {
158+
return workspaceResults.size();
159+
}
160+
161+
@Override
162+
public int getColumnCount() {
163+
return columnNames.length;
164+
}
165+
166+
@Override
167+
public Object getValueAt(int rowIndex, int columnIndex) {
168+
TmWorkspaceInfo workspaceInfo = workspaceResults.get(rowIndex);
169+
switch (columnIndex) {
170+
case 0:
171+
return selected.get(rowIndex); // Boolean checkbox
172+
case 1:
173+
return workspaceInfo.getWorkspaceName();
174+
case 2:
175+
return workspaceInfo.getOwnerKey();
176+
case 3:
177+
return workspaceInfo.getDateCreated();
178+
case 4:
179+
return workspaceInfo.getTotalSize();
180+
default:
181+
return null;
182+
}
183+
}
184+
185+
@Override
186+
public boolean isCellEditable(int rowIndex, int columnIndex) {
187+
return columnIndex == 0; // Allow only checkboxes to be editable
188+
}
189+
190+
@Override
191+
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
192+
if (columnIndex == 0 && aValue instanceof Boolean) {
193+
selected.set(rowIndex, (Boolean) aValue);
194+
fireTableCellUpdated(rowIndex, columnIndex); // Notify JTable of the change
195+
}
196+
}
197+
198+
public void clear() {
199+
workspaceResults.clear();
200+
selected.clear();
201+
fireTableDataChanged(); // Notify the table UI that the data has changed
202+
}
203+
204+
@Override
205+
public Class<?> getColumnClass(int columnIndex) {
206+
return columnIndex == 0 ? Boolean.class : String.class;
207+
}
208+
209+
public List<Long> getSelectedWorkspaces() {
210+
List<Long> selectedWorkspaces = new ArrayList<>();
211+
for (int i = 0; i < workspaceResults.size(); i++) {
212+
if (selected.get(i)) {
213+
selectedWorkspaces.add(workspaceResults.get(i).getWorkspaceId());
214+
}
215+
}
216+
return selectedWorkspaces;
217+
}
218+
219+
@Override
220+
public String getColumnName(int column) {
221+
return columnNames[column];
222+
}
223+
}
224+
4.56 KB
Loading
922 Bytes
Loading

modules/ViewerController/src/main/java/org/janelia/workstation/controller/access/TiledMicroscopeDomainMgr.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.janelia.workstation.core.events.Events;
1616
import org.janelia.workstation.core.events.lifecycle.ConsolePropsLoaded;
1717
import org.janelia.model.domain.tiledMicroscope.BoundingBox3d;
18+
import org.janelia.model.domain.tiledMicroscope.TmWorkspaceInfo;
1819
import org.janelia.workstation.integration.util.FrameworkAccess;
1920
import org.slf4j.Logger;
2021
import org.slf4j.LoggerFactory;
@@ -153,6 +154,11 @@ public Collection<BoundingBox3d> getWorkspaceBoundingBoxes(Long workspaceId) thr
153154
return client.getWorkspaceBoundingBoxes(workspaceId);
154155
}
155156

157+
public List<TmWorkspaceInfo> getLargestWorkspaces () {
158+
LOG.debug("getLargestWorkspaces()");
159+
return client.getLargestWorkspaces(AccessManager.getSubjectKey());
160+
}
161+
156162
public List<TmOperation> getOperationLogs (Long workspaceId, Long neuronId, Date startTime, Date endTime,
157163
String subjectKey) {
158164
LOG.debug("getOperationLogs(workspaceId={}, neuronId={}, startTime={}, endTime={})",
@@ -231,6 +237,11 @@ public void remove(TmWorkspace workspace) throws Exception {
231237
getModel().notifyDomainObjectRemoved(workspace);
232238
}
233239

240+
public void removeWorkspaces(List<Long> selectedWorkspaces) {
241+
LOG.debug("removeWorkspaces({})", selectedWorkspaces);
242+
client.removeWorkspaces(selectedWorkspaces);
243+
}
244+
234245
class RetrieveNeuronsTask extends RecursiveTask<Stream<TmNeuronMetadata>> {
235246
double defaultLength = 50000.0;
236247
int start;

modules/ViewerController/src/main/java/org/janelia/workstation/controller/access/TiledMicroscopeRestClient.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.fasterxml.jackson.core.JsonFactory;
1717
import com.fasterxml.jackson.core.JsonParser;
18+
import com.fasterxml.jackson.core.type.TypeReference;
1819
import com.fasterxml.jackson.databind.ObjectMapper;
1920
import com.google.common.io.ByteStreams;
2021

@@ -118,6 +119,27 @@ public TmSample create(TmSample tmSample, Map<String, Object> storageAttributes)
118119
return response.readEntity(TmSample.class);
119120
}
120121

122+
public List<TmWorkspaceInfo> getLargestWorkspaces(String subjectKey ) {
123+
WebTarget target = getMouselightDataEndpoint("/workspace/largest")
124+
.queryParam("username", subjectKey);
125+
Response response = target
126+
.request("application/json")
127+
.get();
128+
checkBadResponse(target, response);
129+
try {
130+
String jsonResponse = response.readEntity(String.class);
131+
132+
133+
ObjectMapper objectMapper = new ObjectMapper();
134+
return objectMapper.readValue(jsonResponse, new TypeReference<List<TmWorkspaceInfo>>() {
135+
});
136+
} catch (Exception e) {
137+
FrameworkAccess.handleException(e);
138+
LOG.error ("Problems returning the largest workspaces in the db");
139+
throw new RemoteServiceException("Client had problems retrieving largest workspaces");
140+
}
141+
}
142+
121143
public List<TmOperation> getOperationLogs(Long workspaceId, Long neuronId,
122144
Date startTime, Date endTime,
123145
String subjectKey ) {
@@ -501,4 +523,13 @@ public boolean isServerPathAvailable(String serverPath, boolean directoryOnly, M
501523
int responseStatus = response.getStatus();
502524
return responseStatus == 200;
503525
}
526+
527+
public void removeWorkspaces(List<Long> selectedWorkspaces) {
528+
WebTarget target = getMouselightDataEndpoint("/workspaces/remove");
529+
530+
Response response = target
531+
.request("application/json")
532+
.put(Entity.json(selectedWorkspaces));
533+
checkBadResponse(target, response);
534+
}
504535
}

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109

110110
<janeliaws.jetty.version>9.4.46.v20220331</janeliaws.jetty.version>
111111

112-
<janeliaws.jacs-model.version>3.3.0</janeliaws.jacs-model.version>
112+
<janeliaws.jacs-model.version>3.3.4</janeliaws.jacs-model.version>
113113

114114
<janeliaws.jacs-messaging.version>2.5.0</janeliaws.jacs-messaging.version>
115115

0 commit comments

Comments
 (0)