Skip to content

Conversation

@pkharge
Copy link
Contributor

@pkharge pkharge commented Jul 29, 2022

Guidelines for Pull Requests

If you haven't yet read our code review guidelines, please do so, You can find them here.

Please confirm the following by adding an x for each item (turn [ ] into [x]).

  • I have removed all code style changes that are not necessary (e.g. changing blanks across the whole file that don’t need to be changed, adding empty lines in parts other than your own code)
  • I am not making any changes to files that don’t have any effect (e.g. imports added that don’t need to be added)
  • I do not have any sysout statements in my code or commented out code that isn’t needed anymore
  • I am not reformatting any files in the wrong format or without cause.
  • I am not changing file encoding or line endings to something else than UTF-8, LF
  • My pull request does not show an insane amount of files being changed although my ticket only requires a few files being changed
  • I have added Javadoc/documentation where appropriate
  • I have added test cases where appropriate
  • I have explained any part of my code/implementation decisions that is not be self-explanatory

Please provide a brief description of your ticket

The user would get a zip file that contains one html page for each page + any css, javascript, etc.

Anything else the reviewer needs to know?

... describe here ...

@diging-jenkins
Copy link

Can one of the admins verify this patch?

@pkharge pkharge marked this pull request as draft July 29, 2022 23:40
@pkharge pkharge marked this pull request as ready for review August 1, 2022 23:16
Copy link
Member

@jdamerow jdamerow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't not go through everything but given I'm asking for some major changes, I'll leave it with this for now.

});
IOUtils.close(responseZipStream);
IOUtils.close(bufferedOutputStream);
IOUtils.close(byteArrayOutputStream);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can probably be tightened up by using try-with statements

