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

Sorting in CPT Dashboard #138

Open
wants to merge 7 commits into
base: revamp
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
23 changes: 23 additions & 0 deletions backend/app/api/v1/commons/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Define the keywords for sorting.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this is hard-coded and not retrieved from an API?
What does this apply to?

I think it would be beneficial to mention some of this in the comment.

DIRECTIONS = ("asc", "desc")
FIELDS = (
"ciSystem.keyword",
"benchmark.keyword",
"ocpVersion.keyword",
"releaseStream.keyword",
"platform.keyword",
"networkType.keyword",
"ipsec.keyword",
"fips.keyword",
"encrypted.keyword",
"publish.keyword",
"computeArch.keyword",
"controlPlaneArch.keyword",
"jobStatus.keyword",
"startDate",
"endDate",
"workerNodesCount",
"masterNodesCount",
"infraNodesCount",
"totalNodesCount",
)
5 changes: 3 additions & 2 deletions backend/app/api/v1/commons/hce.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ async def getData(
start_datetime: date, end_datetime: date, size: int, offset: int, configpath: str
):
query = {
"query": {"bool": {"filter": {"range": {"date": {"format": "yyyy-MM-dd"}}}}}
"size": size,
"from": offset,
"query": {"bool": {"filter": {"range": {"date": {"format": "yyyy-MM-dd"}}}}},
}
es = ElasticService(configpath=configpath)
response = await es.post(
Expand All @@ -25,5 +27,4 @@ async def getData(

jobs[["group"]] = jobs[["group"]].fillna(0)
jobs.fillna("", inplace=True)

return {"data": jobs, "total": response["total"]}
9 changes: 8 additions & 1 deletion backend/app/api/v1/commons/ocp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@


async def getData(
start_datetime: date, end_datetime: date, size: int, offset: int, configpath: str
start_datetime: date,
end_datetime: date,
size: int,
offset: int,
sort: str,
configpath: str,
):
query = {
"size": size,
Expand All @@ -14,6 +19,8 @@ async def getData(
"bool": {"filter": {"range": {"timestamp": {"format": "yyyy-MM-dd"}}}}
},
}
if sort:
query["sort"] = utils.build_sort_terms(sort)

es = ElasticService(configpath=configpath)
response = await es.post(
Expand Down
5 changes: 4 additions & 1 deletion backend/app/api/v1/commons/quay.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


async def getData(
start_datetime: date, end_datetime: date, size, offset, configpath: str
start_datetime: date, end_datetime: date, size, offset, sort: str, configpath: str
):
query = {
"size": size,
Expand All @@ -15,6 +15,9 @@ async def getData(
},
}

if sort:
query["sort"] = utils.build_sort_terms(sort)

es = ElasticService(configpath=configpath)
response = await es.post(
query=query,
Expand Down
31 changes: 31 additions & 0 deletions backend/app/api/v1/commons/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from app.services.search import ElasticService
from fastapi import HTTPException, status
import re
import app.api.v1.commons.constants as constants


async def getMetadata(uuid: str, configpath: str):
Expand Down Expand Up @@ -65,3 +68,31 @@ def getReleaseStream(row):
elif row["releaseStream"].__contains__("ec"):
return "Engineering Candidate"
return "Stable"


def build_sort_terms(sort_string: str) -> list[dict[str, str]]:
"""

Validates and transforms a sort string in the format 'sort=key:direction' to
a list of dictionaries [{key: {"order": direction}}].

:param sort_string: str, input string in the format 'sort=key:direction'

:return: list, transformed sort structure or raises a ValueError for invalid input

"""
sort_terms = []
if sort_string:
key, dir = sort_string.split(":", maxsplit=1)
if dir not in constants.DIRECTIONS:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
f"Sort direction {dir!r} must be one of {','.join(constants.DIRECTIONS)}",
)
if key not in constants.FIELDS:
raise HTTPException(
status.HTTP_400_BAD_REQUEST,
f"Sort key {key!r} must be one of {','.join(constants.FIELDS)}",
)
sort_terms.append({f"{key}": {"order": dir}})
return sort_terms
3 changes: 1 addition & 2 deletions backend/app/api/v1/endpoints/cpt/maps/hce.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import date
import pandas as pd


################################################################
# This will return a Dictionary from HCE required by the CPT
# endpoint, it contians totalJobs and a Dataframe with the following columns:
Expand All @@ -16,8 +17,6 @@
# "version"
# "testName"
################################################################


async def hceMapper(start_datetime: date, end_datetime: date, size: int, offset: int):
response = await getData(
start_datetime, end_datetime, size, offset, f"hce.elasticsearch"
Expand Down
5 changes: 4 additions & 1 deletion backend/app/api/v1/endpoints/ocp/ocpJobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async def jobs(
pretty: bool = Query(False, description="Output content in pretty format."),
size: int = Query(None, description="Number of jobs to fetch"),
offset: int = Query(None, description="Offset Number to fetch jobs from"),
sort: str = Query(None, description="To sort fields on specified direction"),
dbutenhof marked this conversation as resolved.
Show resolved Hide resolved
):
if start_date is None:
start_date = datetime.utcnow().date()
Expand All @@ -59,7 +60,9 @@ async def jobs(
elif not offset:
offset = 0

results = await getData(start_date, end_date, size, offset, "ocp.elasticsearch")
results = await getData(
start_date, end_date, size, offset, sort, "ocp.elasticsearch"
)
jobs = []
if "data" in results and len(results["data"]) >= 1:
dbutenhof marked this conversation as resolved.
Show resolved Hide resolved
jobs = results["data"].to_dict("records")
Expand Down
5 changes: 4 additions & 1 deletion backend/app/api/v1/endpoints/quay/quayJobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ async def jobs(
pretty: bool = Query(False, description="Output content in pretty format."),
size: int = Query(None, description="Number of jobs to fetch"),
offset: int = Query(None, description="Offset Number to fetch jobs from"),
sort: str = Query(None, description="To sort fields on specified direction"),
):
if start_date is None:
start_date = datetime.utcnow().date()
Expand All @@ -59,7 +60,9 @@ async def jobs(
elif not offset:
offset = 0

results = await getData(start_date, end_date, size, offset, "quay.elasticsearch")
results = await getData(
start_date, end_date, size, offset, sort, "quay.elasticsearch"
)

jobs = []
if "data" in results and len(results["data"]) >= 1:
Expand Down
2 changes: 1 addition & 1 deletion backend/app/services/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ async def post(
self,
query,
indice=None,
size=10000,
size=None,
start_date=None,
end_date=None,
timestamp_field=None,
Expand Down
66 changes: 2 additions & 64 deletions frontend/src/actions/commonActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,69 +6,6 @@ import { setOCPCatFilters } from "./ocpActions";
import { setQuayCatFilters } from "./quayActions";
import { setTelcoCatFilters } from "./telcoActions";

const getSortableRowValues = (result, tableColumns) => {
const tableKeys = tableColumns.map((item) => item.value);
return tableKeys.map((key) => result[key]);
};

export const sortTable = (currState) => (dispatch, getState) => {
const results = [...getState()[currState].filteredResults];
const { activeSortDir, activeSortIndex, tableColumns } =
getState()[currState];
try {
if (activeSortIndex !== null && typeof activeSortIndex !== "undefined") {
const sortedResults = results.sort((a, b) => {
const aValue = getSortableRowValues(a, tableColumns)[activeSortIndex];
const bValue = getSortableRowValues(b, tableColumns)[activeSortIndex];
if (typeof aValue === "number") {
if (activeSortDir === "asc") {
return aValue - bValue;
}
return bValue - aValue;
} else {
if (activeSortDir === "asc") {
return aValue.localeCompare(bValue);
}
return bValue.localeCompare(aValue);
}
});
dispatch(sortedTableRows(currState, sortedResults));
}
} catch (error) {
console.log(error);
}
};

const sortedTableRows = (currState, sortedResults) => (dispatch) => {
if (currState === "cpt") {
dispatch({
type: TYPES.SET_FILTERED_DATA,
payload: sortedResults,
});
return;
}
if (currState === "ocp") {
dispatch({
type: TYPES.SET_OCP_FILTERED_DATA,
payload: sortedResults,
});
return;
}
if (currState === "quay") {
dispatch({
type: TYPES.SET_QUAY_FILTERED_DATA,
payload: sortedResults,
});
return;
}
if (currState === "telco") {
dispatch({
type: TYPES.SET_TELCO_FILTERED_DATA,
payload: sortedResults,
});
}
};

const findItemCount = (data, key, value) => {
return data.reduce(function (n, item) {
return n + (item[key].toLowerCase() === value);
Expand Down Expand Up @@ -203,13 +140,14 @@ export const getSelectedFilter =
};

export const getRequestParams = (type) => (dispatch, getState) => {
const { start_date, end_date, size, offset } = getState()[type];
const { start_date, end_date, size, offset, sort } = getState()[type];
const params = {
pretty: true,
...(start_date && { start_date }),
...(end_date && { end_date }),
size: size,
offset: offset,
...(sort && { sort }),
};

return params;
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/actions/homeActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
getFilteredData,
getRequestParams,
getSelectedFilter,
sortTable,
} from "./commonActions";

import API from "@/utils/axiosInstance";
Expand Down Expand Up @@ -56,7 +55,6 @@ export const fetchOCPJobsData =
});

dispatch(applyFilters());
dispatch(sortTable("cpt"));
dispatch(tableReCalcValues());
}
} catch (error) {
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/actions/ocpActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
getFilteredData,
getRequestParams,
getSelectedFilter,
sortTable,
} from "./commonActions";

import API from "@/utils/axiosInstance";
Expand Down Expand Up @@ -52,7 +51,6 @@ export const fetchOCPJobs = () => async (dispatch) => {
});

dispatch(applyFilters());
dispatch(sortTable("ocp"));
dispatch(tableReCalcValues());
}
} catch (error) {
Expand Down
71 changes: 65 additions & 6 deletions frontend/src/actions/sortingActions.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import * as TYPES from "@/actions/types.js";

import { fetchOCPJobs, setOCPSortDir, setOCPSortIndex } from "./ocpActions";
import {
fetchQuayJobsData,
setQuaySortDir,
setQuaySortIndex,
} from "./quayActions";
import {
fetchTelcoJobsData,
setTelcoSortDir,
setTelcoSortIndex,
} from "./telcoActions";
import { setCPTSortDir, setCPTSortIndex } from "./homeActions";
import { setOCPSortDir, setOCPSortIndex } from "./ocpActions";
import { setQuaySortDir, setQuaySortIndex } from "./quayActions";
import { setTelcoSortDir, setTelcoSortIndex } from "./telcoActions";

import { sortTable } from "./commonActions";
import store from "@/store/store";

const { dispatch } = store;
Expand All @@ -29,6 +38,56 @@ export const setActiveSortIndex = (index, currType) => {
dispatch(setTelcoSortIndex(index));
}
};
export const handleOnSort = (currType) => {
dispatch(sortTable(currType));
export const handleOnSort = (colName, currType) => {
dispatch(sortTable(colName, currType));
};

const offsetActions = {
cpt: TYPES.SET_CPT_OFFSET,
ocp: TYPES.SET_OCP_OFFSET,
quay: TYPES.SET_QUAY_OFFSET,
telco: TYPES.SET_TELCO_OFFSET,
};
const fetchJobsMap = {
ocp: fetchOCPJobs,
quay: fetchQuayJobsData,
telco: fetchTelcoJobsData,
};
const sortObjActions = {
ocp: TYPES.SET_OCP_SORT_OBJ,
quay: TYPES.SET_QUAY_SORT_OBJ,
};
export const sortTable = (colName, currState) => (dispatch, getState) => {
const { activeSortDir, activeSortIndex } = getState()[currState];
const countObj = [
"masterNodesCount",
"workerNodesCount",
"infraNodesCount",
"totalNodesCount",
"startDate",
"endDate",
];
try {
if (
typeof activeSortDir !== "undefined" &&
typeof activeSortIndex !== "undefined"
) {
dispatch({ type: offsetActions[currState], payload: 0 });
let fieldName = countObj.includes(colName)
? colName
: `${colName}.keyword`;
if (colName === "build") {
fieldName = "ocpVersion.keyword";
}

const sortParam = `${fieldName}:${activeSortDir}`;
dispatch({ type: sortObjActions[currState], payload: sortParam });
console.log(sortParam);
const isFromSorting = true;

dispatch(fetchJobsMap[currState](isFromSorting));
}
} catch (error) {
console.log(error);
}
};
Loading