From 81db476e3a9460e1f4d82eb4dbc483c0eab41ab9 Mon Sep 17 00:00:00 2001 From: Alex Vasylenko Date: Wed, 9 Nov 2022 17:14:40 +0200 Subject: [PATCH] Feat: add enable and list filters for Scheds --- querybook/server/logic/schedule.py | 9 ++ .../EnvironmentSelection.tsx | 7 +- .../DataDocScheduleList.tsx | 150 ++++++++++++++++-- .../DataDocScheduleSelectionList.tsx | 14 ++ querybook/webapp/const/options.tsx | 5 + querybook/webapp/redux/dataDoc/selector.ts | 9 ++ .../webapp/redux/scheduledDataDoc/action.ts | 8 +- .../webapp/redux/scheduledDataDoc/types.ts | 18 ++- querybook/webapp/resource/dataDoc.ts | 4 +- querybook/webapp/ui/Form/FormField.tsx | 2 +- 10 files changed, 199 insertions(+), 27 deletions(-) create mode 100644 querybook/webapp/components/DataDocScheduleList/DataDocScheduleSelectionList.tsx create mode 100644 querybook/webapp/const/options.tsx diff --git a/querybook/server/logic/schedule.py b/querybook/server/logic/schedule.py index 4e78d4925..eb3dec2b4 100644 --- a/querybook/server/logic/schedule.py +++ b/querybook/server/logic/schedule.py @@ -11,6 +11,7 @@ TaskRunRecord, ) from models.datadoc import DataDoc +from models.board import BoardItem DATADOC_SCHEDULE_PREFIX = "run_data_doc_" @@ -194,6 +195,14 @@ def get_scheduled_data_docs_by_user( if "name" in filters: query = query.filter(DataDoc.title.contains(filters.get("name"))) + if "status" in filters: + query = query.filter(TaskSchedule.enabled == filters.get("status")) + + if filters.get("list_ids"): + query = query.join(BoardItem, BoardItem.data_doc_id == DataDoc.id).filter( + BoardItem.parent_board_id.in_(filters.get("list_ids")) + ) + count = query.count() docs_with_schedules = query.offset(offset).limit(limit).all() docs_with_schedules_and_records = get_task_run_record_run_with_schedule( diff --git a/querybook/webapp/components/AppAdmin/components/EnvironmentSelection/EnvironmentSelection.tsx b/querybook/webapp/components/AppAdmin/components/EnvironmentSelection/EnvironmentSelection.tsx index 2bc796612..c21667ffe 100644 --- a/querybook/webapp/components/AppAdmin/components/EnvironmentSelection/EnvironmentSelection.tsx +++ b/querybook/webapp/components/AppAdmin/components/EnvironmentSelection/EnvironmentSelection.tsx @@ -2,12 +2,7 @@ import { useField, useFormikContext } from 'formik'; import React, { useMemo } from 'react'; import { SimpleField } from 'ui/FormikField/SimpleField'; - -interface OptionsType { - value: string; - key: string; - hidden?: boolean; -} +import { OptionsType } from 'const/options'; export const EnvironmentSelection = ({ options = [], diff --git a/querybook/webapp/components/DataDocScheduleList/DataDocScheduleList.tsx b/querybook/webapp/components/DataDocScheduleList/DataDocScheduleList.tsx index ee4129215..093ae0ce8 100644 --- a/querybook/webapp/components/DataDocScheduleList/DataDocScheduleList.tsx +++ b/querybook/webapp/components/DataDocScheduleList/DataDocScheduleList.tsx @@ -1,20 +1,41 @@ -import React, { useEffect, useMemo, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; - +import React, { + useEffect, + useMemo, + useState, + useRef, + useCallback, +} from 'react'; +import { debounce } from 'lodash'; +import { useSelector, useDispatch } from 'react-redux'; +import { Dispatch, IStoreState } from 'redux/store/types'; import { getScheduledDocs } from 'redux/scheduledDataDoc/action'; + import { IScheduledDocFilters } from 'redux/scheduledDataDoc/types'; -import { Dispatch, IStoreState } from 'redux/store/types'; import { Checkbox } from 'ui/Checkbox/Checkbox'; import { Container } from 'ui/Container/Container'; import { DebouncedInput } from 'ui/DebouncedInput/DebouncedInput'; import { Pagination } from 'ui/Pagination/Pagination'; import { PrettyNumber } from 'ui/PrettyNumber/PrettyNumber'; import { AccentText, EmptyText } from 'ui/StyledText/StyledText'; +import { IconButton } from 'ui/Button/IconButton'; +import { Popover } from 'ui/Popover/Popover'; +import { fetchBoards } from 'redux/board/action'; +import { queryDataDocFiltersSelector } from 'redux/dataDoc/selector'; import { DataDocScheduleItem } from './DataDocScheduleItem'; +import Select, { OptionTypeBase } from 'react-select'; +import { makeReactSelectStyle } from 'lib/utils/react-select'; +import { makeSelectOptions, Select as SimpleSelect } from 'ui/Select/Select'; +import { DataDocScheduleSelectionList } from './DataDocScheduleSelectionList'; import './DataDocScheduleList.scss'; +const enabledOptions = [ + { key: '', value: 'All' }, + { key: true, value: 'Enabled' }, + { key: false, value: 'Disabled' }, +]; + function useDataDocScheduleFiltersAndPagination() { const { page: initPage, @@ -24,20 +45,40 @@ function useDataDocScheduleFiltersAndPagination() { } = useSelector((state: IStoreState) => state.scheduledDocs); const [docName, setDocName] = useState(initFilters.name ?? ''); - const [scheduledOnly, setScheduledOnly] = useState( - initFilters.scheduled_only ?? false - ); + + const [extraFilters, setExtraFilters] = useState({ + status: null, + list_ids: [], + scheduled_only: initFilters.scheduled_only ?? false, + }); + + const updateFilters = useCallback((params) => { + setExtraFilters((state) => ({ + ...state, + ...params, + })); + }, []); const filters: IScheduledDocFilters = useMemo(() => { const _filters: IScheduledDocFilters = {}; if (docName) { _filters.name = docName; } - if (scheduledOnly) { + + if (extraFilters.scheduled_only) { _filters.scheduled_only = true; } + + if (extraFilters.status !== null) { + _filters.status = extraFilters.status; + } + + if (extraFilters.list_ids) { + _filters.list_ids = extraFilters.list_ids; + } + return _filters; - }, [docName, scheduledOnly]); + }, [docName, extraFilters]); const [page, setPage] = useState(initPage); const [pageSize, setPageSize] = useState(initPageSize); @@ -45,13 +86,13 @@ function useDataDocScheduleFiltersAndPagination() { return { filters, setDocName, - setScheduledOnly, numberOfResults, page, setPage, pageSize, setPageSize, + updateFilters, }; } @@ -66,7 +107,10 @@ function useDataDocWithSchedules( getScheduledDocs({ paginationPage: page, paginationPageSize: pageSize, - paginationFilter: filters, + paginationFilter: { + ...filters, + list_ids: filters.list_ids?.map((l) => l.value), + }, }) ); }, [page, pageSize, filters, dispatch]); @@ -92,7 +136,7 @@ const DataDocScheduleList: React.FC = () => { filters, setDocName, - setScheduledOnly, + updateFilters, } = useDataDocScheduleFiltersAndPagination(); const dataDocsWithSchedule = useDataDocWithSchedules( @@ -102,6 +146,74 @@ const DataDocScheduleList: React.FC = () => { ); const totalPages = Math.ceil(numberOfResults / pageSize); + const [showSearchFilter, setShowSearchFilter] = useState(false); + const filterButtonRef = useRef(); + const dispatch = useDispatch(); + const boards = useSelector(queryDataDocFiltersSelector); + useEffect(() => { + dispatch(fetchBoards()); + }, []); + + const handleUpdateList = React.useCallback( + debounce((params: OptionTypeBase[]) => { + updateFilters({ + list_ids: params, + }); + }, 500), + [] + ); + + const handleUpdateStatus = React.useCallback( + ({ target: { value } }: { target: { value: string } }) => { + updateFilters({ + status: value === '' ? null : value === 'true', + }); + }, + [] + ); + + const handleUpdateScheduledOnly = React.useCallback((value) => { + updateFilters({ + scheduled_only: value, + }); + }, []); + + const reactSelectStyle = makeReactSelectStyle(true); + + const searchFiltersPickerDOM = showSearchFilter && ( + { + setShowSearchFilter(false); + }} + anchor={filterButtonRef.current} + > +
+
+ + + {makeSelectOptions(enabledOptions)} + + + +