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

ci: remove init, set tests to run on dev #49

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 0 additions & 52 deletions .github/workflows/init.yml

This file was deleted.

1 change: 1 addition & 0 deletions .github/workflows/test_and_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
branches:
- main
- dev

jobs:
ci:
Expand Down
46 changes: 28 additions & 18 deletions src/aind_qc_portal/projects/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import param

from aind_qc_portal.docdb.database import get_project_data
from aind_qc_portal.utils import format_link, qc_status_color_css, qc_status_link_html
from aind_qc_portal.utils import (
format_link,
qc_status_color_css,
qc_status_link_html,
)
from aind_data_schema.core.quality_control import QualityControl, Status

ALWAYS_COLUMNS = ["Subject ID", "Date"]
Expand Down Expand Up @@ -35,24 +39,24 @@ def __init__(self, project_name: str, **params):
self.project_name = project_name
self._df = pd.DataFrame(columns=["_id", "timestamp"])

self.subject_selector = pn.widgets.MultiChoice(
name="Subject ID"
)
self.columns_selector = pn.widgets.MultiChoice(
name="Columns"
)
self.derived_selector = pn.widgets.Select(name="Derived", options=["All", "Raw", "Derived"])
self.type_selector = pn.widgets.Select(
name="Type"
self.subject_selector = pn.widgets.MultiChoice(name="Subject ID")
self.columns_selector = pn.widgets.MultiChoice(name="Columns")
self.derived_selector = pn.widgets.Select(
name="Derived", options=["All", "Raw", "Derived"]
)
self.type_selector = pn.widgets.Select(name="Type")
self.status_selector = pn.widgets.Select(
name="QC Status", options=QC_STATUS_OPTIONS
)

self._get_assets()

self.subject_selector.options = self.subjects
self.columns_selector.options = [column for column in self.columns if column not in ALWAYS_COLUMNS + HIDDEN_COLUMNS]
self.columns_selector.options = [
column
for column in self.columns
if column not in ALWAYS_COLUMNS + HIDDEN_COLUMNS
]
self.type_selector.options = ["All"] + self.types

def _get_assets(self):
Expand Down Expand Up @@ -102,7 +106,9 @@ def _get_assets(self):

record_data = {
"_id": record.get("_id"),
"Raw Data": record.get("data_description", {}).get("data_level")
"Raw Data": record.get("data_description", {}).get(
"data_level"
)
== "raw",
"project_name": record.get("data_description", {}).get(
"project_name"
Expand Down Expand Up @@ -141,11 +147,16 @@ def _get_assets(self):
lambda x: ", ".join(x) if x else None
)
self._df["QC Status"] = self._df.apply(
lambda row: qc_status_link_html(row["QC Status"], row["qc_link"], row["QC Status"]), axis=1
lambda row: qc_status_link_html(
row["QC Status"], row["qc_link"], row["QC Status"]
),
axis=1,
)
print(self._df["QC Status"].values[0])

self._df.drop(columns=["qc_link", "operator", "session_start_time", "location"])
self._df.drop(
columns=["qc_link", "operator", "session_start_time", "location"]
)

# Sort dataframe by time and then by subject ID
self._df.sort_values(by="timestamp", ascending=True, inplace=True)
Expand All @@ -168,13 +179,12 @@ def _data_filtered(self) -> pd.DataFrame:

if self.derived_filter != "All":
filtered_df = filtered_df[
filtered_df["Raw Data"] == (True if self.derived_filter == "Raw" else False)
filtered_df["Raw Data"]
== (True if self.derived_filter == "Raw" else False)
]

if self.type_filter != "All":
filtered_df = filtered_df[
filtered_df["Type"] == self.type_filter
]
filtered_df = filtered_df[filtered_df["Type"] == self.type_filter]

if self.status_filter != "All":
filtered_df = filtered_df[
Expand Down
58 changes: 34 additions & 24 deletions src/aind_qc_portal/projects/project_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,11 @@ def __init__(self, project_name: str, dataset: ProjectDataset):
self.history_chart = self.history_panel()
if hasattr(self.history_chart, "selection"):
self.selection_history_chart = pn.bind(
self.selection_history_panel, self.history_chart.selection.param.brush
self.selection_history_panel,
self.history_chart.selection.param.brush,
)
else:
self.selection_history_chart = pn.widgets.StaticText(
value=""
)
self.selection_history_chart = pn.widgets.StaticText(value="")

@property
def has_data(self):
Expand All @@ -59,8 +58,7 @@ def get_asset_count(self):
return len(self.dataset.data_filtered())

def update_subject_selector(self, event):
"""Update the subject selector based on the brush selection
"""
"""Update the subject selector based on the brush selection"""
if event.new.get("Subject ID") is not None:
self.dataset.subject_selector.value = event.new["Subject ID"]
else:
Expand All @@ -76,8 +74,10 @@ def history_panel(self):
data = self.dataset.data

# Check that timestamp column has values
if data['timestamp'].isnull().all():
return pn.widgets.StaticText(value="Data processing error: project is missing timestamp data in some assets. Please reach out to scientific computing for help repairing your metadata.")
if data["timestamp"].isnull().all():
return pn.widgets.StaticText(
value="Data processing error: project is missing timestamp data in some assets. Please reach out to scientific computing for help repairing your metadata."
)

# Calculate the time range to show on the x axis
(min_range, max_range, range_unit, format) = df_timestamp_range(
Expand All @@ -102,7 +102,9 @@ def history_panel(self):
alt.Tooltip("Date:T", title="Date"),
],
color=alt.condition(
self.brush, alt.Color("Subject ID:N"), alt.value("lightgray")
self.brush,
alt.Color("Subject ID:N"),
alt.value("lightgray"),
),
href=alt.Href("qc_link:N"),
)
Expand All @@ -125,7 +127,9 @@ def selection_history_panel(self, selection):
data = self.dataset.data_filtered()

if data.empty:
return pn.widgets.StaticText(value="No data found for the selected filters")
return pn.widgets.StaticText(
value="No data found for the selected filters"
)

# Calculate the time range to show on the x axis
(min_range, max_range, range_unit, format) = df_timestamp_range(
Expand Down Expand Up @@ -170,12 +174,14 @@ def selection_history_panel(self, selection):

return pn.pane.Vega(chart, sizing_mode="stretch_width")

def _panel(self,
subject_filter,
derived_filter,
columns_filter,
type_filter,
status_filter) -> pn.Column:
def _panel(
self,
subject_filter,
derived_filter,
columns_filter,
type_filter,
status_filter,
) -> pn.Column:
"""Return panel object"""

self.dataset.subject_filter = subject_filter
Expand All @@ -192,11 +198,15 @@ def _panel(self,

def panel(self):

return pn.Column(self.history_chart, pn.bind(
self._panel,
subject_filter=self.dataset.subject_selector,
derived_filter=self.dataset.derived_selector,
columns_filter=self.dataset.columns_selector,
type_filter=self.dataset.type_selector,
status_filter=self.dataset.status_selector,
), styles=OUTER_STYLE)
return pn.Column(
self.history_chart,
pn.bind(
self._panel,
subject_filter=self.dataset.subject_selector,
derived_filter=self.dataset.derived_selector,
columns_filter=self.dataset.columns_selector,
type_filter=self.dataset.type_selector,
status_filter=self.dataset.status_selector,
),
styles=OUTER_STYLE,
)
4 changes: 1 addition & 3 deletions src/aind_qc_portal/qc_project_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,7 @@ def panel(self):
dataset.subject_selector.value = dataset.subject_filter
dataset.derived_selector.value = dataset.derived_filter
dataset.columns_selector.value = [
column
for column in dataset.columns_filter
if column not in ALWAYS_COLUMNS
column for column in dataset.columns_filter if column not in ALWAYS_COLUMNS
]
dataset.type_selector.value = dataset.type_filter
dataset.status_selector.value = dataset.status_filter
Expand Down
66 changes: 50 additions & 16 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,35 @@
df_timestamp_range,
replace_markdown_with_html,
qc_status_color_css,
bincount2D
bincount2D,
)


class TestUtils(unittest.TestCase):

def test_format_link(self):
self.assertEqual(format_link("http://example.com"), '<a href="http://example.com" target="_blank">link</a>')
self.assertEqual(format_link("http://example.com", "Example"), '<a href="http://example.com" target="_blank">Example</a>')
self.assertEqual(
format_link("http://example.com"),
'<a href="http://example.com" target="_blank">link</a>',
)
self.assertEqual(
format_link("http://example.com", "Example"),
'<a href="http://example.com" target="_blank">Example</a>',
)

def test_status_html(self):
self.assertEqual(qc_status_html(Status.PASS), '<span style="color:#1D8649;">Pass</span>')
self.assertEqual(qc_status_html(Status.PENDING), '<span style="color:#2A7DE1;">Pending</span>')
self.assertEqual(qc_status_html(Status.FAIL), '<span style="color:#FF5733;">Fail</span>')
self.assertEqual(
qc_status_html(Status.PASS),
'<span style="color:#1D8649;">Pass</span>',
)
self.assertEqual(
qc_status_html(Status.PENDING),
'<span style="color:#2A7DE1;">Pending</span>',
)
self.assertEqual(
qc_status_html(Status.FAIL),
'<span style="color:#FF5733;">Fail</span>',
)

def test_df_timestamp_range(self):
data = {
Expand All @@ -41,13 +56,24 @@ def test_df_timestamp_range(self):
self.assertEqual(fmt, "%b %d")

def test_md_style(self):
self.assertEqual(replace_markdown_with_html(12, "test"), '<span style="font-size:12pt">test</span>')
self.assertEqual(
replace_markdown_with_html(12, "test"),
'<span style="font-size:12pt">test</span>',
)

def test_qc_color(self):
self.assertEqual(qc_status_color_css("No QC"), "background-color: #FFB71B")
self.assertEqual(qc_status_color_css("Pass"), "background-color: #1D8649")
self.assertEqual(qc_status_color_css("Fail"), "background-color: #FF5733")
self.assertEqual(qc_status_color_css("Pending"), "background-color: #2A7DE1")
self.assertEqual(
qc_status_color_css("No QC"), "background-color: #FFB71B"
)
self.assertEqual(
qc_status_color_css("Pass"), "background-color: #1D8649"
)
self.assertEqual(
qc_status_color_css("Fail"), "background-color: #FF5733"
)
self.assertEqual(
qc_status_color_css("Pending"), "background-color: #2A7DE1"
)

def test_bincount2D(self):
x = np.array([1, 2, 2, 3])
Expand All @@ -57,17 +83,25 @@ def test_bincount2D(self):
self.assertTrue(np.array_equal(xscale, np.array([1, 2, 3])))
self.assertTrue(np.array_equal(yscale, np.array([4, 5, 6])))

@patch('aind_qc_portal.utils.pn')
@patch("aind_qc_portal.utils.pn")
def test_set_background(self, mock_pn):
mock_pn.config.raw_css = [] # Mock raw_css to ensure a clean state
mock_pn.state.location.query_params = {} # Mock query_params to ensure no background param
mock_pn.state.location.query_params = (
{}
) # Mock query_params to ensure no background param
format_css_background()
self.assertIn("background-color: #003057", mock_pn.config.raw_css[0]) # Default dark_blue color
self.assertIn(
"background-color: #003057", mock_pn.config.raw_css[0]
) # Default dark_blue color

mock_pn.config.raw_css = [] # Reset mock raw_css
mock_pn.state.location.query_params = {"background": "light_blue"} # Mock query_params with light_blue
mock_pn.state.location.query_params = {
"background": "light_blue"
} # Mock query_params with light_blue
format_css_background()
self.assertIn("background-color: #2A7DE1", mock_pn.config.raw_css[0]) # light_blue color
self.assertIn(
"background-color: #2A7DE1", mock_pn.config.raw_css[0]
) # light_blue color


if __name__ == "__main__":
Expand Down
Loading