* feat: enable re-run OCR (draft)

* add rerun OCR for single file functionality

* added tooltip

* add icon and changed position

* added - rerun OCR for all documents in project

* fix: remove idents

* feat: enable re-run OCR (draft)

* add rerun OCR for single file functionality

* added tooltip

* add icon and changed position

* added - rerun OCR for all documents in project

* refactor: delete redundant local state and rename

* fix: indent

* style: change submenu text

* docs: added manual test runbook

* style: idents

* refactor: rename dropdown class name

* renaming: re-run.. to run, files to documents..

* fix: style and grammar

* fix: run on not visited

* docs: new Test Runbook revision

* style: fix wording and spacing

* refactor: use anonymous wrapper function for onclick

Co-authored-by: kunzheng <58841788+kunzms@users.noreply.github.com>
Co-authored-by: Robert Stewart (eXcell <v-stewro@microsoft.com>
This commit is contained in:
alex-krasn 2020-06-11 11:14:07 -07:00 коммит произвёл GitHub
Родитель 7c57de9656
Коммит cbe9b0ed1c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 403 добавлений и 312 удалений

Просмотреть файл

@ -0,0 +1,24 @@
# Test Runbook
## **Enable rerun OCR for current or all documents**
> ### Feature description ###
Add the following buttons to the canvas command bar:
- "Run OCR on current document"
- "Run OCR on all documents"
> ### Use Case ###
**`As`** a user
**`I want`** to rerun OCR on documents
**`So`** I can update OCR results
> ### Acceptance criteria ###
#### Scenario One ####
**`Given`** I've opened a project containing documents and I'm on the Tag Editor page.
**`When`** I click "Run OCR on current document" in the canvas command bar.
**`Then`** I should see "Running OCR..." for the current docucment. When running OCR finishes, I should be able to view the document's updated OCR JSON file.
#### **Scenario Two** ####
**`Given`** I've opened a project containing documents and I'm on the Tag Editor page.
**`When`** I click "Run OCR on all documents" in the canvas command bar.
**`Then`** I should see "Running OCR..." for all documents. When running OCR finishes for each document, I should be ale to view each document's updated OCR JSON file.

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,190 +1,202 @@
{
"fontName": "fabric-icons",
"fontFamilyName": "FabricMDL2Icons",
"excludeGlyphs": false,
"excludeThirdPartyIcons": false,
"chunkSubsets": false,
"hashFontFileName": true,
"glyphs": [
{
"name": "Insights",
"unicode": "E3AF"
},
{
"name": "MachineLearning",
"unicode": "E3B8"
},
{
"name": "TagGroup",
"unicode": "E3F6"
},
{
"name": "ChevronDown",
"unicode": "E70D"
},
{
"name": "ChevronUp",
"unicode": "E70E"
},
{
"name": "Edit",
"unicode": "E70F"
},
{
"name": "Add",
"unicode": "E710"
},
{
"name": "Cancel",
"unicode": "E711"
},
{
"name": "Settings",
"unicode": "E713"
},
{
"name": "Link",
"unicode": "E71B"
},
{
"name": "Filter",
"unicode": "E71C"
},
{
"name": "ZoomOut",
"unicode": "E71F"
},
{
"name": "Search",
"unicode": "E721"
},
{
"name": "CheckboxComposite",
"unicode": "E73A"
},
{
"name": "CheckMark",
"unicode": "E73E"
},
{
"name": "Up",
"unicode": "E74A"
},
{
"name": "Down",
"unicode": "E74B"
},
{
"name": "Delete",
"unicode": "E74D"
},
{
"name": "Cloud",
"unicode": "E753"
},
{
"name": "ChevronLeft",
"unicode": "E76B"
},
{
"name": "ChevronRight",
"unicode": "E76C"
},
{
"name": "Home",
"unicode": "E80F"
},
{
"name": "MapLayers",
"unicode": "E81E"
},
{
"name": "View",
"unicode": "E890"
},
{
"name": "Download",
"unicode": "E896"
},
{
"name": "Help",
"unicode": "E897"
},
{
"name": "ZoomIn",
"unicode": "E8A3"
},
{
"name": "Rename",
"unicode": "E8AC"
},
{
"name": "Copy",
"unicode": "E8C8"
},
{
"name": "Tag",
"unicode": "E8EC"
},
{
"name": "Label",
"unicode": "E932"
},
{
"name": "Info",
"unicode": "E946"
},
{
"name": "AddTo",
"unicode": "ECC8"
},
{
"name": "OpenFolderHorizontal",
"unicode": "ED25"
},
{
"name": "Table",
"unicode": "ED86"
},
{
"name": "TextField",
"unicode": "EDC3"
},
{
"name": "DocumentManagement",
"unicode": "EFFC"
},
{
"name": "TextDocument",
"unicode": "F029"
},
{
"name": "BranchMerge",
"unicode": "F295"
},
{
"name": "Plug",
"unicode": "F300"
},
{
"name": "PlugConnected",
"unicode": "F302"
},
{
"name": "AlertSolid",
"unicode": "F331"
},
{
"name": "Hide3",
"unicode": "F6AC"
},
{
"name": "WarningSolid",
"unicode": "F736"
},
{
"name": "BookAnswers",
"unicode": "F8A4"
}
]
}
"fontName": "fabric-icons",
"fontFamilyName": "FabricMDL2Icons",
"excludeGlyphs": false,
"excludeThirdPartyIcons": false,
"chunkSubsets": false,
"hashFontFileName": true,
"glyphs": [
{
"name": "Table",
"unicode": "ED86"
},
{
"name": "TextField",
"unicode": "EDC3"
},
{
"name": "TextDocument",
"unicode": "F029"
},
{
"name": "DocumentManagement",
"unicode": "EFFC"
},
{
"name": "OpenFolderHorizontal",
"unicode": "ED25"
},
{
"name": "Info",
"unicode": "E946"
},
{
"name": "Label",
"unicode": "E932"
},
{
"name": "Documentation",
"unicode": "EC17"
},
{
"name": "AddTo",
"unicode": "ECC8"
},
{
"name": "Hide3",
"unicode": "F6AC"
},
{
"name": "WarningSolid",
"unicode": "F736"
},
{
"name": "BranchMerge",
"unicode": "F295"
},
{
"name": "PlugConnected",
"unicode": "F302"
},
{
"name": "Plug",
"unicode": "F300"
},
{
"name": "AlertSolid",
"unicode": "F331"
},
{
"name": "Refresh",
"unicode": "E72C"
},
{
"name": "CheckboxComposite",
"unicode": "E73A"
},
{
"name": "Cancel",
"unicode": "E711"
},
{
"name": "More",
"unicode": "E712"
},
{
"name": "Settings",
"unicode": "E713"
},
{
"name": "Link",
"unicode": "E71B"
},
{
"name": "Filter",
"unicode": "E71C"
},
{
"name": "ZoomOut",
"unicode": "E71F"
},
{
"name": "Search",
"unicode": "E721"
},
{
"name": "Add",
"unicode": "E710"
},
{
"name": "CheckMark",
"unicode": "E73E"
},
{
"name": "ChevronLeft",
"unicode": "E76B"
},
{
"name": "ChevronRight",
"unicode": "E76C"
},
{
"name": "Up",
"unicode": "E74A"
},
{
"name": "Down",
"unicode": "E74B"
},
{
"name": "Delete",
"unicode": "E74D"
},
{
"name": "Cloud",
"unicode": "E753"
},
{
"name": "Edit",
"unicode": "E70F"
},
{
"name": "ChevronUp",
"unicode": "E70E"
},
{
"name": "ChevronDown",
"unicode": "E70D"
},
{
"name": "Copy",
"unicode": "E8C8"
},
{
"name": "ZoomIn",
"unicode": "E8A3"
},
{
"name": "Rename",
"unicode": "E8AC"
},
{
"name": "Tag",
"unicode": "E8EC"
},
{
"name": "Download",
"unicode": "E896"
},
{
"name": "View",
"unicode": "E890"
},
{
"name": "Help",
"unicode": "E897"
},
{
"name": "Home",
"unicode": "E80F"
},
{
"name": "MapLayers",
"unicode": "E81E"
},
{
"name": "Insights",
"unicode": "E3AF"
},
{
"name": "MachineLearning",
"unicode": "E3B8"
},
{
"name": "TagGroup",
"unicode": "E3F6"
},
{
"name": "BookAnswers",
"unicode": "F8A4"
}
]
}

