Skip to content

Commit d5996f1

Browse files
committed
added colormap, create equation table
1 parent 2ecfa1e commit d5996f1

File tree

5 files changed

+90
-58
lines changed

5 files changed

+90
-58
lines changed

qiita_db/meta_util.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ def update_resource_allocation_redis(active=True):
593593
if len(df) == 0:
594594
continue
595595

596-
fig, axs = resource_allocation_plot(df, cname, sname, col_name)
596+
fig, axs = resource_allocation_plot(df, col_name)
597597
titles = [0, 0]
598598
images = [0, 0]
599599

@@ -605,21 +605,19 @@ def update_resource_allocation_redis(active=True):
605605
# only time
606606
new_fig = plt.figure()
607607
new_ax = new_fig.add_subplot(111)
608-
609-
scatter_data = ax.collections[0]
610-
new_ax.scatter(scatter_data.get_offsets()[:, 0],
611-
scatter_data.get_offsets()[:, 1],
612-
s=scatter_data.get_sizes(), label="data")
613-
614608
line = ax.lines[0]
615609
new_ax.plot(line.get_xdata(), line.get_ydata(),
616610
linewidth=1, color='orange')
617-
618-
if len(ax.collections) > 1:
619-
failure_data = ax.collections[1]
620-
new_ax.scatter(failure_data.get_offsets()[:, 0],
621-
failure_data.get_offsets()[:, 1],
622-
color='red', s=3, label="failures")
611+
612+
handles, labels = ax.get_legend_handles_labels()
613+
for handle, label, scatter_data in zip(handles,
614+
labels,
615+
ax.collections):
616+
color = handle.get_facecolor()
617+
new_ax.scatter(scatter_data.get_offsets()[:, 0],
618+
scatter_data.get_offsets()[:, 1],
619+
s=scatter_data.get_sizes(), label=label,
620+
color=color)
623621

624622
new_ax.set_xscale('log')
625623
new_ax.set_yscale('log')

qiita_db/support_files/patches/93.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,11 @@ CREATE INDEX IF NOT EXISTS processing_job_command_parameters_payload ON qiita.pr
6262
-- Addding contraints for the slurm_reservation column
6363
ALTER TABLE qiita.analysis DROP CONSTRAINT IF EXISTS analysis_slurm_reservation_valid_chars;
6464
ALTER TABLE qiita.analysis ADD CONSTRAINT analysis_slurm_reservation_valid_chars CHECK ( slurm_reservation ~ '^[a-zA-Z0-9_]*$' );
65+
66+
-- Jan 7, 2025
67+
-- Adding a table for formulas for resource allocations
68+
CREATE TABLE qiita.allocation_equations (
69+
equation_id SERIAL PRIMARY KEY,
70+
equation_name TEXT NOT NULL,
71+
expression TEXT NOT NULL
72+
);

qiita_db/test/test_util.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,8 +1327,7 @@ def setUp(self):
13271327

13281328
def test_plot_return(self):
13291329
# check the plot returns correct objects
1330-
fig1, axs1 = qdb.util.resource_allocation_plot(
1331-
self.df, self.cname, self.sname, self.col_name)
1330+
fig1, axs1 = qdb.util.resource_allocation_plot(self.df, self.col_name)
13321331
self.assertIsInstance(
13331332
fig1, Figure,
13341333
"Returned object fig1 is not a Matplotlib Figure")
@@ -1345,13 +1344,12 @@ def test_minimize_const(self):
13451344
fig, axs = plt.subplots(ncols=2, figsize=(10, 4), sharey=False)
13461345

13471346
bm, options = qdb.util._resource_allocation_plot_helper(
1348-
self.df, axs[0], self.cname, self.sname, 'MaxRSSRaw',
1349-
qdb.util.MODELS_MEM, self.col_name)
1347+
self.df, axs[0], 'MaxRSSRaw', qdb.util.MODELS_MEM, self.col_name)
13501348
# check that the algorithm chooses correct model for MaxRSSRaw and
13511349
# has 0 failures
13521350
k, a, b = options.x
1353-
failures_df = qdb.util._resource_allocation_failures(
1354-
self.df, k, a, b, bm, self.col_name, 'MaxRSSRaw')
1351+
failures_df = qdb.util._resource_allocation_success_failures(
1352+
self.df, k, a, b, bm, self.col_name, 'MaxRSSRaw')[-1]
13551353
failures = failures_df.shape[0]
13561354
self.assertEqual(bm, qdb.util.mem_model3,
13571355
msg=f"""Best memory model
@@ -1367,11 +1365,10 @@ def test_minimize_const(self):
13671365
# check that the algorithm chooses correct model for ElapsedRaw and
13681366
# has 1 failure
13691367
bm, options = qdb.util._resource_allocation_plot_helper(
1370-
self.df, axs[1], self.cname, self.sname, 'ElapsedRaw',
1371-
qdb.util.MODELS_TIME, self.col_name)
1368+
self.df, axs[1], 'ElapsedRaw', qdb.util.MODELS_TIME, self.col_name)
13721369
k, a, b = options.x
1373-
failures_df = qdb.util._resource_allocation_failures(
1374-
self.df, k, a, b, bm, self.col_name, 'ElapsedRaw')
1370+
failures_df = qdb.util._resource_allocation_success_failures(
1371+
self.df, k, a, b, bm, self.col_name, 'ElapsedRaw')[-1]
13751372
failures = failures_df.shape[0]
13761373

13771374
self.assertEqual(bm, qdb.util.time_model1,

qiita_db/util.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
from email.mime.text import MIMEText
7575

7676
import matplotlib.pyplot as plt
77+
from matplotlib import colormaps
7778
import numpy as np
7879
import pandas as pd
7980
from io import StringIO
@@ -2363,17 +2364,13 @@ def send_email(to, subject, body):
23632364
smtp.close()
23642365

23652366

2366-
def resource_allocation_plot(df, cname, sname, col_name):
2367+
def resource_allocation_plot(df, col_name):
23672368
"""Builds resource allocation plot for given filename and jobs
23682369
23692370
Parameters
23702371
----------
23712372
file : str, required
23722373
Builds plot for the specified file name. Usually provided as tsv.gz
2373-
cname: str, required
2374-
Specified job type
2375-
sname: str, required
2376-
Specified job sub type.
23772374
col_name: str, required
23782375
Specifies x axis for the graph
23792376
@@ -2392,12 +2389,12 @@ def resource_allocation_plot(df, cname, sname, col_name):
23922389
ax = axs[0]
23932390
# models for memory
23942391
_resource_allocation_plot_helper(
2395-
df, ax, cname, sname, "MaxRSSRaw", MODELS_MEM, col_name)
2392+
df, ax, "MaxRSSRaw", MODELS_MEM, col_name)
23962393

23972394
ax = axs[1]
23982395
# models for time
23992396
_resource_allocation_plot_helper(
2400-
df, ax, cname, sname, "ElapsedRaw", MODELS_TIME, col_name)
2397+
df, ax, "ElapsedRaw", MODELS_TIME, col_name)
24012398

24022399
return fig, axs
24032400

@@ -2442,7 +2439,7 @@ def retrieve_resource_data(cname, sname, version, columns):
24422439

24432440

24442441
def _resource_allocation_plot_helper(
2445-
df, ax, cname, sname, curr, models, col_name):
2442+
df, ax, curr, models, col_name):
24462443
"""Helper function for resource allocation plot. Builds plot for MaxRSSRaw
24472444
and ElapsedRaw
24482445
@@ -2459,14 +2456,14 @@ def _resource_allocation_plot_helper(
24592456
col_name: str, required
24602457
Specifies x axis for the graph
24612458
curr: str, required
2462-
Either MaxRSSRaw or ElapsedRaw
2459+
Either MaxRSSRaw or ElapsedRaw (y axis)
24632460
models: list, required
24642461
List of functions that will be used for visualization
24652462
24662463
"""
24672464

24682465
x_data, y_data = df[col_name], df[curr]
2469-
ax.scatter(x_data, y_data, s=2, label="data")
2466+
# ax.scatter(x_data, y_data, s=2, label="data")
24702467
d = dict()
24712468
for index, row in df.iterrows():
24722469
x_value = row[col_name]
@@ -2518,13 +2515,21 @@ def _resource_allocation_plot_helper(
25182515
str(timedelta(seconds=round(cmin_value, 2))).rstrip('0').rstrip('.')
25192516

25202517
x_plot = np.array(df[col_name])
2521-
failures_df = _resource_allocation_failures(
2518+
success_df, failures_df = _resource_allocation_success_failures(
25222519
df, k, a, b, best_model, col_name, curr)
25232520
failures = failures_df.shape[0]
2524-
25252521
ax.scatter(failures_df[col_name], failures_df[curr], color='red', s=3,
25262522
label="failures")
2527-
2523+
success_df['node_name'] = success_df['node_name'].fillna('unknown')
2524+
slurm_hosts = set(success_df['node_name'].tolist())
2525+
cmap = colormaps.get_cmap('Accent').resampled(len(slurm_hosts))
2526+
colors = [cmap(
2527+
i / (len(slurm_hosts) - 1)) for i in range(len(slurm_hosts))]
2528+
2529+
for i, host in enumerate(slurm_hosts):
2530+
host_df = success_df[success_df['node_name'] == host]
2531+
ax.scatter(host_df[col_name], host_df[curr], color=colors[i], s=3,
2532+
label=host)
25282533
ax.set_title(
25292534
f'k||a||b: {k}||{a}||{b}\n'
25302535
f'model: {get_model_name(best_model)}\n'
@@ -2590,8 +2595,10 @@ def _resource_allocation_calculate(
25902595
options = minimize(_resource_allocation_custom_loss, init,
25912596
args=(x, y, model, middle))
25922597
k, a, b = options.x
2593-
failures_df = _resource_allocation_failures(
2594-
df, k, a, b, model, col_name, type_)
2598+
# important: here we take the 2nd (last) value of tuple since
2599+
# the helper function returns success, then failures.
2600+
failures_df = _resource_allocation_success_failures(
2601+
df, k, a, b, model, col_name, type_)[-1]
25952602
y_plot = model(x, k, a, b)
25962603
if not any(y_plot):
25972604
continue
@@ -2676,9 +2683,9 @@ def _resource_allocation_custom_loss(params, x, y, model, p):
26762683
return np.mean(weighted_residuals)
26772684

26782685

2679-
def _resource_allocation_failures(df, k, a, b, model, col_name, type_):
2686+
def _resource_allocation_success_failures(df, k, a, b, model, col_name, type_):
26802687
"""Helper function for resource allocation plot. Creates a dataframe with
2681-
failures.
2688+
successes and failures given current model.
26822689
26832690
Parameters
26842691
----------
@@ -2699,14 +2706,19 @@ def _resource_allocation_failures(df, k, a, b, model, col_name, type_):
26992706
27002707
Returns
27012708
----------
2702-
pandas.Dataframe
2703-
Dataframe containing failures for current type.
2709+
tuple with:
2710+
pandas.Dataframe
2711+
Dataframe containing successes for current type.
2712+
pandas.Dataframe
2713+
Dataframe containing failures for current type.
27042714
"""
27052715

27062716
x_plot = np.array(df[col_name])
27072717
df[f'c{type_}'] = model(x_plot, k, a, b)
2718+
success_df = df[df[type_] <= df[f'c{type_}']]
27082719
failures_df = df[df[type_] > df[f'c{type_}']]
2709-
return failures_df
2720+
2721+
return (success_df, failures_df)
27102722

27112723

27122724
def MaxRSS_helper(x):

qiita_pet/templates/resources.html

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -114,19 +114,29 @@ <h3>Generated on: {{time}} </h3>
114114
toggleDataVisibility(true);
115115
{% end %}
116116

117-
const commandsConst = JSON.parse(`{% raw commands %}`);
118-
const softwareSelect = document.getElementById('software');
119-
const versionSelect = document.getElementById('version');
120-
const commandSelect = document.getElementById('command');
121-
122-
// Populate software options
123-
for (const software in commandsConst) {
124-
const option = document.createElement('option');
125-
option.value = software;
126-
option.textContent = software;
127-
softwareSelect.appendChild(option);
128-
}
117+
const commandsConst = JSON.parse(`{% raw commands %}`);
118+
const softwareSelect = document.getElementById('software');
119+
const versionSelect = document.getElementById('version');
120+
const commandSelect = document.getElementById('command');
121+
122+
// Populate software options
123+
for (const software in commandsConst) {
124+
const option = document.createElement('option');
125+
option.value = software;
126+
option.textContent = software;
127+
softwareSelect.appendChild(option);
128+
}
129129

130+
// If there's only one software option, select it automatically
131+
function autoSelectIfSingle(selectElem) {
132+
const realOptions = Array.from(selectElem.options).filter(opt => opt.value !== "");
133+
if (realOptions.length === 1) {
134+
selectElem.value = realOptions[0].value;
135+
// Trigger the change event to populate next select
136+
const event = new Event('change', { bubbles: true });
137+
selectElem.dispatchEvent(event);
138+
}
139+
}
130140

131141
function populateVersions(software) {
132142
versionSelect.innerHTML = '<option value="">Select Version</option>';
@@ -140,9 +150,10 @@ <h3>Generated on: {{time}} </h3>
140150
versionSelect.appendChild(option);
141151
}
142152
}
153+
// Auto-select if only one version available
154+
autoSelectIfSingle(versionSelect);
143155
}
144156

145-
146157
function populateCommands(software, version) {
147158
commandSelect.innerHTML = '<option value="">Select Command</option>';
148159

@@ -154,6 +165,9 @@ <h3>Generated on: {{time}} </h3>
154165
commandSelect.appendChild(option);
155166
});
156167
}
168+
169+
// Auto-select if only one command available
170+
autoSelectIfSingle(commandSelect);
157171
}
158172

159173
function sendPostRequest(software, version, command) {
@@ -224,8 +238,7 @@ <h3>Generated on: {{time}} </h3>
224238
}
225239

226240
bootstrapAlert("Data updated successfully", "success", 2200);
227-
}
228-
else if (response.status === "no_data") {
241+
} else if (response.status === "no_data") {
229242
toggleDataVisibility(false);
230243
$('#default-message').html('<h3>No data available for the selected options.</h3>');
231244
bootstrapAlert("No data available", "info", 2200);
@@ -264,5 +277,9 @@ <h3>Generated on: {{time}} </h3>
264277
sendPostRequest(selectedSoftware, selectedVersion, selectedCommand);
265278
}
266279
});
280+
281+
// Attempt auto-select after initial population of software
282+
autoSelectIfSingle(softwareSelect);
283+
267284
</script>
268285
{% end %}

0 commit comments

Comments
 (0)