responseZipStream.closeEntry();

} catch (IOException e) {
System.err.println(e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

return byteArrayOutputStream.toByteArray();

} catch (IOException e) {
throw new IOException(e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no point in wrapping an IOException in another IOException

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still



@Service
public class DownloadsManager {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs an interface

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell so far, this class needs to be a little refactored. There should be several methods:

  • create a snapshot (or whatever you want to call the generated html pages)
  • get one
  • list all
  • get generated files

When the user generates a new snapshot the create method is called. This should be an @Async method as it might take a while. The user sees a progress page (polling for updates from the front end, not just one call as the server might time out).
When the generation process is done, the user will be sent to the page for that snapshot. The create method should return the created ExhibitionDownload object. The get method is probably called here, to get the object by its id. If the user wants to download the files, they can do so from that page (get generated files method will be called).
The list all snapshots page will call the list method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdamerow , want to check my understanding on this once.
The first time user clicks on download button, the async rest api will return an newly created exhibitionDownload object with a unique folder name (used timestamps). this api will trigger create snapshot method.
then Ui will poll another rest api (this will be an simple GET api) by sending exhibitionDownload object as input, untill the requested folder is ready. once its ready, the folder should be download as zip folder.

so there will be 2 different rest apis in backend, one async which just returns exhibitionDownload object and trigger createSnapshot method. another one will be used simply to poll.

is this understanding correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure. What do you mean by "async rest api"?

The flow would be:

  • POST request to start zip generation (the request itself does not have to be async, but it will start an async method on the server)
  • the controller will return an id that the client can then use to poll (id could be the id of an exhibitionDownload object if that is being created and returned by the async method)
  • "then Ui will poll another rest api (this will be an simple GET api)" - yes, "by sending exhibitionDownload object as input" -> not sure what that means. It would use the id returned in the previous step
  • "untill the requested folder is ready." - yes
  • "once its ready, the folder should be download as zip folder." - once it's ready the user should be able to down load, it should probably show a message that the generation is done or something. It does not have to automatically start downloading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jdamerow , to show the progress of download to the user, do we want to create a separate page where user will be redirected to, or can we show it within a section in the same downloadlist page?
I tried putting it in an alert box, but if the page gets refreshed, the user won't be able to see the notification.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

either is fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's on the same page and the user won't see the progress anymore after reloading the page, there is needs to be another indication that makes clear that export is in progress.

});
IOUtils.close(responseZipStream);
IOUtils.close(bufferedOutputStream);
IOUtils.close(byteArrayOutputStream);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these needed since you're using the try-with statement?

Resource resource = null;
try {
String pathToResources = request.getServletContext().getRealPath("") + "/resources";
String exhibitionFolderName= "Exhibition"+ LocalDateTime.now();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

business logic that should not be in the controller

@jdamerow jdamerow closed this Oct 24, 2022
@pooja-thalur pooja-thalur reopened this Apr 15, 2025
# Conflicts:
#	vspace/src/test/java/edu/asu/diging/vspace/core/services/impl/ImageServiceTest.java
@jdamerow
Copy link
Member

Make it so, Jenkins.

@jdamerow
Copy link
Member

A couple of issues:

  • After clicking "Create Snapshot" nothing happens. There should be some indication that the snapshot generation has started. And if a snapshot generation is currently running, there needs to be some indication on the page as well.
  • The exported exhibition:
    • there is not index.html file in the generated root directory, so it's not clear what file is the first page of the exhibition
    • there are no links on any of the pages apparently! So one cannot move from one page to the next.

@jdamerow jdamerow closed this Jun 17, 2025
vspace/pom.xml Outdated
<uploaded.files.path></uploaded.files.path>

<downloaded.files.path></downloaded.files.path>
<resources.path></resources.path>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two need a comment what should go here.

*/
@Async
@Transactional
public Future<SnapshotTask> createSnapshot(String resourcesPath, String exhibitionFolderName,SequenceHistory sequenceHistory, ExhibitionSnapshot exhibitionSnapshot)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally, resource path would not be required but calculate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still

import edu.asu.diging.vspace.core.services.IRenderingManager;

@Service
public class AsyncSnapshotCreator {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs an interface

@Girik1105 Girik1105 reopened this Oct 6, 2025
htmlContent.append(" </div>\n");
htmlContent.append(" </div>\n");
htmlContent.append("</body>\n");
htmlContent.append("</html>");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be done using a template like the rest of the files.

*/
@Async
@Transactional
public Future<SnapshotTask> createSnapshot(String resourcesPath, String exhibitionFolderName,SequenceHistory sequenceHistory, ExhibitionSnapshot exhibitionSnapshot)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this still

Set<String> visitedSequences = new HashSet<String>();
createSequencesSnapshot(startSequence, module, space, spaceFolderName, imagesFolder, visitedSequences);
} catch (FileStorageException e) {
logger.error("Could not download Module",e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error like this need to be communicated to the user; they need to know if a snapshot couldn't fully be created.

* @param context
* @param id
*/
private void populateContextForSpace(Context context, String id, SequenceHistory sequenceHistory) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like sequenceHistory is never used?

exhibitionSnapshot = exhibitionSnapshotRepository.save(exhibitionSnapshot);

try {
snapshotTask = createSnapshot(resourcesPath, exhibitionFolderName, sequenceHistory, exhibitionSnapshot);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't seem to make sense; if sequenceHistory is autowired, wouldn't it get the history of the current user? that should not be used to generate a snapshot. I don't think sequence history is at important for snapshots?

@jdamerow jdamerow closed this Oct 15, 2025
@Girik1105 Girik1105 reopened this Oct 24, 2025

<uploaded.files.path>${project.basedir}/uploads</uploaded.files.path>
<downloaded.files.path>${project.basedir}/downloads</downloaded.files.path>
<resources.path>${project.basedir}/src/main/webapp/resources</resources.path>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe project.basedir is an absolute path to the base directory of the project? This does not work, as the war will be build on a different system than where it is deployed, so the directory will be a different one. where ever those variables (uploaded.files.path, etc) are being used, it should use relative paths for directories that are within the project, and absolute paths set through configurations for folders outside of the project..

byte[] bytes = byteOutput.toByteArray();
byteOutput.close();
return bytes;
catch(IOException e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be on line 106 according to our style guide

throw new IOException(e.getMessage(), e);
}
}
// Delete the folder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useless comment; comments should explain why not what (unless it's not obvious from the code, which it is here).

// Delete the folder
deleteFolder(folderPath, folderName);
} catch (IOException e) {
// Delete the zip file if an exception occurred
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

// Get exhibition details
IExhibition exhibition = exhibitionManager.getStartExhibition();
String exhibitionTitle = exhibition != null ? exhibition.getTitle() : "Virtual Exhibition";
Space startSpace = exhibition != null && exhibition.getStartSpace() != null ?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the interface here instead of Space?

try {
return storageEngineDownloads.getMediaContent("", exhibitionSnapshot.get().getFolderName() + ZIP_FILE_EXTENSION);
} catch (IOException e) {
throw new ExhibitionSnapshotNotFoundException(e.getMessage(), e);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you just throw the exception, there is no reason to repeat the message of the wrapped exception. Either have a message that explains something or just not provide a message at all (and just wrap the exception).


Pageable requestedPageForFiles = PageRequest.of(filesPagenum - 1, pageSize);
Page<ExhibitionSnapshot> page = exhibitionSnapshotRepository.findAllByOrderByCreationDateDesc(requestedPageForFiles);
return page.map(exhibitionSnapshot-> {return (ExhibitionSnapshot) exhibitionSnapshot;});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused, what is this line doing? looks like it's mapping snapshots onto themselves?

if(snapshotTask.isPresent()) {
return snapshotTask.get();
}
else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code style

$('.successAlert').append(alert);
const poll = (id, folderName) => {
maxAttempts = 10;
interval= 3000;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that miliseconds? (needs a comment)

var alert = $('<div class="alert alert-info alert-dismissible fade show" role="alert"><p>Snapshot '+folderName+' creation is in progress. Please wait. </p><button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>');
$('.successAlert').append(alert);
const poll = (id, folderName) => {
maxAttempts = 10;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems odd. Why would polling for a completed task have a maximum number of attempts. It should poll until complete or there was an error.

@jdamerow jdamerow closed this Oct 27, 2025
@Girik1105 Girik1105 reopened this Nov 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants