Skip to content

Commit

Permalink
Merge pull request #84 from lsst-dm/tickets/DM-45287
Browse files Browse the repository at this point in the history
DM-45287 Introduce script details page to CM web app
  • Loading branch information
emanehab99 authored Jul 31, 2024
2 parents b81c799 + 1027353 commit 53f7da0
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 36 deletions.
51 changes: 44 additions & 7 deletions src/lsst/cmservice/web_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
from lsst.cmservice.config import config

from lsst.cmservice.web_app.pages.campaigns import search_campaigns, get_campaign_details
from lsst.cmservice.web_app.pages.steps import get_campaign_steps, get_step_details, get_campaign_by_id
from lsst.cmservice.web_app.pages.steps import (
get_campaign_steps,
get_step_details,
get_campaign_by_id,
)
from lsst.cmservice.web_app.pages.step_details import get_step_details_by_id
from lsst.cmservice.web_app.pages.group_details import get_group_by_id
from lsst.cmservice.web_app.pages.job_details import get_job_by_id
from lsst.cmservice.web_app.pages.script_details import get_script_by_id


@asynccontextmanager
Expand Down Expand Up @@ -67,7 +72,6 @@ async def get_campaigns(
try:
async with session.begin():
campaigns = await db.Campaign.get_rows(session)
# campaigns = await get_campaign_list(session)
campaigns_list = []
for campaign in campaigns:
campaign_details = await get_campaign_details(session, campaign)
Expand Down Expand Up @@ -176,12 +180,13 @@ async def get_step(
return templates.TemplateResponse(f"Something went wrong {e}")


@web_app.get("/campaign/{campaign_id}/{step_id}/{group_id}/", response_class=HTMLResponse)
@web_app.get("/group/{group_id}/", response_class=HTMLResponse)
@web_app.get("/group/{campaign_id}/{step_id}/{group_id}/", response_class=HTMLResponse)
async def get_group(
request: Request,
campaign_id: int,
step_id: int,
group_id: int,
campaign_id: int | None = None,
step_id: int | None = None,
session: async_scoped_session = Depends(db_session_dependency),
) -> HTMLResponse:
try:
Expand All @@ -190,8 +195,8 @@ async def get_group(
name="group_details.html",
request=request,
context={
"campaign_id": campaign_id,
"step_id": step_id,
# "campaign_id": campaign_id,
# "step_id": step_id,
"group": group_details,
"jobs": jobs,
"scripts": scripts,
Expand Down Expand Up @@ -231,6 +236,38 @@ async def get_job(
return templates.TemplateResponse(f"Something went wrong {e}")


@web_app.get("/script/{campaign_id}/{script_id}/", response_class=HTMLResponse)
@web_app.get("/script/{campaign_id}/{step_id}/{script_id}/", response_class=HTMLResponse)
@web_app.get("/script/{campaign_id}/{step_id}/{group_id}/{script_id}/", response_class=HTMLResponse)
@web_app.get("/script/{campaign_id}/{step_id}/{group_id}/{job_id}/{script_id}/", response_class=HTMLResponse)
async def get_script(
request: Request,
script_id: int,
campaign_id: int | None = None,
step_id: int | None = None,
group_id: int | None = None,
job_id: int | None = None,
session: async_scoped_session = Depends(db_session_dependency),
) -> HTMLResponse:
try:
script_details = await get_script_by_id(session, script_id)
return templates.TemplateResponse(
name="script_details.html",
request=request,
context={
"script": script_details,
"campaign_id": campaign_id,
"step_id": step_id,
"group_id": group_id,
"job_id": job_id,
},
)
except Exception as e:
print(e)
traceback.print_tb(e.__traceback__)
return templates.TemplateResponse(f"Something went wrong {e}")


@web_app.get("/layout/", response_class=HTMLResponse)
async def test_layout(request: Request) -> HTMLResponse:
return templates.TemplateResponse("mockup.html", {"request": request})
Expand Down
2 changes: 1 addition & 1 deletion src/lsst/cmservice/web_app/pages/campaigns.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async def get_campaign_details(session: async_scoped_session, campaign: Campaign
lambda group: map_status(group.status) in ["NEED_ATTENTION", "FAILED"],
groups,
)
scripts = await campaign.get_scripts(session)
scripts = await campaign.get_all_scripts(session)
no_scripts_completed = len([script for script in scripts if script.status == StatusEnum.accepted])
need_attention_scripts = filter(
lambda script: map_status(script.status) in ["NEED_ATTENTION", "FAILED"],
Expand Down
11 changes: 10 additions & 1 deletion src/lsst/cmservice/web_app/pages/group_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
async def get_group_by_id(
session: async_scoped_session,
group_id: int,
campaign_id: int | None = None,
step_id: int | None = None,
) -> tuple[dict[str, Any] | None, list[dict[Any, Any]] | None, list[dict[Any, Any]] | None]:
q = select(Group).where(Group.id == group_id)
async with session.begin_nested():
Expand All @@ -20,6 +22,11 @@ async def get_group_by_id(
scripts = None

if group is not None:
if campaign_id is None:
campaign = await group.get_campaign(session)
c_id = campaign.id
s_id = group.parent_id if step_id is None else step_id

wms_reports_dict = await group.get_wms_reports(session)
wms_report = [y.__dict__ for y in wms_reports_dict.reports.values()]

Expand Down Expand Up @@ -58,10 +65,12 @@ async def get_group_by_id(
"superseded": group.superseded,
"status": map_status(group.status),
"data": group.data,
"collections": collections,
"collections": {key: collections[key] for key in collections if key.startswith("group_")},
"child_config": group.child_config,
"wms_report": wms_report,
"aggregated_wms_report": aggregated_report_dict,
"step_id": s_id,
"campaign_id": c_id,
}

return group_details, jobs, scripts
Expand Down
2 changes: 1 addition & 1 deletion src/lsst/cmservice/web_app/pages/job_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ async def get_job_by_id(
"superseded": job.superseded,
"status": map_status(job.status),
"data": job.data,
"collections": collections,
"collections": {key: collections[key] for key in collections if key.startswith("job_")},
"child_config": job.child_config,
"wms_report": wms_report,
"aggregated_wms_report": aggregated_report_dict,
Expand Down
74 changes: 74 additions & 0 deletions src/lsst/cmservice/web_app/pages/script_details.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from typing import Any
from sqlalchemy import select
from sqlalchemy.ext.asyncio import async_scoped_session

from lsst.cmservice.db import Script
from lsst.cmservice.common.enums import LevelEnum
from lsst.cmservice.web_app.utils.utils import map_status


def is_script_collection(collection: tuple[str, str]) -> bool:
key, value = collection
return not (
key.startswith("campaign_")
or key.startswith("step_")
or key.startswith("group_")
or key.startswith("job_")
or key == "out"
)


async def get_script_by_id(
session: async_scoped_session,
script_id: int,
campaign_id: int | None = None,
step_id: int | None = None,
group_id: int | None = None,
job_id: int | None = None,
) -> dict[str, Any] | None:
q = select(Script).where(Script.id == script_id)

async with session.begin_nested():
results = await session.scalars(q)
script = results.one()
script_details = None
c_id = None
s_id = None
g_id = None
j_id = None

if script is not None:
if campaign_id is None:
campaign = await script.get_campaign(session)
c_id = campaign.id
parent = await script.get_parent(session)
if not parent.level == LevelEnum.campaign:
if parent.level == LevelEnum.step:
s_id = script.parent_id if step_id is None else step_id
elif parent.level == LevelEnum.group:
g_id = script.parent_id if step_id is None else group_id
elif parent.level == LevelEnum.job:
j_id = script.parent_id if step_id is None else job_id
collections = await script.resolve_collections(session)
filtered_collections = dict(
filter(
lambda collection: is_script_collection(collection),
collections.items(),
),
)
script_details = {
"id": script.id,
"name": script.name,
"campaign_id": c_id,
"step_id": s_id,
"group_id": g_id,
"job_id": j_id,
"fullname": script.fullname,
"superseded": script.superseded,
"status": map_status(script.status),
"data": script.data,
"collections": filtered_collections,
"child_config": script.child_config,
}

return script_details
2 changes: 1 addition & 1 deletion src/lsst/cmservice/web_app/pages/step_details.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async def get_step_details_by_id(
collections = await step.resolve_collections(session)
step_details = await get_step_details(session, step)
# get step dicts
step_details["collections"] = collections
step_details["collections"] = {key: collections[key] for key in collections if key.startswith("step_")}
step_details["data"] = step.data
step_details["child_config"] = step.child_config
groups = await get_step_groups(session, step)
Expand Down
13 changes: 13 additions & 0 deletions src/lsst/cmservice/web_app/static/css/output.css
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,10 @@ video {
margin-left: auto;
}

.mr-2 {
margin-right: 0.5rem;
}

.mt-1 {
margin-top: 0.25rem;
}
Expand Down Expand Up @@ -742,6 +746,10 @@ video {
height: 100%;
}

.h-24 {
height: 6rem;
}

.min-h-full {
min-height: 100%;
}
Expand Down Expand Up @@ -1467,6 +1475,11 @@ video {
color: rgb(31 41 55 / var(--tw-text-opacity));
}

.hover\:text-teal-500:hover {
--tw-text-opacity: 1;
color: rgb(20 184 166 / var(--tw-text-opacity));
}

.focus\:outline-none:focus {
outline: 2px solid transparent;
outline-offset: 2px;
Expand Down
7 changes: 2 additions & 5 deletions src/lsst/cmservice/web_app/templates/campaign_card.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,19 @@
<span class="font-semibold inline-flex pl-3 text-xs uppercase"> Last updated 6:42pm Mon 7 Feb </span>
</div>
<div class="mt-3 pb-3">
<!-- <p class="text-sm font-normal leading-6 text-gray-900 pl-3">-->
<div class="block text-xs pl-3">LSST Version: {{campaign.lsst_version}}</div>
<div class="block text-xs pl-3">out: {{campaign.out}}</div>
<div class="block text-xs pl-3">source: {{campaign.source}}</div>
<!-- </p>-->
<div class="block text-xs pt-2 pl-3 text-green-700">{{campaign.scripts_completed}}</div>
<div class="block text-xs pt-2 pl-3 text-green-700">{{campaign.groups_completed}}</div>
<div class="block text-xs pt-2 pl-3 text-red-400">
{% for group in campaign.need_attention_groups %}
<a class="pr-2" href="#">{{group.fullname}}</a>
<a class="pr-2" href="{{ url_for('get_group', campaign_id=campaign.id, step_id=group.parent_id, group_id=group.id) }}">{{group.fullname}}</a>
{% endfor %}
</div>
<div class="block text-xs pt-2 pl-3 text-red-400">
{% for script in campaign.need_attention_scripts %}
<a class="pr-2" href="#">{{script.name}}</a>
<a class="pr-2" href="{{ url_for('get_script', campaign_id=campaign.id, script_id=script.id) }}">{{script.name}}</a>
{% endfor %}
</div>
</div>
Expand All @@ -37,7 +35,6 @@
</span></a>
</div>
<button type="button" class="absolute right-3 top-6 text-teal-600" aria-expanded="false" aria-hidden="true" aria-haspopup="true">
<!-- <span class="absolute right-6 top-6 text-teal-400" aria-hidden="true">-->
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" stroke="currentColor">
<path d="M3 10a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0zM8.5 10a1.5 1.5 0 113 0 1.5 1.5 0 01-3 0zM15.5 8.5a1.5 1.5 0 100 3 1.5 1.5 0 000-3z" />
</svg>
Expand Down
31 changes: 24 additions & 7 deletions src/lsst/cmservice/web_app/templates/group_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
<svg class="h-5 w-5 flex-shrink-0 text-gray-300" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<a href="{{ url_for('get_steps', campaign_id=campaign_id) }}" class="ml-2 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">{{ fullname[1] }}</a>
<a href="{{ url_for('get_steps', campaign_id=group.campaign_id) }}" class="ml-2 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">{{ fullname[1] }}</a>
</div>
</li>
<li>
<div class="flex items-center">
<svg class="h-5 w-5 flex-shrink-0 text-gray-300" fill="currentColor" viewBox="0 0 20 20" aria-hidden="true">
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<a href="{{ url_for('get_step', campaign_id=campaign_id, step_id=step_id) }}" class="ml-2 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">{{ fullname[2] }}</a>
<a href="{{ url_for('get_step', campaign_id=group.campaign_id, step_id=group.step_id) }}" class="ml-2 text-sm font-medium text-gray-500 hover:text-gray-700" aria-current="page">{{ fullname[2] }}</a>
</div>
</li>
</ol>
Expand Down Expand Up @@ -119,7 +119,7 @@
init(params) {
let jobName = document.createElement('a');
jobName.textContent = params.data.name;
jobName.href = `{{url_for("get_job", campaign_id=campaign_id, step_id=step_id, group_id=group.id, job_id="${params.data.id}")}}`;
jobName.href = `{{url_for("get_job", campaign_id=group.campaign_id, step_id=group.step_id, group_id=group.id, job_id="${params.data.id}")}}`;
jobName.setAttribute('class', 'font-bold hover:text-gray-500');
this.eGui = document.createElement('span');
this.eGui.appendChild(jobName)
Expand All @@ -133,14 +133,31 @@
return false
}
}
class ScriptNameRenderer {
eGui;

init(params) {
let scriptName = document.createElement('a');
scriptName.textContent = params.data.name;
scriptName.href = `{{url_for("get_script", campaign_id=group.campaign_id, step_id=group.step_id, group_id=group.id, script_id="${params.data.id}")}}`;
scriptName.setAttribute('class', 'font-bold hover:text-gray-500');
this.eGui = document.createElement('span');
this.eGui.appendChild(scriptName)
}

getGui() {
return this.eGui;
}

refresh(params) {
return false
}
}
const groupScripts = {{scripts|tojson}};
const scriptsGridOptions = {
rowData: groupScripts,
columnDefs: [
{
field: "name",
flex: 1,
},
{field: "name", flex: 1, cellRenderer: ScriptNameRenderer,},
{field: "status", flex: 1},
{field: "superseded", flex: 1}
]
Expand Down
Loading

0 comments on commit 53f7da0

Please sign in to comment.