added some support to add process model metadata. need to fix frontend w/ burnettk

This commit is contained in:
jasquat 2022-12-02 15:46:05 -05:00
parent 20ca5a2978
commit 6b75fc32a3
6 changed files with 130 additions and 29 deletions

View File

@ -38,6 +38,7 @@ class ProcessModelInfo:
fault_or_suspend_on_exception: str = NotificationType.fault.value
exception_notification_addresses: list[str] = field(default_factory=list)
parent_groups: list[dict] | None = None
metadata_extraction_paths: dict[str, str] | None = None
def __post_init__(self) -> None:
"""__post_init__."""
@ -76,6 +77,8 @@ class ProcessModelInfoSchema(Schema):
exception_notification_addresses = marshmallow.fields.List(
marshmallow.fields.String
)
metadata_extraction_paths = marshmallow.fields.Dict(keys=marshmallow.fields.Str(required=False), values=marshmallow.fields.Str(required=False), required=False)
@post_load
def make_spec(

View File

@ -261,19 +261,26 @@ def process_model_create(
modified_process_group_id: str, body: Dict[str, Union[str, bool, int]]
) -> flask.wrappers.Response:
"""Process_model_create."""
process_model_info = ProcessModelInfoSchema().load(body)
body_include_list = [
"id",
"display_name",
"primary_file_name",
"primary_process_id",
"description",
"metadata_extraction_paths",
]
body_filtered = {
include_item: body[include_item]
for include_item in body_include_list
if include_item in body
}
if modified_process_group_id is None:
raise ApiError(
error_code="process_group_id_not_specified",
message="Process Model could not be created when process_group_id path param is unspecified",
status_code=400,
)
if process_model_info is None:
raise ApiError(
error_code="process_model_could_not_be_created",
message=f"Process Model could not be created from given body: {body}",
status_code=400,
)
unmodified_process_group_id = un_modify_modified_process_model_id(
modified_process_group_id
@ -286,6 +293,14 @@ def process_model_create(
status_code=400,
)
process_model_info = ProcessModelInfo(**body_filtered) # type: ignore
if process_model_info is None:
raise ApiError(
error_code="process_model_could_not_be_created",
message=f"Process Model could not be created from given body: {body}",
status_code=400,
)
ProcessModelService.add_process_model(process_model_info)
return Response(
json.dumps(ProcessModelInfoSchema().dump(process_model_info)),
@ -299,7 +314,6 @@ def process_model_delete(
) -> flask.wrappers.Response:
"""Process_model_delete."""
process_model_identifier = modified_process_model_identifier.replace(":", "/")
# process_model_identifier = f"{process_group_id}/{process_model_id}"
ProcessModelService().process_model_delete(process_model_identifier)
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
@ -314,6 +328,7 @@ def process_model_update(
"primary_file_name",
"primary_process_id",
"description",
"metadata_extraction_paths",
]
body_filtered = {
include_item: body[include_item]
@ -321,7 +336,6 @@ def process_model_update(
if include_item in body
}
# process_model_identifier = f"{process_group_id}/{process_model_id}"
process_model = get_process_model(process_model_identifier)
ProcessModelService.update_process_model(process_model, body_filtered)
return ProcessModelInfoSchema().dump(process_model)
@ -330,10 +344,7 @@ def process_model_update(
def process_model_show(modified_process_model_identifier: str) -> Any:
"""Process_model_show."""
process_model_identifier = modified_process_model_identifier.replace(":", "/")
# process_model_identifier = f"{process_group_id}/{process_model_id}"
process_model = get_process_model(process_model_identifier)
# TODO: Temporary. Should not need the next line once models have correct ids
# process_model.id = process_model_identifier
files = sorted(SpecFileService.get_files(process_model))
process_model.files = files
for file in process_model.files:
@ -425,7 +436,6 @@ def process_model_file_update(
) -> flask.wrappers.Response:
"""Process_model_file_update."""
process_model_identifier = modified_process_model_id.replace(":", "/")
# process_model_identifier = f"{process_group_id}/{process_model_id}"
process_model = get_process_model(process_model_identifier)
request_file = get_file_from_request()
@ -1142,7 +1152,7 @@ def process_instance_report_show(
per_page: int = 100,
) -> flask.wrappers.Response:
"""Process_instance_report_show."""
process_instances = ProcessInstanceModel.query.order_by( # .filter_by(process_model_identifier=process_model.id)
process_instances = ProcessInstanceModel.query.order_by(
ProcessInstanceModel.start_in_seconds.desc(), ProcessInstanceModel.id.desc() # type: ignore
).paginate(
page=page, per_page=per_page, error_out=False

View File

@ -333,6 +333,7 @@ class TestProcessApi(BaseTest):
process_model.display_name = "Updated Display Name"
process_model.primary_file_name = "superduper.bpmn"
process_model.primary_process_id = "superduper"
process_model.metadata_extraction_paths = {'extraction1': 'path1'}
modified_process_model_identifier = process_model_identifier.replace("/", ":")
response = client.put(
@ -346,6 +347,7 @@ class TestProcessApi(BaseTest):
assert response.json["display_name"] == "Updated Display Name"
assert response.json["primary_file_name"] == "superduper.bpmn"
assert response.json["primary_process_id"] == "superduper"
assert response.json["metadata_extraction_paths"] == {'extraction1': 'path1'}
def test_process_model_list_all(
self,

View File

@ -142,7 +142,7 @@ export default function ProcessInstanceListTable({
ReportColumn[]
>([]);
const [processInstanceReportJustSaved, setProcessInstanceReportJustSaved] =
useState<boolean>(false);
useState<string | null>(null);
const [showReportColumnForm, setShowReportColumnForm] =
useState<boolean>(false);
const [reportColumnToOperateOn, setReportColumnToOperateOn] =
@ -367,10 +367,14 @@ export default function ProcessInstanceListTable({
const processInstanceReportSaveTag = () => {
if (processInstanceReportJustSaved) {
let titleOperation = 'Updated';
if (processInstanceReportJustSaved === 'new') {
titleOperation = 'Created';
}
return (
<InlineNotification
title="Perspective Saved"
subtitle={`as '${
title={`Perspective ${titleOperation}:`}
subtitle={`'${
processInstanceReportSelection
? processInstanceReportSelection.identifier
: ''
@ -498,7 +502,7 @@ export default function ProcessInstanceListTable({
}
setErrorMessage(null);
setProcessInstanceReportJustSaved(false);
setProcessInstanceReportJustSaved(null);
navigate(`/admin/process-instances?${queryParamString}`);
};
@ -586,10 +590,7 @@ export default function ProcessInstanceListTable({
setEndToTime('');
};
const processInstanceReportDidChange = (
selection: any,
savedReport: boolean = false
) => {
const processInstanceReportDidChange = (selection: any, mode?: string) => {
clearFilters();
const selectedReport = selection.selectedItem;
setProcessInstanceReportSelection(selectedReport);
@ -600,7 +601,7 @@ export default function ProcessInstanceListTable({
}
setErrorMessage(null);
setProcessInstanceReportJustSaved(savedReport);
setProcessInstanceReportJustSaved(mode || null);
navigate(`/admin/process-instances${queryParamString}`);
};
@ -615,12 +616,12 @@ export default function ProcessInstanceListTable({
};
// TODO onSuccess reload/select the new report in the report search
const onSaveReportSuccess = (result: any) => {
const onSaveReportSuccess = (result: any, mode: string) => {
processInstanceReportDidChange(
{
selectedItem: result,
},
true
mode
);
};
@ -638,7 +639,7 @@ export default function ProcessInstanceListTable({
}
return (
<ProcessInstanceListSaveAsReport
onSuccess={onSaveReportSuccess}
onSuccess={(result: any) => onSaveReportSuccess(result, 'new')}
buttonClassName="narrow-button"
columnArray={reportColumns()}
orderBy=""
@ -705,7 +706,7 @@ export default function ProcessInstanceListTable({
} else {
newReportFilters.splice(existingReportFilterIndex, 1);
}
} else {
} else if (reportColumnForEditing.filter_field_value) {
newReportFilters = newReportFilters.concat([newReportFilter]);
}
}
@ -1157,7 +1158,7 @@ export default function ProcessInstanceListTable({
<Column sm={2} md={4} lg={2}>
<ProcessInstanceListSaveAsReport
buttonClassName="with-tiny-top-margin"
onSuccess={onSaveReportSuccess}
onSuccess={(result: any) => onSaveReportSuccess(result, 'edit')}
columnArray={reportColumns()}
orderBy=""
buttonText="Save"

View File

@ -2,9 +2,11 @@ import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
// @ts-ignore
import { Button, ButtonSet, Form, Stack, TextInput } from '@carbon/react';
// @ts-ignore
import { AddAlt } from '@carbon/icons-react';
import { modifyProcessIdentifierForPathParam, slugifyString } from '../helpers';
import HttpService from '../services/HttpService';
import { ProcessModel } from '../interfaces';
import { MetadataExtractionPaths, ProcessModel } from '../interfaces';
type OwnProps = {
mode: string;
@ -23,6 +25,7 @@ export default function ProcessModelForm({
const [idHasBeenUpdatedByUser, setIdHasBeenUpdatedByUser] =
useState<boolean>(false);
const [displayNameInvalid, setDisplayNameInvalid] = useState<boolean>(false);
useState<boolean>(false);
const navigate = useNavigate();
const navigateToProcessModel = (result: ProcessModel) => {
@ -64,6 +67,7 @@ export default function ProcessModelForm({
const postBody = {
display_name: processModel.display_name,
description: processModel.description,
metadata_extraction_paths: processModel.metadata_extraction_paths,
};
if (mode === 'new') {
Object.assign(postBody, {
@ -87,6 +91,66 @@ export default function ProcessModelForm({
setProcessModel(processModelToCopy);
};
const metadataExtractionPathForm = (
metadataKey: string,
metadataPath: string
) => {
return (
<>
<TextInput
id="process-model-metadata-extraction-path-key"
labelText="Extraction Key"
value={metadataKey}
onChange={(event: any) => {
const cep: MetadataExtractionPaths =
processModel.metadata_extraction_paths || {};
delete cep[metadataKey];
cep[event.target.value] = metadataPath;
updateProcessModel({ metadata_extraction_paths: cep });
}}
/>
<TextInput
id="process-model-metadata-extraction-path"
labelText="Extraction Path"
value={metadataPath}
onChange={(event: any) => {
const cep: MetadataExtractionPaths =
processModel.metadata_extraction_paths || {};
cep[metadataKey] = event.target.value;
updateProcessModel({ metadata_extraction_paths: cep });
}}
/>
</>
);
};
const metadataExtractionPathFormArea = () => {
if (processModel.metadata_extraction_paths) {
console.log(
'processModel.metadata_extraction_paths',
processModel.metadata_extraction_paths
);
return Object.keys(processModel.metadata_extraction_paths).map(
(metadataKey: string) => {
return metadataExtractionPathForm(
metadataKey,
processModel.metadata_extraction_paths
? processModel.metadata_extraction_paths[metadataKey]
: ''
);
}
);
}
return null;
};
const addBlankMetadataExtractionPath = () => {
const cep: MetadataExtractionPaths =
processModel.metadata_extraction_paths || {};
Object.assign(cep, { '': '' });
updateProcessModel({ metadata_extraction_paths: cep });
};
const onDisplayNameChanged = (newDisplayName: any) => {
setDisplayNameInvalid(false);
const updateDict = { display_name: newDisplayName };
@ -145,6 +209,22 @@ export default function ProcessModelForm({
/>
);
textInputs.push(<>{metadataExtractionPathFormArea()}</>);
textInputs.push(
<Button
data-qa="add-metadata-extraction-path-button"
renderIcon={AddAlt}
className="button-white-background"
kind=""
size="sm"
onClick={() => {
addBlankMetadataExtractionPath();
}}
>
Add Metadata Extraction Path
</Button>
);
return textInputs;
};

View File

@ -98,6 +98,10 @@ export interface ProcessGroupLite {
display_name: string;
}
export interface MetadataExtractionPaths {
[key: string]: string;
}
export interface ProcessModel {
id: string;
description: string;
@ -105,6 +109,7 @@ export interface ProcessModel {
primary_file_name: string;
files: ProcessFile[];
parent_groups?: ProcessGroupLite[];
metadata_extraction_paths?: MetadataExtractionPaths;
}
export interface ProcessGroup {