• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pinneylab / htbam_analysis / 17821832757

18 Sep 2025 07:46AM UTC coverage: 20.461% (+1.8%) from 18.666%
17821832757

push

github

Nicholas-Freitas
Merge remote-tracking branch 'origin/freitas_refactor_5_16_25' into main

0 of 634 new or added lines in 6 files covered. (0.0%)

2 existing lines in 1 file now uncovered.

470 of 2297 relevant lines covered (20.46%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/htbam_analysis/analysis/plot.py
NEW
1
from dash import Dash, dcc, html, Input, Output, no_update, State
×
NEW
2
import plotly.graph_objs as go
×
NEW
3
import base64
×
NEW
4
import tempfile
×
NEW
5
import matplotlib.pyplot as plt
×
NEW
6
import numpy as np
×
7

NEW
8
def plot_chip(plotting_var, chamber_names, graphing_function=None, title=None):
×
9
    ''' This function creates a Dash visualization of a chip, based on a certain Run (run_name)
10
        Inputs:
11
            plotting_var: a dictionary mapping chamber_id to the variable to be plotted for that chamber
12
            chamber_names: a dictionary mapping chamber_id to the name of the sample in the chamber (e.g. '1,1': ecADK_XYZ')
13
            graphing_function: a function that takes in a single chamber_id (e.g. '1,1') and matplotlib axis and returns the axis object after plotting.
14
            title: a string to be used as the title of the plot
15
        TODO: make all the variables stored in Dash properly...
16
    '''
17

18
    # Make the image array
19
    #NB: eventually, store width/height in DB and reference!
NEW
20
    img_array = np.zeros([56,32])
×
21

22
    # Here we're plotting to value for each chamber (e.g. coloring by std curve slope)
NEW
23
    for chamber_id, value in plotting_var.items():
×
NEW
24
        x = int(chamber_id.split(',')[0])
×
NEW
25
        y = int(chamber_id.split(',')[1])
×
NEW
26
        img_array[y-1,x-1] = value 
×
27
    
28
    #generate title
NEW
29
    if title is None:
×
NEW
30
        title = ''
×
31
    
32
    #Create the figure
NEW
33
    layout = go.Layout()
×
34

35
    # create 1‐indexed axes
NEW
36
    x_vals = list(range(1, img_array.shape[1] + 1))
×
NEW
37
    y_vals = list(range(1, img_array.shape[0] + 1))
×
38

NEW
39
    heatmap = go.Heatmap(
×
40
        x=x_vals,
41
        y=y_vals,
42
        z=img_array,
43
        colorscale='Viridis',
44
        hovertemplate='x=%{x}<br>y=%{y}<br>z=%{z}<extra></extra>'
45
    )
46
    
NEW
47
    fig = go.Figure(layout=layout, data=heatmap)
×
48

49
    #center title in fig
NEW
50
    fig.update_layout(title=title,
×
51
                        title_x=0.5, 
52
                        yaxis=dict(scaleanchor="x", scaleratio=1, autorange='reversed'), 
53
                        xaxis=dict(scaleratio=1),
54
                        plot_bgcolor='rgba(0,0,0,0)',
55
                        width=600, height=600,
56
                        hovermode='x')
NEW
57
    fig.update_xaxes(showticklabels=False)
×
NEW
58
    fig.update_yaxes(showticklabels=False)
×
59

60
    #create dash app:
NEW
61
    app = Dash(__name__)
×
NEW
62
    app.layout = html.Div(
×
63
        style={'display': 'flex'},
64
        children=[
65
            html.Div(
66
                style={'flex': '1'},
67
                children=[
68
                    dcc.Graph(id="graph", figure=fig, clear_on_unhover=True),
69
                    dcc.Tooltip(id="graph-tooltip"),
70
                ]
71
            ),
72
            html.Div(id="side-panel", style={'flex': '1', 'paddingLeft': '20px', 'backgroundColor': 'white'})  # Set side-panel background to white
73
        ]
74
    )
75

76
    ### GRAPHING FUNCTION ON HOVER:
NEW
77
    if graphing_function is not None:
×
NEW
78
        @app.callback(
×
79
            Output("graph-tooltip", "show"),
80
            Output("graph-tooltip", "bbox"),
81
            Output("graph-tooltip", "children"),
82
            Input("graph", "hoverData"),
83
        )
NEW
84
        def display_hover(hoverData):
×
NEW
85
            if hoverData is None:
×
NEW
86
                return False, no_update, no_update
×
87
            # demo only shows the first point, but other points may also be available
NEW
88
            pt = hoverData["points"][0]
×
NEW
89
            chamber_id = str(pt['x']) + ',' + str(pt['y'])
×
NEW
90
            bbox = pt["bbox"]
×
NEW
91
            chamber_name = chamber_names[chamber_id]
×
92
            #get the data for the point:
NEW
93
            fig, ax = plt.subplots()
×
NEW
94
            ax = graphing_function(chamber_id, ax)
×
95
            #reduce whitespace on margins of graph:
NEW
96
            fig.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0, hspace=0)