Просмотреть файл

@ -86,7 +86,7 @@ export class AssetPreview extends React.Component<IAssetPreviewProps, IAssetPrev
public render() {
const { loaded, hasError } = this.state;
const size = this.props.asset.size;
const { size } = this.props.asset;
const classNames = ["asset-preview"];
if (size) {
if (size.width > size.height) {

Просмотреть файл

@ -6,7 +6,7 @@
height: 100%;
padding-bottom: 0px;
}
.map {
background-color: Gainsboro;
width: 100%;
@ -57,7 +57,7 @@
color: white;
text-shadow:
1px 1px 0 #000,
-1px -1px 0 #000,
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
@ -86,3 +86,8 @@
width: 100%;
height: 100%;
}
.additional-action-dropdown {
margin-left: 0.5rem;
margin-right: -1rem;
}

Просмотреть файл

@ -52,6 +52,7 @@ export interface ICanvasProps extends React.Props<Canvas> {
onCanvasRendered?: (canvas: HTMLCanvasElement) => void;
onRunningOCRStatusChanged?: (isRunning: boolean) => void;
onTagChanged?: (oldTag: ITag, newTag: ITag) => void;
runOcrForAllDocs?: (runForAllDocs:boolean) => void;
}
export interface ICanvasState {
@ -212,6 +213,8 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
handleZoomIn={this.handleCanvasZoomIn}
handleZoomOut={this.handleCanvasZoomOut}
layers={this.state.layers}
handleRunOcr={this.runOcr}
handleRunOcrForAllDocuments={this.runOcrForAllDocuments}
/>
<ImageMap
ref={(ref) => this.imageMap = ref}
@ -285,6 +288,11 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
);
}
private runOcrForAllDocuments = () => {
this.setState({ocrStatus: OcrStatus.runningOCR})
this.props.runOcrForAllDocs(true);
}
public updateSize() {
this.imageMap.updateSize();
}
@ -938,14 +946,19 @@ export default class Canvas extends React.Component<ICanvasProps, ICanvasState>
});
}
private loadOcr = async () => {
private runOcr = () => {
this.loadOcr(true);
}
private loadOcr = async (force?: boolean) => {
const asset = this.state.currentAsset.asset;
if (asset.isRunningOCR) {
// Skip loading OCR this time since it's running. This will be triggered again once it's finished.
return;
}
try {
const ocr = await this.ocrService.getRecognizedText(asset.path, asset.name, this.setOCRStatus);
const ocr = await this.ocrService.getRecognizedText(asset.path, asset.name, this.setOCRStatus, force);
if (asset.id === this.state.currentAsset.asset.id) {
// since get OCR is async, we only set currentAsset's OCR
this.setState({

Просмотреть файл

@ -6,6 +6,8 @@ import { getDarkGreyTheme } from "../../../../common/themes";
interface ICanvasCommandBarProps {
handleZoomIn: () => void;
handleZoomOut: () => void;
handleRunOcr: () => void;
handleRunOcrForAllDocuments: () => void;
handleLayerChange: (layer: string) => void;
layers: any;
}
@ -81,6 +83,29 @@ export const CanvasCommandBar: React.FunctionComponent<ICanvasCommandBarProps> =
iconProps: { iconName: "ZoomIn" },
onClick: () => props.handleZoomIn(),
},
{
key: "additionalActions",
title: "Additional actions",
ariaLabel: "Additional actions",
className: "additional-action-dropdown",
iconProps: { iconName: "More" },
subMenuProps: {
items: [
{
key: "runOcrForCurrentDocument",
text: "Run OCR on current document",
iconProps: { iconName: "TextDocument" },
onClick: () => props.handleRunOcr(),
},
{
key: "runOcrForAllDocuments",
text: "Run OCR for all documents",
iconProps: { iconName: "Documentation" },
onClick: () => props.handleRunOcrForAllDocuments(),
},
],
},
},
];
return (

Просмотреть файл

@ -37,7 +37,6 @@ import { constants } from "../../../../common/constants";
import PreventLeaving from "../../common/preventLeaving/preventLeaving";
import { Spinner, SpinnerSize } from "@fluentui/react/lib/Spinner";
import { getPrimaryGreenTheme, getPrimaryRedTheme } from "../../../../common/themes";
import { SkipButton } from "../../shell/skipButton";
/**
* Properties for Editor Page
@ -221,7 +220,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
theme={getPrimaryGreenTheme()}
className="editor-page-sidebar-run-ocr"
type="button"
onClick={this.loadAllOCRs}
onClick={() => this.loadOcrForNotVisited()}
disabled={this.state.isRunningOCRs}>
{this.state.isRunningOCRs ?
<div>
@ -231,7 +230,7 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
ariaLive="off"
labelPosition="right"
/>
</div> : "Run OCR on all files"
</div> : "Run OCR on unvisited documents"
}
</PrimaryButton>
</div>}
@ -269,7 +268,8 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
lockedTags={this.state.lockedTags}
hoveredLabel={this.state.hoveredLabel}
setTableToView={this.setTableToView}
closeTableView={this.closeTableView}>
closeTableView={this.closeTableView}
runOcrForAllDocs={this.loadOcrForNotVisited}>
<AssetPreview
controlsEnabled={this.state.isValid}
onBeforeAssetChanged={this.onBeforeAssetSelected}
@ -629,11 +629,10 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
});
}
private loadAllOCRs = async () => {
public loadOcrForNotVisited = async (runForAll?: boolean) => {
if (this.state.isRunningOCRs) {
return;
}
const { project } = this.props;
const ocrService = new OCRService(project);
if (this.state.assets) {
@ -641,14 +640,16 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
try {
await throttle(
constants.maxConcurrentServiceRequests,
this.state.assets.filter((asset) => asset.state === AssetState.NotVisited).map((asset) => asset.id),
this.state.assets
.filter((asset) => runForAll ? asset : asset.state === AssetState.NotVisited)
.map((asset) => asset.id),
async (assetId) => {
// Get the latest version of asset.
const asset = this.state.assets.find((asset) => asset.id === assetId);
if (asset && asset.state === AssetState.NotVisited) {
if (asset && (asset.state === AssetState.NotVisited || runForAll)) {
try {
this.updateAssetState(asset.id, true);
await ocrService.getRecognizedText(asset.path, asset.name);
await ocrService.getRecognizedText(asset.path, asset.name, undefined, runForAll);
this.updateAssetState(asset.id, false, AssetState.Visited);
} catch (err) {
this.updateAssetState(asset.id, false);
@ -659,7 +660,8 @@ export default class EditorPage extends React.Component<IEditorPageProps, IEdito
});
}
}
});
}
);
} finally {
this.setState({ isRunningOCRs: false });
}

Просмотреть файл

@ -54,7 +54,9 @@ export function registerIcons() {
MapLayers: "\uE81E",
BookAnswers: "\uF8A4",
Cancel: "\uE711",
Refresh: "\uE72C",
Documentation: "\uEC17",
More: "\uE712",
},
});
}

Просмотреть файл

@ -32,7 +32,9 @@ export class OCRService {
public async getRecognizedText(
filePath: string,
fileName: string,
onStatusChanged?: (ocrStatus: OcrStatus) => void): Promise<any> {
onStatusChanged?: (ocrStatus: OcrStatus) => void,
rewrite?: boolean
): Promise<any> {
Guard.empty(filePath);
Guard.empty(this.project.apiUriBase);
@ -43,7 +45,7 @@ export class OCRService {
try {
notifyStatusChanged(OcrStatus.loadingFromAzureBlob);
ocrJson = await this.readOcrFile(ocrFileName);
if (!this.isValidOcrFormat(ocrJson)) {
if (!this.isValidOcrFormat(ocrJson) || rewrite) {
ocrJson = await this.fetchOcrUriResult(filePath, ocrFileName);
}
} catch (e) {
@ -52,7 +54,6 @@ export class OCRService {
} finally {
notifyStatusChanged(OcrStatus.done);
}
return ocrJson;
}