Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Network Visualization core logic #99

Merged
merged 7 commits into from
Jan 9, 2025
Merged

Conversation

pnavada
Copy link

@pnavada pnavada commented Nov 18, 2024

PR Type

enhancement


Description

  • Implemented core logic for network visualization using Cytoscape.js in the server module.
  • Developed a UI module to support network visualization, including data upload and settings.
  • Integrated the network server module into the main server function.
  • Added a new tab for network interpretation in the UI.

Changes walkthrough 📝

Relevant files
Enhancement
module-visualize-network-server.R
Implement network visualization server logic with Cytoscape.js

R/module-visualize-network-server.R

  • Added a server module to handle network visualization.
  • Implemented reactive expressions for data processing and network
    rendering.
  • Integrated Cytoscape.js for network visualization.
  • Added error handling for CSV data upload.
  • +114/-0 
    module-visualize-network-ui.R
    Develop UI for network visualization with Cytoscape.js     

    R/module-visualize-network-ui.R

  • Created a UI module for network visualization.
  • Integrated Cytoscape.js and related libraries.
  • Added UI components for data upload and network settings.
  • +69/-0   
    server.R
    Integrate network server module into main server                 

    R/server.R

  • Integrated the network server module into the main server function.
  • +2/-0     
    ui.R
    Add network interpretation tab to UI                                         

    R/ui.R

    • Added a new tab for network interpretation in the UI.
    +1/-0     

    💡 PR-Agent usage: Comment /help "your question" on any pull request to receive relevant information

    @pnavada pnavada changed the title Adding network visualization core logic Network Visualization core logic Nov 18, 2024
    Copy link

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 Security concerns

    JavaScript Injection Risk:
    The implementation directly uses user inputs in JavaScript code generation, which could lead to JavaScript injection vulnerabilities. This is particularly concerning in lines where node and edge data are dynamically generated based on user input. Consider sanitizing these inputs or employing other methods to ensure the security of the generated code.

    ⚡ Recommended focus areas for review

    Error Handling
    The error handling in the CSV reading function could be improved by providing more specific error messages or handling different types of errors differently.

    JavaScript Injection Risk
    Directly using user input in JavaScript code generation could lead to JavaScript injection vulnerabilities. Consider sanitizing or validating the inputs before use.

    Performance Concern
    The loop for edge elements creation might be inefficient for large datasets. Consider optimizing this section or using vectorized operations if possible.

    Copy link

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Score
    Possible bug
    Add error handling for data processing functions to improve robustness

    Consider adding error handling for the annotateProteinInfoFromIndra and
    getSubnetworkFromIndra functions to manage potential failures or exceptions in data
    processing.

    R/module-visualize-network-server.R [33-34]

    -annotated_df <- annotateProteinInfoFromIndra(df(), proteinIdType())
    -subnetwork <- getSubnetworkFromIndra(annotated_df, pValue())
    +annotated_df <- tryCatch({
    +    annotateProteinInfoFromIndra(df(), proteinIdType())
    +}, error = function(e) {
    +    showNotification(paste("Error in annotation:", e$message), type = "error")
    +    return(NULL)
    +})
    +subnetwork <- tryCatch({
    +    getSubnetworkFromIndra(annotated_df, pValue())
    +}, error = function(e) {
    +    showNotification(paste("Error in subnetwork extraction:", e$message), type = "error")
    +    return(NULL)
    +})
    Suggestion importance[1-10]: 8

    Why: Adding error handling for the functions annotateProteinInfoFromIndra and getSubnetworkFromIndra is crucial to manage potential failures, improving the robustness and user experience of the application.

    8
    Ensure numeric inputs are valid to prevent runtime errors

    Validate the numeric conversion of input$pValue and input$logFC to handle
    non-numeric inputs gracefully.

    R/module-visualize-network-server.R [20-28]

     pValue <- reactive({
         req(input$pValue)
    -    as.numeric(input$pValue)
    +    if (!is.na(as.numeric(input$pValue))) {
    +        as.numeric(input$pValue)
    +    } else {
    +        showNotification("Invalid pValue input", type = "error")
    +        return(NULL)
    +    }
     })
     logFC <- reactive({
         req(input$logFC)
    -    as.numeric(input$logFC)
    +    if (!is.na(as.numeric(input$logFC))) {
    +        as.numeric(input$logFC)
    +    } else {
    +        showNotification("Invalid logFC input", type = "error")
    +        return(NULL)
    +    }
     })
    Suggestion importance[1-10]: 7

    Why: Validating the numeric conversion of inputs pValue and logFC is essential to prevent runtime errors from non-numeric inputs, thus enhancing the application's stability and user experience.

    7
    Performance
    Implement caching to reduce redundant data processing and enhance responsiveness

    Implement caching for the df, proteinIdType, pValue, and logFC reactive expressions
    to improve performance by avoiding redundant computations.

    R/module-visualize-network-server.R [4-28]

     df <- reactive({
         req(input$dataUpload)
    -    tryCatch({
    -        read.csv(input$dataUpload$datapath)
    -    }, error = function(e) {
    -        ...
    +    isolate({
    +        if (!exists("cached_df")) {
    +            cached_df <<- tryCatch({
    +                read.csv(input$dataUpload$datapath)
    +            }, error = function(e) {
    +                ...
    +            })
    +        }
    +        cached_df
         })
     })
     proteinIdType <- reactive({
         req(input$proteinIdType)
    -    input$proteinIdType
    +    isolate({
    +        if (!exists("cached_proteinIdType")) {
    +            cached_proteinIdType <<- input$proteinIdType
    +        }
    +        cached_proteinIdType
    +    })
     })
     ...
    Suggestion importance[1-10]: 6

    Why: Implementing caching for reactive expressions can significantly enhance performance by reducing redundant computations, especially beneficial in scenarios with frequent data updates or large datasets.

    6
    Use more efficient methods for generating JavaScript code to enhance performance

    Optimize the JavaScript code generation by using more efficient string concatenation
    techniques or pre-compiled templates.

    R/module-visualize-network-server.R [54-60]

    -js_code <- paste0("
    -    cytoscape.use(cytoscapeDagre);
    -    var cy = cytoscape({
    -        container: document.getElementById('network-cy'),
    -        elements: [", paste(elements, collapse = ", "), "],
    -        ...
    -    });
    -    ")
    +js_template <- "cytoscape.use(cytoscapeDagre);
    +                var cy = cytoscape({
    +                    container: document.getElementById('network-cy'),
    +                    elements: [%s],
    +                    ...
    +                });"
    +js_code <- sprintf(js_template, paste(elements, collapse = ", "))
    Suggestion importance[1-10]: 5

    Why: Optimizing JavaScript code generation using templates can improve performance, although the impact might be moderate depending on the overall application size and complexity.

    5

    @tonywu1999 tonywu1999 changed the base branch from devel to devel-bionet December 4, 2024 16:29
    @@ -0,0 +1,176 @@
    networkServer <- function(input, output, session, parent_session, significant) {
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    nit: Could you rename this to visualizeNetworkServer? I think networkServer may confuse people thinking it has something to do with computer networks.

    Copy link
    Author

    Choose a reason for hiding this comment

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

    done

    if (is.null(input$dataUpload) && !is.null(significant())) {
    df <- significant()
    if (!is.null(df) && "Protein" %in% names(df)) {
    df$Protein <- as.character(significant()$Protein)
    Copy link
    Contributor

    @tonywu1999 tonywu1999 Dec 4, 2024

    Choose a reason for hiding this comment

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

    Code would be easier to follow if you do df$Protein <- as.character(df$Protein)

    Copy link
    Author

    Choose a reason for hiding this comment

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

    done

    @@ -909,6 +908,7 @@ statmodelServer <- function(input, output, session,parent_session, loadpage_inpu
    return(
    list(
    input = input,
    significant = SignificantProteins,
    Copy link
    Contributor

    @tonywu1999 tonywu1999 Dec 4, 2024

    Choose a reason for hiding this comment

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

    I think this is where we had talked about using the proteins being already filtered before coming to step 5 network viz.

    I think adding a significant parameter to the returned object may not be necessary since you can use the dataComparison$ComparisonResult table as the list of proteins with their p-values

    Copy link
    Author

    Choose a reason for hiding this comment

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

    done

    @@ -376,7 +376,6 @@ statmodelServer <- function(input, output, session,parent_session, loadpage_inpu
    data_comp = data_comparison()
    significant = data_comp$ComparisonResult[
    which(data_comp$ComparisonResult$adj.pvalue < input$signif), ]
    print(significant)
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Any reason for removing this print statement here?

    Copy link
    Author

    Choose a reason for hiding this comment

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

    added it back

    renderNetwork <- reactive({
    # Annotate protein info and filter the subnetwork based on p-value
    annotated_df <- tryCatch({
    annotateProteinInfoFromIndra(df(), proteinIdType())
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Did you add a dependency on MSstatsBioNet for this package? i.e. using the @importFrom tag and adding the dependency in the DESCRIPTION file.

    Copy link
    Author

    Choose a reason for hiding this comment

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

    done

    interaction <- edge_parts[3]

    # Get edges table
    edges_table <- renderNetwork()$edges_table
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Will this call INDRA again? If so, could we use the original edges table that we had already.

    Copy link
    Author

    Choose a reason for hiding this comment

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

    No, it wont call INDRA again. It happens only when the inputs change

    # Observe the event when an edge is clicked
    observeEvent(input$edgeClicked, {
    edge_id <- input$edgeClicked$id
    edge_parts <- strsplit(edge_id, "-")[[1]]
    Copy link
    Contributor

    @tonywu1999 tonywu1999 Dec 4, 2024

    Choose a reason for hiding this comment

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

    rather than a string split, I believe we already have source, target, and interaction data stored in the edge as well. Would it be simpler to pass and retrieve that information instead of the ID itself?

    Copy link
    Author

    Choose a reason for hiding this comment

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

    done

    R/server.R Outdated
    @@ -48,6 +49,8 @@ server <- function(input, output, session) {
    selected = "Uploaddata")
    })

    callModule(networkServer, "network", session, significant)
    Copy link
    Contributor

    Choose a reason for hiding this comment

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

    Instead of passing significant, could you instead pass data_comparison$ComparisonResult instead here?

    Copy link
    Author

    Choose a reason for hiding this comment

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

    done

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    2 participants