×
97
            #save the figure as a temp file:
NEW
98
            tempfile_name = tempfile.NamedTemporaryFile().name+'.png'
×
NEW
99
            plt.savefig(tempfile_name)
×
NEW
100
            plt.close()
×
101
            # #read in temp file as base64 encoded string:
NEW
102
            with open(tempfile_name, "rb") as image_file:
×
NEW
103
                img_src = "data:image/png;base64," + str(base64.b64encode(image_file.read()).decode("utf-8"))
×
NEW
104
            children = [
×
105
                html.Div(children=[
106
                    #no space after header:
107
                    html.H3('{},{}:  {}'.format(pt['x'], pt['y'], chamber_name), style={"color": 'black', "fontFamily":"Arial", "textAlign": "center", "marginBottom": "0px"}), #1-index
108
                    #add the image with reduced whitespace:
109
                    html.Img(src=img_src, style={"width": "100%"}),
110
                ],
111
                style={'width': '400px', 'white-space': 'none'})
112
            ]
113

NEW
114
            return True, bbox, children
×
115

116
    ### HIGHLIGHT ON HOVER / CLICK ###
NEW
117
    @app.callback(
×
118
        Output("graph", "figure"),
119
        Input("graph", "hoverData"),
120
        Input("graph", "clickData"),
121
        State("graph", "figure"),
122
    )
NEW
123
    def update_highlights(hoverData, clickData, fig):
×
124
        # clear old shapes
NEW
125
        fig["layout"]["shapes"] = []
×
126

127
        # hover => red outlines
NEW
128
        if hoverData:
×
NEW
129
            pt = hoverData["points"][0]
×
NEW
130
            sample = chamber_names.get(f"{pt['x']},{pt['y']}")
×
NEW
131
            if sample:
×
NEW
132
                for cid, name in chamber_names.items():
×
NEW
133
                    if name == sample:
×
NEW
134
                        i, j = map(int, cid.split(","))
×
NEW
135
                        fig["layout"]["shapes"].append({
×
136
                            "type": "rect",
137
                            "x0": i-0.4, "x1": i+0.4,
138
                            "y0": j-0.4, "y1": j+0.4,
139
                            "line": {"color": "red", "width": 2},
140
                            "fillcolor": "rgba(0,0,0,0)",
141
                            "name": "hover"
142
                        })
143

144
        # click => magenta outlines
NEW
145
        if clickData:
×
NEW
146
            pt = clickData["points"][0]
×
NEW
147
            sample = chamber_names.get(f"{pt['x']},{pt['y']}")
×
NEW
148
            if sample:
×
NEW
149
                for cid, name in chamber_names.items():
×
NEW
150
                    if name == sample:
×
NEW
151
                        i, j = map(int, cid.split(","))
×
NEW
152
                        fig["layout"]["shapes"].append({
×
153
                            "type": "rect",
154
                            "x0": i-0.4, "x1": i+0.4,
155
                            "y0": j-0.4, "y1": j+0.4,
156
                            "line": {"color": "magenta", "width": 3},
157
                            "fillcolor": "rgba(0,0,0,0)",
158
                            "name": "selected"
159
                        })
160

NEW
161
        return fig
×
162

163
    ### GRAPHING FUNCTION ON CLICK (side‐panel) ###
NEW
164
    if graphing_function is not None:
×
NEW
165
        @app.callback(
×
166
            Output("side-panel", "children"),
167
            Input("graph", "clickData"),
168
        )
NEW
169
        def display_click_side(clickData):
×
NEW
170
            if clickData is None:
×
NEW
171
                return no_update
×
172
            # identify clicked chamber
NEW
173
            pt = clickData["points"][0]
×
NEW
174
            cid = f"{pt['x']},{pt['y']}"
×
NEW
175
            sample = chamber_names.get(cid, "")
×
176

177
            # generate inset plot PNG
NEW
178
            fig2, ax2 = plt.subplots()
×
NEW
179
            ax2 = graphing_function(cid, ax2)
×
NEW
180
            fig2.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9)
×
NEW
181
            tmp = tempfile.NamedTemporaryFile().name + ".png"
×
NEW
182
            plt.savefig(tmp)
×
NEW
183
            plt.close(fig2)
×
NEW
184
            with open(tmp, "rb") as f:
×
NEW
185
                img_src = "data:image/png;base64," + base64.b64encode(f.read()).decode("utf-8")
×
186

187
            # return side‐panel contents
NEW
188
            return html.Div([
×
189
                html.H3(f"{cid}: {sample}", style={"textAlign": "center"}),
190
                html.Img(src=img_src, style={"width": "100%"})
191
            ])
192

NEW
193
    app.run_server()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc