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:
stew-ro 2020-04-03 14:17:18 -07:00 коммит произвёл GitHub
Родитель d8dc57ca6a
Коммит ce06b7eabc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 275 добавлений и 36 удалений

131
public/analyze.py Normal file
Просмотреть файл

@ -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:])

11
src/assets/css/bootstrap-theme-slate.css поставляемый
Просмотреть файл

@ -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) {