update jobs page using tabulator

This commit is contained in:
Jordan Blasenhauer 2024-08-16 17:01:30 +02:00
parent 2b8257b8c1
commit 4003f8f042
10 changed files with 335 additions and 252 deletions

View file

@ -1,19 +1,136 @@
from .utils.widgets import title_widget, table_widget
from .utils.widgets import (
button_widget,
button_group_widget,
title_widget,
subtitle_widget,
text_widget,
tabulator_widget,
input_widget,
icons_widget,
regular_widget,
select_widget,
unmatch_widget,
)
from .utils.table import add_column
from .utils.format import get_fields_from_field
from typing import Optional
columns = [
add_column(title="Name", field="name", formatter="text"),
add_column(title="Plugin id", field="plugin_id", formatter="text"),
add_column(title="Interval", field="every", formatter="text"),
add_column(title="reload", field="reload", formatter="icons"),
add_column(title="success", field="success", formatter="icons"),
add_column(title="history", field="history", formatter="buttongroup"),
add_column(title="Cache", field="cache", formatter="buttongroup"),
]
def jobs_filter(intervals: Optional[list] = None) -> list: # healths = "up", "down", "loading"
filters = [
{
"type": "=",
"fields": ["every"],
"setting": {
"id": "select-every",
"name": "select-every",
"label": "jobs_interval", # keep it (a18n)
"value": "all", # keep "all"
"values": intervals,
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"popovers": [
{
"iconName": "info",
"text": "jobs_interval_desc",
}
],
"fieldSize": "sm",
},
},
{
"type": "=",
"fields": ["reload"],
"setting": {
"id": "select-reload",
"name": "select-reload",
"label": "jobs_reload", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all", "success", "failed"],
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"popovers": [
{
"iconName": "info",
"text": "jobs_reload_desc",
}
],
"fieldSize": "sm",
},
},
{
"type": "=",
"fields": ["success"],
"setting": {
"id": "select-success",
"name": "select-success",
"label": "jobs_success", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all", "success", "failed"],
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"popovers": [
{
"iconName": "info",
"text": "jobs_success_desc",
}
],
"fieldSize": "sm",
},
},
]
if intervals is not None and (isinstance(intervals, list) and len(intervals) > 0):
filters = [
{
"type": "like",
"fields": ["name", "plugin_id"],
"setting": {
"id": "input-search",
"name": "input-search",
"label": "jobs_search", # keep it (a18n)
"placeholder": "inp_keyword", # keep it (a18n)
"value": "",
"inpType": "input",
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"popovers": [
{
"iconName": "info",
"text": "jobs_search_desc",
}
],
"fieldSize": "sm",
},
},
] + filters
return filters
def jobs_builder(jobs):
jobs_list = get_jobs_list(jobs)
intervals = ["all"]
# loop on each job
for job in jobs_list:
# loop on each item
for item in job:
# get the interval if not already in intervals
if item.get("every") and item.get("every") not in intervals:
intervals.append(item.get("every"))
for name, value in jobs.items():
if value.get("every") and value.get("every") not in intervals:
intervals.append(value.get("every"))
jobs_list = get_jobs_list(jobs)
builder = [
{
@ -21,115 +138,12 @@ def jobs_builder(jobs):
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
"widgets": [
title_widget("jobs_title"),
table_widget(
positions=[3, 2, 1, 1, 1, 1, 3],
header=[
"jobs_table_name",
"jobs_table_plugin_id",
"jobs_table_interval",
"jobs_table_reload",
"jobs_table_success",
"jobs_table_history",
"jobs_table_cache_downloadable",
],
tabulator_widget(
id="table-list-jobs",
layout="fitColumns",
columns=columns,
items=jobs_list,
filters=[
{
"filter": "table",
"filterName": "keyword",
"type": "keyword",
"value": "",
"keys": ["name", "plugin_id"],
"field": {
"id": "jobs-keyword",
"value": "",
"type": "text",
"name": "jobs-keyword",
"label": "jobs_search",
"placeholder": "inp_keyword",
"isClipboard": False,
"popovers": [
{
"text": "jobs_search_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
},
},
{
"filter": "table",
"filterName": "every",
"type": "select",
"value": "all",
"keys": ["every"],
"field": {
"id": "jobs-every",
"value": "all",
"values": intervals,
"name": "jobs-every",
"onlyDown": True,
"label": "jobs_interval",
"popovers": [
{
"text": "jobs_interval_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
},
},
{
"filter": "table",
"filterName": "reload",
"type": "select",
"value": "all",
"keys": ["reload"],
"field": {
"id": "jobs-last-run",
"value": "all",
"values": ["all", "success", "failed"],
"name": "jobs-last-run",
"onlyDown": True,
"label": "jobs_reload",
"popovers": [
{
"text": "jobs_reload_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
},
},
{
"filter": "table",
"filterName": "success",
"type": "select",
"value": "all",
"keys": ["success"],
"field": {
"id": "jobs-success",
"value": "all",
"values": ["all", "success", "failed"],
"name": "jobs-success",
"onlyDown": True,
"label": "jobs_success",
"popovers": [
{
"text": "jobs_success_desc",
"iconName": "info",
},
],
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
"fieldSize": "sm",
},
},
],
minWidth="lg",
title="jobs_table_title",
filters=jobs_filter(intervals),
),
],
}
@ -142,153 +156,166 @@ def get_jobs_list(jobs):
data = []
# loop on each dict
for key, value in jobs.items():
item = []
item.append({"name": key, "type": "Text", "data": {"text": key}})
item = {"name": text_widget(text=key)["data"]}
# loop on each value
for k, v in value.items():
# override widget type for some keys
if k in ("reload", "history"):
is_success = v if k == "reload" else v[0].get("success")
item.append(
{
k: "success" if is_success else "failed",
"type": "Icons",
"data": {
"iconName": "check" if is_success else "cross",
},
}
)
item["reload" if k == "reload" else "success"] = icons_widget(
iconName="check" if is_success else "cross", value="success" if is_success else "failed"
)["data"]
if k not in ("history"):
continue
if k in ("plugin_id", "every"):
item.append({k: v, "type": "Text", "data": {"text": v}})
item[k] = text_widget(text=v)["data"]
continue
if k in ("history"):
history_columns = [
add_column(title="Start date", field="start_date", formatter="text"),
add_column(title="End date", field="end_date", formatter="text"),
add_column(title="Success", field="success", formatter="icons"),
]
items = []
for hist in v:
items.append(
[
{
"type": "Text",
"data": {
"text": hist["start_date"],
},
},
{
"type": "Text",
"data": {
"text": hist["end_date"],
},
},
{
"type": "Icons",
"data": {
"iconName": "check" if hist["success"] else "cross",
},
},
]
{
"start_date": text_widget(text=hist["start_date"])["data"],
"end_date": text_widget(text=hist["end_date"])["data"],
"success": icons_widget(iconName="check" if hist["success"] else "cross", value="success" if hist["success"] else "failed")["data"],
}
)
item.append(
{
"type": "Button",
"data": {
"id": f"open-modal-history-{k}",
"text": "jobs_history",
"hideText": True,
"color": "blue",
"size": "normal",
"iconName": "document",
"iconColor": "white",
"modal": {
item["history"] = button_group_widget(
buttons=[
button_widget(
id=f"open-modal-history-{key}",
text="jobs_history",
hideText=True,
color="success",
size="normal",
iconName="eye",
iconColor="white",
modal={
"widgets": [
{"type": "Title", "data": {"title": key}},
{"type": "Subtitle", "data": {"subtitle": "jobs_history_subtitle"}},
{
"type": "Table",
"data": {
"title": "jobs_history_table_title",
"minWidth": "",
"header": [
"jobs_table_start_run",
"jobs_table_end_run",
"jobs_table_success",
],
"positions": [5, 5, 2],
"items": items,
},
},
{
"type": "ButtonGroup",
"data": {
"buttons": [
{
"id": f"close-history-{k}",
"text": "action_close",
"color": "close",
"size": "normal",
"attrs": {"data-close-modal": ""},
}
]
},
},
title_widget(title="jobs_history_title"),
tabulator_widget(
id=f"history-{key}",
columns=history_columns,
items=items,
layout="fitDataFill",
),
button_group_widget(
buttons=[
button_widget(
id=f"close-history-{key}",
text="action_close",
color="close",
size="normal",
attrs={"data-close-modal": ""},
)
]
),
]
},
},
}
)
)
]
)["data"]
if k in ("cache") and len(v) <= 0:
item.append({k: v, "type": "Text", "data": {"text": ""}})
item["cache"] = button_group_widget(
buttons=[
button_widget(
id=f"open-modal-cache-{key}",
text="jobs_no_cache",
hideText=True,
color="info",
size="normal",
iconName="document",
iconColor="white",
containerClass="hidden",
)
]
)["data"]
continue
if k in ("cache") and len(v) > 0:
files = []
# loop on each cache item
for cache in v:
for index, cache in enumerate(v):
file_name = f"{cache['file_name']} [{cache['service_id']}]" if cache["service_id"] else f"{cache['file_name']}"
files.append(file_name)
item.append(
{
k: " ".join(files),
"type": "Fields",
"data": {
"setting": {
"attrs": {
"data-plugin-id": value.get("plugin_id", ""),
"data-job-name": key,
},
"id": f"{key}_cache",
"label": f"{key}_cache",
"hideLabel": True,
"inpType": "select",
"name": f"{key}_cache",
"value": "download file",
"values": files,
"columns": {
"pc": 12,
"tablet": 12,
"mobile": 12,
},
"overflowAttrEl": "data-table-body",
"containerClass": "table download-cache-file",
"maxBtnChars": 16,
"popovers": [
{
"iconName": "info",
"text": "jobs_download_cache_file",
},
files.append(
{
"id": text_widget(text=index)["data"],
"filename": text_widget(text=file_name)["data"],
"download": button_group_widget(
buttonGroupClass="button-group-table-content",
buttons=[
button_widget(
id=f"{key}-cache",
text=f"action_download",
hideText=True,
color="info",
size="normal",
iconName="download",
iconColor="white",
attrs={
"data-plugin-id": value.get("plugin_id", ""),
"data-job-name": key,
},
),
],
}
},
}
)
)["data"],
}
)
item["cache"] = button_group_widget(
buttons=[
button_widget(
id=f"open-modal-cache-{key}",
text="jobs_cache",
hideText=True,
color="info",
size="normal",
iconName="document",
iconColor="white",
modal={
"widgets": [
title_widget(title="jobs_history_title"),
tabulator_widget(
id=f"cache-{key}",
columns=[
add_column(title="id", field="id", formatter="text", maxWidth=80),
add_column(title="File name", field="filename", formatter="text"),
add_column(title="Download", field="download", formatter="buttongroup"),
],
items=files,
layout="fitDataFill",
),
button_group_widget(
buttons=[
button_widget(
id=f"close-history-{key}",
text="action_close",
color="close",
size="normal",
attrs={"data-close-modal": ""},
)
]
),
]
},
)
]
)["data"]
continue
print(item)
data.append(item)
return data

View file

@ -1,8 +1,6 @@
import json
import base64
from builder.jobs import jobs_builder
from utils import save_builder
from pages.jobs import jobs_builder
jobs = {
"anonymous-report": {
@ -216,11 +214,4 @@ jobs = {
output = jobs_builder(jobs)
# store on a file
with open("jobs.json", "w") as f:
json.dump(output, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("jobs.txt", "w") as f:
f.write(output_base64_string)
save_builder(page_name="jobs", output=output, script_name="jobs")

View file

@ -99,7 +99,7 @@ const props = defineProps({
v-if="useEqualStr(widget.type, 'ButtonGroup')"
v-bind="widget.data"
/>
<
<Alert v-if="useEqualStr(widget.type, 'Alert')" v-bind="widget.data" />
<Cell v-if="useEqualStr(widget.type, 'Cell')" v-bind="widget.data" />
<Container

View file

@ -3,6 +3,7 @@
import Grid from "@components/Widget/Grid.vue";
import GridLayout from "@components/Widget/GridLayout.vue";
import Table from "@components/Widget/Table.vue";
import Tabulator from "@components/Widget/Tabulator.vue";
import Title from "@components/Widget/Title.vue";
import { useEqualStr } from "@utils/global.js";
@ -89,6 +90,10 @@ const props = defineProps({
<template v-for="(widget, index) in container.widgets" :key="index">
<Table v-if="useEqualStr(widget.type, 'Table')" v-bind="widget.data" />
<Title v-if="useEqualStr(widget.type, 'Title')" v-bind="widget.data" />
<Tabulator
v-if="useEqualStr(widget.type, 'Tabulator')"
v-bind="widget.data"
/>
</template>
</Grid>
</GridLayout>

View file

@ -0,0 +1,58 @@
<script setup>
import { defineProps, reactive } from "vue";
/**
* @name Icons/Download.vue
* @description This component is a svg icon representing download.
* @example
* {
* color: 'info',
* }
* @param {String} [iconClass="icon-default"] - The class of the icon.
* @param {Any} [value=""] - Attach a value to icon. Useful on some cases like table filtering using icons.
* @param {String} [color="dark"] - The color of the icon between some tailwind css available colors (purple, green, red, orange, blue, yellow, gray, dark, amber, emerald, teal, indigo, cyan, sky, pink...). Darker colors are also available using the base color and adding '-darker' (e.g. 'red-darker').
* @param {Boolean} [disabled=false] - If true, the icon will be disabled.
*/
const props = defineProps({
iconClass: {
type: String,
required: false,
default: "icon-default",
},
value: {
type: [String, Number],
required: false,
default: "",
},
color: {
type: String,
required: false,
default: "sky",
},
disabled: { type: Boolean, required: false, default: false },
});
const icon = reactive({
color: props.color || "sky",
});
</script>
<template>
<svg
:data-color="icon.color"
:data-value="props.value"
:aria-disabled="props.disabled ? 'true' : 'false'"
data-svg="back"
role="img"
aria-hidden="true"
:class="[props.iconClass, icon.color, 'fill']"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-.53 14.03a.75.75 0 0 0 1.06 0l3-3a.75.75 0 1 0-1.06-1.06l-1.72 1.72V8.25a.75.75 0 0 0-1.5 0v5.69l-1.72-1.72a.75.75 0 0 0-1.06 1.06l3 3Z"
clip-rule="evenodd"
/>
</svg>
</template>

View file

@ -40,7 +40,7 @@ import Eye from "@components/Icons/Eye.vue";
import Uncheck from "@components/Icons/Uncheck.vue";
import Back from "@components/Icons/Back.vue";
import Refresh from "@components/Icons/Refresh.vue";
import Download from "@components/Icons/Download.vue";
/**
* @name Widget/Icons.vue
* @description This component is a wrapper that contains all the icons available in the application (Icons folder).
@ -112,6 +112,7 @@ onMounted(() => {
v-if="useEqualStr(props.iconName, 'exclamation')"
v-bind="icon"
/>
<Download v-if="useEqualStr(props.iconName, 'download')" v-bind="icon" />
<Refresh v-if="useEqualStr(props.iconName, 'refresh')" v-bind="icon" />
<Back v-if="useEqualStr(props.iconName, 'back')" v-bind="icon" />
<Box v-if="useEqualStr(props.iconName, 'box')" v-bind="icon" />

View file

@ -291,10 +291,7 @@ onMounted(() => {
a11yTable(table.instance);
// Add table instance to store in order to use it in other components
tableStore.setTable(props.id, table.instance);
setTimeout(() => {
if (!table.instance) return;
table.instance.redraw();
}, 100);
table.instance.redraw();
});
} catch (e) {}

View file

@ -229,6 +229,7 @@
"global_config_subtitle": "Manage your global settings.",
"jobs_title": "Jobs list",
"jobs_download_cache_file": "download cache files",
"jobs_no_cache_file": "no cache files",
"jobs_search": "search jobs",
"jobs_search_desc": "Search within job name, plugin id or last run.",
"jobs_interval": "Interval",
@ -240,6 +241,7 @@
"jobs_table_title": "Jobs list with plugin id, name, last run, interval and success state.",
"jobs_table_name": "Name",
"jobs_table_plugin_id": "Plugin id",
"jobs_history_title": "Job history",
"jobs_table_interval": "Interval",
"jobs_table_reload": "Reload",
"jobs_table_history": "History",

File diff suppressed because one or more lines are too long

View file

@ -101,6 +101,7 @@ function _sortButtonGroup(column, formatName) {
const aValue = aButtons.map((btn) => btn.data.text).join(" ") || "";
const bValue = bButtons.map((btn) => btn.data.text).join(" ") || "";
console.log(aValue, bValue);
return _baseSorter(aValue, bValue, params);
};
}