kopia lustrzana https://github.com/OpenDroneMap/WebODM
Credits estimate finished
rodzic
bd2fff98ad
commit
66b0c4f6ee
|
@ -180,6 +180,7 @@ footer,
|
|||
/* Highlight */
|
||||
.task-list-item:nth-child(odd),
|
||||
.table-striped>tbody>tr:nth-of-type(odd),
|
||||
select.form-control option[disabled],
|
||||
.theme-background-highlight{
|
||||
background-color: theme("highlight");
|
||||
}
|
||||
|
|
|
@ -76,6 +76,10 @@ class NewTaskPanel extends React.Component {
|
|||
setResizeMode(v){
|
||||
return e => {
|
||||
this.setState({resizeMode: v});
|
||||
|
||||
setTimeout(() => {
|
||||
this.handleFormChanged();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +88,10 @@ class NewTaskPanel extends React.Component {
|
|||
let n = parseInt(e.target.value.replace(/[^\d]*/g, ""));
|
||||
if (isNaN(n)) n = "";
|
||||
this.setState({resizeSize: n});
|
||||
|
||||
setTimeout(() => {
|
||||
this.handleFormChanged();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
handleFormTaskLoaded(){
|
||||
|
@ -139,6 +147,7 @@ class NewTaskPanel extends React.Component {
|
|||
{this.state.items.map((Item, i) => <div key={i} className="form-group">
|
||||
<Item taskInfo={this.state.taskInfo}
|
||||
getFiles={this.props.getFiles}
|
||||
filesCount={this.props.filesCount}
|
||||
/>
|
||||
</div>)}
|
||||
</div>
|
||||
|
|
|
@ -14,6 +14,8 @@ from django.views.decorators.http import require_POST
|
|||
from nodeodm.models import ProcessingNode
|
||||
from app.api.processingnodes import ProcessingNodeSerializer
|
||||
|
||||
ds = GlobalDataStore('lightning')
|
||||
|
||||
def JsonResponse(dict):
|
||||
return HttpResponse(json.dumps(dict), content_type='application/json')
|
||||
|
||||
|
@ -30,11 +32,11 @@ class Plugin(PluginBase):
|
|||
def app_mount_points(self):
|
||||
@login_required
|
||||
def main(request):
|
||||
ds = UserDataStore('lightning', request.user)
|
||||
uds = UserDataStore('lightning', request.user)
|
||||
|
||||
return render(request, self.template_path("index.html"), {
|
||||
'title': 'Lightning Network',
|
||||
'api_key': ds.get_string("api_key")
|
||||
'api_key': uds.get_string("api_key")
|
||||
})
|
||||
|
||||
@login_required
|
||||
|
@ -44,16 +46,14 @@ class Plugin(PluginBase):
|
|||
if api_key is None:
|
||||
return JsonResponse({'error': 'api_key is required'})
|
||||
|
||||
ds = UserDataStore('lightning', request.user)
|
||||
ds.set_string('api_key', api_key)
|
||||
uds = UserDataStore('lightning', request.user)
|
||||
uds.set_string('api_key', api_key)
|
||||
|
||||
return JsonResponse({'success': True})
|
||||
|
||||
@login_required
|
||||
@require_POST
|
||||
def sync_processing_node(request):
|
||||
ds = GlobalDataStore('lightning')
|
||||
|
||||
hostname = request.POST.get('hostname')
|
||||
port = int(request.POST.get('port'))
|
||||
token = request.POST.get('token')
|
||||
|
@ -83,8 +83,6 @@ class Plugin(PluginBase):
|
|||
|
||||
@login_required
|
||||
def get_processing_nodes(request):
|
||||
ds = GlobalDataStore('lightning')
|
||||
|
||||
nodes = get_objects_for_user(request.user, 'view_processingnode', ProcessingNode,
|
||||
accept_global_perms=False)
|
||||
lightning_node_ids = ds.get_json("nodes", [])
|
||||
|
@ -94,19 +92,23 @@ class Plugin(PluginBase):
|
|||
|
||||
return JsonResponse(serializer.data)
|
||||
|
||||
@login_required
|
||||
def is_lightning_node(request):
|
||||
lightning_node_ids = ds.get_json("nodes", [])
|
||||
return JsonResponse({'result': int(request.GET.get('id')) in lightning_node_ids})
|
||||
|
||||
|
||||
return [
|
||||
MountPoint('$', main),
|
||||
MountPoint('save_api_key$', save_api_key),
|
||||
MountPoint('sync_processing_node$', sync_processing_node),
|
||||
MountPoint('get_processing_nodes$', get_processing_nodes),
|
||||
MountPoint('is_lightning_node$', is_lightning_node),
|
||||
]
|
||||
|
||||
|
||||
@receiver(signals.processing_node_removed, dispatch_uid="lightning_on_processing_node_removed")
|
||||
def lightning_on_processing_node_removed(sender, processing_node_id, **kwargs):
|
||||
ds = GlobalDataStore('lightning')
|
||||
|
||||
node_ids = ds.get_json('nodes', [])
|
||||
try:
|
||||
node_ids.remove(processing_node_id)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import './CostEstimateItem.scss';
|
||||
import ResizeModes from 'webodm/classes/ResizeModes';
|
||||
import PropTypes from 'prop-types';
|
||||
import $ from 'jquery';
|
||||
|
||||
|
@ -7,21 +8,140 @@ export default class CostEstimateItem extends React.Component {
|
|||
static defaultProps = {
|
||||
};
|
||||
static propTypes = {
|
||||
taskInfo: PropTypes.object.isRequired,
|
||||
getFiles: PropTypes.func.isRequired,
|
||||
filesCount: PropTypes.number.isRequired
|
||||
}
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
show: false,
|
||||
credits: ""
|
||||
}
|
||||
|
||||
this.imageWidth = null;
|
||||
this.imageHeight = null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps){
|
||||
if (prevProps.taskInfo.selectedNode &&
|
||||
this.props.taskInfo.selectedNode &&
|
||||
prevProps.taskInfo.selectedNode.key !== this.props.taskInfo.selectedNode.key){
|
||||
if (this.props.taskInfo.selectedNode.key !== "auto"){
|
||||
$.get(`/plugins/lightning/is_lightning_node?id=${this.props.taskInfo.selectedNode.key}`)
|
||||
.done(json => {
|
||||
if (json.result !== undefined){
|
||||
this.setState({show: json.result});
|
||||
if (json.result) this.estimateCredits();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.show){
|
||||
if (prevProps.taskInfo.resizeMode !== this.props.taskInfo.resizeMode ||
|
||||
prevProps.taskInfo.resizeSize !== this.props.taskInfo.resizeSize){
|
||||
this.estimateCredits();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extractImageDimensions = (file, cb) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (entry => {
|
||||
var image = new Image();
|
||||
image.src = entry.target.result;
|
||||
image.onload = function() {
|
||||
cb({
|
||||
width: this.width,
|
||||
height: this.height
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
estimateCredits = () => {
|
||||
this.setState({loading: true});
|
||||
|
||||
const getEstimate = () => {
|
||||
const { taskInfo } = this.props;
|
||||
|
||||
let width = this.imageWidth;
|
||||
let height = this.imageHeight;
|
||||
|
||||
if (taskInfo.resizeMode === ResizeModes.YES ||
|
||||
taskInfo.resizeMode === ResizeModes.YESINBROWSER){
|
||||
let largestSide = Math.max(width, height);
|
||||
let multiplier = taskInfo.resizeSize / largestSide;
|
||||
|
||||
width = Math.ceil(width * multiplier);
|
||||
height = Math.ceil(height * multiplier);
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
}
|
||||
|
||||
if (this.estimateRequest){
|
||||
this.estimateRequest.abort();
|
||||
this.estimateRequest = null;
|
||||
}
|
||||
|
||||
this.estimateRequest = $.get("https://webodm.net/r/tasks/estimateCost", {
|
||||
images: this.props.filesCount,
|
||||
width,
|
||||
height
|
||||
}).done(json => {
|
||||
if (json.credits_estimate !== undefined){
|
||||
if (json.credits_estimate === 0) json.credits_estimate += " (Free)";
|
||||
this.setState({credits: json.credits_estimate})
|
||||
}else{
|
||||
this.setState({credits: "Cannot retrieve estimate. Try again later."});
|
||||
}
|
||||
}).fail(e => {
|
||||
this.setState({credits: `Cannot retrieve estimate. Check parameters or try again later.`});
|
||||
}).always(() => {
|
||||
this.setState({loading: false});
|
||||
this.estimateRequest = null;
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.filesCount > 0){
|
||||
if (this.imageWidth === null && this.imageHeight === null){
|
||||
const files = this.props.getFiles();
|
||||
const imageFile = Array.prototype.find.call(files, f => f.type.startsWith("image/"));
|
||||
if (imageFile){
|
||||
this.extractImageDimensions(files[0], dims => {
|
||||
this.imageWidth = dims.width;
|
||||
this.imageHeight = dims.height;
|
||||
getEstimate();
|
||||
});
|
||||
}else{
|
||||
this.setState({show: false});
|
||||
}
|
||||
}else{
|
||||
getEstimate();
|
||||
}
|
||||
}else{
|
||||
this.setState({show: false});
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
const { show, loading, credits } = this.state;
|
||||
|
||||
return (
|
||||
<div className="lightning-cost-estimate-item">
|
||||
<label className="col-sm-2 control-label">Credits Required</label>
|
||||
<div className={"lightning-cost-estimate-item " + (show ? "" : "hide")}>
|
||||
<label className="col-sm-2 control-label">Credits Estimate</label>
|
||||
<div className="col-sm-10 num-credits">
|
||||
{this.props.taskInfo.name}
|
||||
{loading ?
|
||||
<i className="fa fa-circle-o-notch fa-spin"></i> :
|
||||
<div>
|
||||
{ credits }
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -26,7 +26,7 @@ export default class Dashboard extends React.Component {
|
|||
}
|
||||
|
||||
apiUrl = url => {
|
||||
return `http://192.168.2.253:5000${url}?api_key=${this.props.apiKey}`;
|
||||
return `https://webodm.net${url}?api_key=${this.props.apiKey}`;
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
|
@ -121,10 +121,11 @@ export default class Dashboard extends React.Component {
|
|||
</div> }
|
||||
</div>
|
||||
|
||||
<div className="estimator">
|
||||
<h5>Cost Calculator</h5>
|
||||
Drag & drop some images below to estimate the number of credits required to process them with lightning.
|
||||
</div>
|
||||
{nodes.length > 0 ?
|
||||
<div>
|
||||
<hr/>
|
||||
<i className="fa fa-thumbs-o-up"></i> You are all set! When creating a new task from the <a href="/dashboard">Dashboard</a>, select <strong>{nodes[0].hostname}:{nodes[0].port}</strong> from the <strong>Processing Node</strong> drop-down instead of Auto.
|
||||
</div> : ""}
|
||||
|
||||
<div className="buttons text-right">
|
||||
<hr/>
|
||||
|
|
|
@ -33,7 +33,7 @@ export default class Login extends React.Component {
|
|||
handleLogin = () => {
|
||||
this.setState({loggingIn: true});
|
||||
|
||||
$.post("http://192.168.2.253:5000/r/auth/login",//"https://webodm.net/r/auth/login",
|
||||
$.post("https://webodm.net/r/auth/login",
|
||||
{
|
||||
username: this.state.email,
|
||||
password: this.state.password
|
||||
|
|
Ładowanie…
Reference in New Issue