Skip to content

Commit

Permalink
administration: customize jobs views
Browse files Browse the repository at this point in the history
  • Loading branch information
kpsherva committed Aug 23, 2024
1 parent b668c7c commit 03de8c4
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 162 deletions.
56 changes: 39 additions & 17 deletions invenio_jobs/administration/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from invenio_jobs.config import JOBS_QUEUES
from invenio_jobs.models import Task
from invenio_jobs.services.schema import RunSchema
from invenio_jobs.services.ui_schema import ScheduleUISchema


Expand All @@ -36,6 +37,21 @@ class JobsAdminMixin:
search_sort_config_name = "JOBS_SORT_OPTIONS"
search_facets_config_name = "JOBS_FACETS"

actions = {
"schedule": {
"text": "Schedule",
"payload_schema": ScheduleUISchema,
"order": 1,
"icon": "calendar",
},
"runs": {
"text": "Run now",
"payload_schema": RunSchema,
"order": 2,
"icon": "play",
},
}


class JobsListView(JobsAdminMixin, AdminResourceListView):
"""Configuration for Jobs list view."""
Expand All @@ -56,13 +72,6 @@ class JobsListView(JobsAdminMixin, AdminResourceListView):
"user": {"text": _("Started by"), "order": 4, "width": 3},
"next_run": {"text": _("Next run"), "order": 5, "width": 3},
}
actions = {
"schedule": {
"text": "Schedule",
"payload_schema": ScheduleUISchema,
"order": 1,
}
}

@staticmethod
def disabled():
Expand All @@ -73,12 +82,9 @@ def disabled():
class JobsDetailsView(JobsAdminMixin, AdminResourceListView):
"""Configuration for Jobs detail view which shows runs."""

def get_api_endpoint(self, pid_value=None):
"""overwrite get_api_endpoint to accept pid_value."""
return f"/api/jobs/{pid_value}/runs"

url = "/jobs/<pid_value>"
search_request_headers = {"Accept": "application/json"}
request_headers = {"Accept": "application/json"}
name = "job-details"
resource_config = "runs_resource"
title = "Job Details"
Expand All @@ -97,6 +103,27 @@ def get_api_endpoint(self, pid_value=None):
"action": {"text": _("Action"), "order": 5, "width": 2},
}

def get_api_endpoint(self, pid_value=None):
"""overwrite get_api_endpoint to accept pid_value."""
return f"/api/jobs/{pid_value}/runs"

def get_details_api_endpoint(self):
api_url_prefix = current_app.config["SITE_API_URL"]
slash_tpl = "/" if not self.api_endpoint.startswith("/") else ""

if not self.api_endpoint.startswith(api_url_prefix):
return f"{api_url_prefix}{slash_tpl}{self.api_endpoint}"

return f"{slash_tpl}{self.api_endpoint}"

def get_context(self, **kwargs):
ctx = super().get_context(**kwargs)
ctx["request_headers"] = self.request_headers
ctx["ui_config"] = self.item_field_list
ctx["name"] = self.name
ctx["api_endpoint"] = self.get_details_api_endpoint()
return ctx


class JobsFormMixin:
"""Mixin class for form fields."""
Expand All @@ -108,7 +135,7 @@ def form_fields(self):
{"title_l10n": str(queue["title"]), "id": queue["name"]}
for queue in JOBS_QUEUES.values()
]
tasks = [{"title_l10n": name, "id": name} for name, t in Task.all().items()]
tasks = [{"title_l10n": t.title, "id": name} for name, t in Task.all().items()]
return {
"title": {
"order": 1,
Expand Down Expand Up @@ -138,11 +165,6 @@ def form_fields(self):
"order": 5,
"text": _("Active"),
},
"default_args": {
"order": 6,
"text": _("Default Arguments"),
"description": _("A task for the job run."),
},
"created": {"order": 7},
"updated": {"order": 8},
}
Expand Down
10 changes: 10 additions & 0 deletions invenio_jobs/administration/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ class RunsListView(AdminResourceListView):
display_delete = False
display_edit = False
display_create = False
actions = None

# item_field_list = {
# "job": {"text": _("Jobs"), "order": 1, "width": 3},
# "active": {"text": _("Status"), "order": 2, "width": 2},
# "last_run_start_time": {"text": _("Last run"), "order": 3, "width": 3},
# "user": {"text": _("Started by"), "order": 4, "width": 3},
# "next_run": {"text": _("Next run"), "order": 5, "width": 3},
# }

Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ export class JobActions extends Component {
}

onModalTriggerClick = (e, { payloadSchema, dataName, dataActionKey }) => {
const { resource } = this.props;
const { modalOpen } = this.state;

const { resource, actions: actionsConfig } = this.props;
if (dataActionKey === "schedule") {
this.setState({
modalOpen: true,
Expand All @@ -46,6 +45,8 @@ export class JobActions extends Component {
actionSuccessCallback={this.handleSuccess}
actionCancelCallback={this.closeModal}
resource={resource}
actionPayload={resource}
actionConfig={actionsConfig[dataActionKey]}
/>
),
});
Expand All @@ -61,50 +62,41 @@ export class JobActions extends Component {
};

handleSuccess = () => {
const {resource} = this.props;
this.setState({
modalOpen: false,
modalHeader: undefined,
modalBody: undefined,
});
setTimeout(() => {
window.location.reload();
window.location = resource.links.self_admin_html;
}, 1000);
};

render() {
const { actions, Element, resource } = this.props;
const { modalOpen, modalHeader, modalBody } = this.state;

return (
<>
{Object.entries(actions).map(([actionKey, actionConfig]) => {
if (actionKey === "schedule") {
return (
<Element
key={actionKey}
onClick={this.onModalTriggerClick}
payloadSchema={actionConfig.payload_schema}
dataName={actionConfig.text}
dataActionKey={actionKey}
icon
labelPosition="left"
>
<Icon name="calendar" />
{actionConfig.text}
</Element>
);
} else {
return (
<Element
key={actionKey}
onClick={this.onModalTriggerClick}
payloadSchema={actionConfig.payload_schema}
dataName={actionConfig.text}
dataActionKey={actionKey}
>
{actionConfig.text}
</Element>
);
}
const icon = actionConfig.icon;
const labelPos = icon ? "left" : null;
return (
<Element
key={actionKey}
onClick={this.onModalTriggerClick}
payloadSchema={actionConfig.payload_schema}
dataName={actionConfig.text}
dataActionKey={actionKey}
basic
icon={!_isEmpty(icon)}
labelPosition={labelPos}
>
{!_isEmpty(icon) && <Icon name={icon} />}
{actionConfig.text}
</Element>
);
})}
<ActionModal modalOpen={modalOpen} resource={resource}>
{modalHeader && <Modal.Header>{modalHeader}</Modal.Header>}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// This file is part of Invenio
// Copyright (C) 2024 CERN.
//
// Invenio RDM is free software; you can redistribute it and/or modify it
// under the terms of the MIT License; see LICENSE file for more details.

import {
NotificationController,
initDefaultSearchComponents,
AdminDetailsView,
} from "@js/invenio_administration";
import { createSearchAppInit } from "@js/invenio_search_ui";
import { RunActionForm } from "./RunActionForm";
import _get from "lodash/get";
import React from "react";
import ReactDOM from "react-dom";
import { JobRunsHeader } from "./JobRunsHeader";
import { JobSearchLayout } from "./JobSearchLayout";
import { SearchResultItemLayout } from "./RunsSearchResultItemLayout";
import { OverridableContext, overrideStore } from "react-overridable";

const overriddenComponents = overrideStore.getAll();

const domContainer = document.getElementById("invenio-search-config");

const defaultComponents = initDefaultSearchComponents(domContainer);

const overridenComponents = {
...defaultComponents,
"InvenioAdministration.SearchResultItem.layout": SearchResultItemLayout,
"SearchApp.layout": JobSearchLayout,
};

createSearchAppInit(
overridenComponents,
true,
"invenio-search-config",
false,
NotificationController
);

const pidValue = domContainer.dataset.pidValue;

const detailsConfig = document.getElementById("invenio-details-config");

const title = detailsConfig.dataset.title;
const fields = JSON.parse(detailsConfig.dataset.fields);
const resourceName = JSON.parse(detailsConfig.dataset.resourceName);
const displayEdit = JSON.parse(detailsConfig.dataset.displayEdit);
const displayDelete = JSON.parse(detailsConfig.dataset.displayDelete);
const actions = JSON.parse(detailsConfig.dataset.actions);
const apiEndpoint = _get(detailsConfig.dataset, "apiEndpoint");
const idKeyPath = JSON.parse(_get(detailsConfig.dataset, "pidPath", "pid"));
const listUIEndpoint = detailsConfig.dataset.listEndpoint;
const resourceSchema = JSON.parse(detailsConfig.dataset?.resourceSchema);
const requestHeaders = JSON.parse(detailsConfig.dataset?.requestHeaders);
const uiSchema = JSON.parse(detailsConfig.dataset?.uiConfig);
const name = detailsConfig.dataset?.name;

const cmps = {
...overriddenComponents,
"InvenioAdministration.AdminDetailsView.job-details.layout": JobRunsHeader,
"InvenioAdministration.ActionForm.runs.layout": RunActionForm,
};
detailsConfig &&
ReactDOM.render(
<OverridableContext.Provider value={cmps}>
<AdminDetailsView
title={title}
actions={actions}
apiEndpoint={apiEndpoint}
columns={fields}
pid={pidValue}
displayEdit={displayEdit}
displayDelete={displayDelete}
idKeyPath={idKeyPath}
resourceName={resourceName}
listUIEndpoint={listUIEndpoint}
resourceSchema={resourceSchema}
requestHeaders={requestHeaders}
uiSchema={uiSchema}
name={name}
/>
</OverridableContext.Provider>,
detailsConfig
);

This file was deleted.

Loading

0 comments on commit 03de8c4

Please sign in to comment.