feat: add download analyze script to predict page (#130)
* feat: add download analyze script to predict page * refactor: change eol to LF * fix: remove disabling download script button
This commit is contained in:
Родитель
d8dc57ca6a
Коммит
ce06b7eabc
|
@ -0,0 +1,131 @@
|
|||
########### Python Form Recognizer Async Analyze #############
|
||||
import json
|
||||
import time
|
||||
import getopt
|
||||
import sys
|
||||
import os
|
||||
from requests import get, post
|
||||
|
||||
def main(argv):
|
||||
input_file, file_type = getArguments(argv)
|
||||
runAnalysis(input_file, file_type)
|
||||
|
||||
def runAnalysis(input_file, file_type):
|
||||
# Endpoint URL
|
||||
endpoint = r"<endpoint>"
|
||||
apim_key = "<subsription_key>"
|
||||
model_id = "<model_id>"
|
||||
post_url = endpoint + "/formrecognizer/v2.0-preview/custom/models/%s/analyze" % model_id
|
||||
params = {
|
||||
"includeTextDetails": True
|
||||
}
|
||||
|
||||
headers = {
|
||||
# Request headers
|
||||
'Content-Type': file_type,
|
||||
'Ocp-Apim-Subscription-Key': apim_key,
|
||||
}
|
||||
try:
|
||||
with open(input_file, "rb") as f:
|
||||
data_bytes = f.read()
|
||||
except IOError:
|
||||
print("Inputfile not accessible.")
|
||||
sys.exit(2)
|
||||
|
||||
try:
|
||||
print('Initiating analysis...')
|
||||
resp = post(url = post_url, data = data_bytes, headers = headers, params = params)
|
||||
if resp.status_code != 202:
|
||||
print("POST analyze failed:\n%s" % json.dumps(resp.json()))
|
||||
quit()
|
||||
print("POST analyze succeeded:\n%s" % resp.headers)
|
||||
print
|
||||
get_url = resp.headers["operation-location"]
|
||||
except Exception as e:
|
||||
print("POST analyze failed:\n%s" % str(e))
|
||||
quit()
|
||||
|
||||
n_tries = 15
|
||||
n_try = 0
|
||||
wait_sec = 5
|
||||
max_wait_sec = 60
|
||||
print('Getting analysis results...')
|
||||
while n_try < n_tries:
|
||||
try:
|
||||
resp = get(url = get_url, headers = {"Ocp-Apim-Subscription-Key": apim_key})
|
||||
resp_json = resp.json()
|
||||
if resp.status_code != 200:
|
||||
print("GET analyze results failed:\n%s" % json.dumps(resp_json))
|
||||
quit()
|
||||
status = resp_json["status"]
|
||||
if status == "succeeded":
|
||||
print("Analysis succeeded:\n%s" % json.dumps(resp_json))
|
||||
quit()
|
||||
if status == "failed":
|
||||
print("Analysis failed:\n%s" % json.dumps(resp_json))
|
||||
quit()
|
||||
# Analysis still running. Wait and retry.
|
||||
time.sleep(wait_sec)
|
||||
n_try += 1
|
||||
wait_sec = min(2*wait_sec, max_wait_sec)
|
||||
except Exception as e:
|
||||
msg = "GET analyze results failed:\n%s" % str(e)
|
||||
print(msg)
|
||||
quit()
|
||||
print("Analyze operation did not complete within the allocated time.")
|
||||
|
||||
def getArguments(argv):
|
||||
input_file = ''
|
||||
file_type = ''
|
||||
try:
|
||||
opts, args = getopt.gnu_getopt(argv, "ht:", [])
|
||||
except getopt.GetoptError:
|
||||
printCommandDescription(2)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
printCommandDescription()
|
||||
|
||||
if len(args) != 1:
|
||||
printCommandDescription()
|
||||
else:
|
||||
input_file = args[0]
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt == '-t':
|
||||
if arg not in ('application/pdf', 'image/jpeg', 'image/png', 'image/tiff'):
|
||||
print('Type ' + file_type + ' not supported')
|
||||
sys.exit()
|
||||
else:
|
||||
file_type = arg
|
||||
|
||||
if not file_type:
|
||||
file_type = inferrType(input_file)
|
||||
|
||||
return (input_file, file_type)
|
||||
|
||||
def inferrType(input_file):
|
||||
filename, file_extension = os.path.splitext(input_file)
|
||||
if file_extension == '':
|
||||
print('File extension could not be inferred from inputfile. Provide type as an argument.')
|
||||
sys.exit()
|
||||
elif file_extension == '.pdf':
|
||||
return 'application/pdf'
|
||||
elif file_extension == '.jpeg':
|
||||
return 'image/jpeg'
|
||||
elif file_extension == '.png':
|
||||
return 'image/png'
|
||||
elif file_extension == '.tiff':
|
||||
return 'image/tiff'
|
||||
else:
|
||||
print('File extension ' + file_extension + ' not supported')
|
||||
sys.exit()
|
||||
|
||||
def printCommandDescription(exit_status=0):
|
||||
print('analyze.py <inputfile> [-t <type>]')
|
||||
print
|
||||
print('If type option is not provided, type will be inferred from file extension.')
|
||||
sys.exit(exit_status)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
|
@ -464,6 +464,17 @@ mark,
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.alight-vertical-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
}
|
||||
|
||||
.inline-item {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.list-inline-item:not(:last-child) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
|
|
@ -126,6 +126,7 @@ export const english: IAppStrings = {
|
|||
title: "Predict",
|
||||
uploadFile: "Upload file",
|
||||
inProgress: "Prediction in progress...",
|
||||
downloadScript: "Use script",
|
||||
},
|
||||
projectMetrics: {
|
||||
title: "Project Metrics",
|
||||
|
|
|
@ -127,6 +127,7 @@ export const spanish: IAppStrings = {
|
|||
title: "Predecir",
|
||||
uploadFile: "Subir archivo",
|
||||
inProgress: "Predicción en progreso ...",
|
||||
downloadScript: "Usar script",
|
||||
},
|
||||
projectMetrics: {
|
||||
title: "Métricas del proyecto",
|
||||
|
|
|
@ -126,6 +126,7 @@ export interface IAppStrings {
|
|||
title: string;
|
||||
uploadFile: string;
|
||||
inProgress: string;
|
||||
downloadScript: string;
|
||||
};
|
||||
projectMetrics: {
|
||||
title: string;
|
||||
|
|
|
@ -43,4 +43,23 @@
|
|||
border-radius: 2px;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.seperator {
|
||||
margin: 20px 10px;
|
||||
position: relative;
|
||||
background-color: inherit;
|
||||
width: 45%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.seperator:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
border-top: 1px solid white;
|
||||
background: white;
|
||||
width: 100%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import { parseTiffData, renderTiffToCanvas, loadImageToCanvas } from "../../../.
|
|||
import { constants } from "../../../../common/constants";
|
||||
import { getPrimaryGreenTheme, getPrimaryWhiteTheme } from "../../../../common/themes";
|
||||
import { SkipButton } from "../../shell/skipButton";
|
||||
import axios from "axios";
|
||||
|
||||
const cMapUrl = constants.pdfjsCMapUrl(pdfjsLib.version);
|
||||
|
||||
|
@ -154,48 +155,65 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
|
|||
<div className="predict-sidebar bg-lighter-1">
|
||||
<div className="condensed-list">
|
||||
<h6 className="condensed-list-header bg-darker-2 p-2 flex-center">
|
||||
<FontIcon iconName="Insights" />
|
||||
<FontIcon className="mr-1" iconName="Insights" />
|
||||
<span className="condensed-list-title">Predict</span>
|
||||
</h6>
|
||||
<div className="p-3">
|
||||
<h5>{strings.predict.uploadFile}</h5>
|
||||
<div style={{display: "flex", marginBottom: "25px"}}>
|
||||
<input
|
||||
aria-hidden="true"
|
||||
type="file"
|
||||
accept="application/pdf, image/jpeg, image/png, image/tiff"
|
||||
id="hiddenInputFile"
|
||||
ref={this.fileInput}
|
||||
onChange={this.handleFileChange}
|
||||
disabled={browseFileDisabled} />
|
||||
<input
|
||||
type="text"
|
||||
id="inputPredictFile"
|
||||
style = {{cursor: (browseFileDisabled ? "default" : "pointer")}}
|
||||
onClick={this.handleDummyInputClick}
|
||||
readOnly={true}
|
||||
className="dummyInputFile"
|
||||
aria-label={strings.predict.uploadFile}
|
||||
value={this.state.fileLabel}/>
|
||||
<div className="rlMargin10">
|
||||
<PrimaryButton
|
||||
theme={getPrimaryGreenTheme()}
|
||||
text="Browse"
|
||||
allowDisabledFocus
|
||||
disabled={browseFileDisabled}
|
||||
autoFocus={true}
|
||||
onClick={this.handleDummyInputClick}
|
||||
/>
|
||||
</div>
|
||||
<h5>
|
||||
{strings.predict.downloadScript}
|
||||
</h5>
|
||||
<PrimaryButton
|
||||
theme={getPrimaryGreenTheme()}
|
||||
text="Download python script"
|
||||
allowDisabledFocus
|
||||
autoFocus={true}
|
||||
onClick={this.handleDownloadClick}
|
||||
/>
|
||||
<div className="alight-vertical-center mt-2">
|
||||
<div className="seperator"/>
|
||||
or
|
||||
<div className="seperator"/>
|
||||
</div>
|
||||
<h5>
|
||||
{strings.predict.uploadFile}
|
||||
</h5>
|
||||
<div style={{display: "flex", marginBottom: "25px"}}>
|
||||
<input
|
||||
aria-hidden="true"
|
||||
type="file"
|
||||
accept="application/pdf, image/jpeg, image/png, image/tiff"
|
||||
id="hiddenInputFile"
|
||||
ref={this.fileInput}
|
||||
onChange={this.handleFileChange}
|
||||
disabled={browseFileDisabled} />
|
||||
<input
|
||||
type="text"
|
||||
id="inputPredictFile"
|
||||
style = {{cursor: (browseFileDisabled ? "default" : "pointer")}}
|
||||
onClick={this.handleDummyInputClick}
|
||||
readOnly={true}
|
||||
className="dummyInputFile"
|
||||
aria-label={strings.predict.uploadFile}
|
||||
value={this.state.fileLabel}/>
|
||||
<div className="rlMargin10">
|
||||
<PrimaryButton
|
||||
theme={getPrimaryWhiteTheme()}
|
||||
text="Predict"
|
||||
aria-label={!this.state.predictionLoaded ? strings.predict.inProgress : ""}
|
||||
theme={getPrimaryGreenTheme()}
|
||||
text="Browse"
|
||||
allowDisabledFocus
|
||||
disabled={predictDisabled}
|
||||
onClick={this.handleClick}
|
||||
disabled={browseFileDisabled}
|
||||
autoFocus={true}
|
||||
onClick={this.handleDummyInputClick}
|
||||
/>
|
||||
</div>
|
||||
<PrimaryButton
|
||||
theme={getPrimaryWhiteTheme()}
|
||||
text="Predict"
|
||||
aria-label={!this.state.predictionLoaded ? strings.predict.inProgress : ""}
|
||||
allowDisabledFocus
|
||||
disabled={predictDisabled}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</div>
|
||||
{!this.state.predictionLoaded &&
|
||||
<div className="loading-container">
|
||||
<Spinner
|
||||
|
@ -377,6 +395,63 @@ export default class PredictPage extends React.Component<IPredictPageProps, IPre
|
|||
return 1;
|
||||
}
|
||||
|
||||
private handleDownloadClick = () => {
|
||||
this.triggerDownload();
|
||||
}
|
||||
|
||||
private async triggerDownload(): Promise<any> {
|
||||
axios.get("/analyze.py").then((response) => {
|
||||
const modelID = _.get(this.props.project, "trainRecord.modelInfo.modelId") as string;
|
||||
if (!modelID) {
|
||||
throw new AppError(
|
||||
ErrorCode.PredictWithoutTrainForbidden,
|
||||
strings.errors.predictWithoutTrainForbidden.message,
|
||||
strings.errors.predictWithoutTrainForbidden.title);
|
||||
}
|
||||
const endpointURL = this.props.project.apiUriBase as string;
|
||||
const apiKey = this.props.project.apiKey as string;
|
||||
const analyzeScript = response.data.replace(/<endpoint>|<subsription_key>|<model_id>/gi,
|
||||
(matched: string) => {
|
||||
switch (matched) {
|
||||
case "<endpoint>":
|
||||
return endpointURL;
|
||||
case "<subsription_key>":
|
||||
return apiKey;
|
||||
case "<model_id>":
|
||||
return modelID;
|
||||
}
|
||||
});
|
||||
const fileURL = window.URL.createObjectURL(
|
||||
new Blob([analyzeScript]));
|
||||
const fileLink = document.createElement("a");
|
||||
const fileBaseName = "analysis";
|
||||
const downloadFileName = fileBaseName + modelID.substring(0, 4) + ".py";
|
||||
|
||||
fileLink.href = fileURL;
|
||||
fileLink.setAttribute("download", downloadFileName);
|
||||
document.body.appendChild(fileLink);
|
||||
fileLink.click();
|
||||
|
||||
}).catch((error) => {
|
||||
let alertMessage = "";
|
||||
if (error.response) {
|
||||
alertMessage = error.response.data;
|
||||
} else if (error.errorCode === ErrorCode.PredictWithoutTrainForbidden) {
|
||||
alertMessage = strings.errors.predictWithoutTrainForbidden.message;
|
||||
} else if (error.errorCode === ErrorCode.ModelNotFound) {
|
||||
alertMessage = error.message;
|
||||
} else {
|
||||
alertMessage = interpolate(strings.errors.endpointConnectionError.message, { endpoint: "form recognizer backend URL" });
|
||||
}
|
||||
this.setState({
|
||||
shouldShowAlert: true,
|
||||
alertTitle: "Download failed",
|
||||
alertMessage,
|
||||
isPredicting: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async getPrediction(): Promise<any> {
|
||||
const modelID = _.get(this.props.project, "trainRecord.modelInfo.modelId");
|
||||
if (!modelID) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче