diff --git a/src/components/svg-icon.vue b/src/components/svg-icon.vue
index b4d2c16..a8f80b9 100644
--- a/src/components/svg-icon.vue
+++ b/src/components/svg-icon.vue
@@ -6,6 +6,8 @@
+
+
@@ -16,7 +18,17 @@
icon: {
type: String,
validator(value) {
- return ['image','videocam','visibility','photo_library','no_photography','photo_camera'].includes(value)
+ const iconList = [
+ 'image',
+ 'videocam',
+ 'visibility',
+ 'photo_library',
+ 'no_photography',
+ 'photo_camera',
+ 'cloud_upload',
+ 'cloud_done'
+ ]
+ return iconList.includes(value)
}
},
fillColor: {
diff --git a/src/pages/detect.vue b/src/pages/detect.vue
index ae4b5c6..2c2cf87 100644
--- a/src/pages/detect.vue
+++ b/src/pages/detect.vue
@@ -24,20 +24,20 @@
@delete="deleteChip(result.resultIndex)"
:style="chipGradient(result.confidence)"
/>
- No results.
+ No results.
@@ -56,7 +56,6 @@
- {{ isCordova }}
@@ -84,6 +83,9 @@
+
+
+
@@ -221,7 +223,10 @@
import RegionIcon from '../components/region-icon.vue'
import SvgIcon from '../components/svg-icon.vue'
+ import submitMixin from './submit-mixin'
+
export default {
+ mixins: [submitMixin],
props: {
f7route: Object,
},
@@ -245,7 +250,9 @@
serverSettings: {},
debugOn: false,
debugText: ['Variables loaded'],
- isCordova: !!window.cordova
+ isCordova: !!window.cordova,
+ uploadUid: null,
+ uploadDirty: false
}
},
created () {
@@ -301,6 +308,7 @@
},
showResults () {
var filteredResults = this.resultData.detections
+ if (!filteredResults) return []
var allSelect = this.detectorLabels.every( s => { return s.detect } )
var selectedLabels = this.detectorLabels
.filter( l => { return l.detect })
@@ -311,6 +319,14 @@
filteredResults[i].isSearched = allSelect || selectedLabels.includes(d.label)
})
return filteredResults
+ },
+ numResults () {
+ return this.showResults.filter( r => { return r.aboveThreshold && r.isSearched && !r.isDeleted }).length
+ },
+ viewedAll () {
+ return this.resultData.detections
+ .filter( s => { return s.confidence >= this.detectorLevel})
+ .every( s => { return s.beenViewed })
}
},
methods: {
@@ -334,6 +350,7 @@
return;
}
self.resultData = JSON.parse(xhr.response)
+ self.uploadDirty = true
}
var doodsData = {
@@ -399,11 +416,13 @@
box.style.top = `${(img.offsetHeight - imgHeight) / 2 + this.resultData.detections[iChip].top * imgHeight}px`
box.style.width = `${(Math.min(this.resultData.detections[iChip].right, 1) - Math.max(this.resultData.detections[iChip].left, 0)) * imgWidth}px`
box.style.height = `${(Math.min(this.resultData.detections[iChip].bottom, 1) - Math.max(this.resultData.detections[iChip].top, 0)) * imgHeight}px`
+ this.resultData.detections[iChip].beenViewed = true
},
deleteChip ( iChip ) {
f7.dialog.confirm(`${this.resultData.detections[iChip].label} is identified with ${this.resultData.detections[iChip].confidence.toFixed(1)}% confidence. Are you sure you want to delete it?`, () => {
this.resultData.detections.splice(iChip, 1)
this.resetView()
+ this.uploadDirty = true
});
},
getImage (searchImage) {
@@ -419,7 +438,7 @@
}
resolve()
})
- loadImage.then((imageData) => {
+ loadImage.then(() => {
this.imageLoaded = true
this.resultData = {}
this.resetView()
@@ -432,6 +451,17 @@
this.selectedChip = -1
const box = this.$refs.structure_box
box.style.display = 'none'
+ },
+ videoStream() {
+ //TODO
+ return null
+ },
+ async submitData () {
+ var uploadData = this.showResults
+ .filter( d => { return d.aboveThreshold && d.isSearched && !d.isDeleted })
+ .map( r => { return {"top": r.top, "left": r.left, "bottom": r.bottom, "right": r.right, "label": r.label}})
+ this.uploadUid = await this.uploadData(this.imageView.split(',')[1],uploadData,this.uploadUid)
+ if (this.uploadUid) { this.uploadDirty = false }
}
}
}
diff --git a/src/pages/submit-mixin.js b/src/pages/submit-mixin.js
new file mode 100644
index 0000000..bc46fd7
--- /dev/null
+++ b/src/pages/submit-mixin.js
@@ -0,0 +1,67 @@
+import { f7 } from 'framework7-vue'
+
+export default {
+ methods: {
+ newUid (length) {
+ const uidLength = length || 16
+ const uidChars = 'abcdefghijklmnopqrstuvwxyz0123456789'
+ var uid = []
+ for (var i = 0; i < uidLength; i++) {
+ uid.push(uidChars.charAt(Math.floor(Math.random() * ((i < 4) ? 26 : 36))))
+ }
+ return uid.join('')
+ },
+ uploadData (imagePayload, classPayload, prevUid) {
+ let uploadImage = new Promise (resolve => {
+ const dataUid = prevUid || this.newUid(16)
+ var byteChars = window.atob(imagePayload)
+ var byteArrays = []
+ var len = byteChars.length
+
+ for (var offset = 0; offset < len; offset += 1024) {
+ var slice = byteChars.slice(offset, offset + 1024)
+ var byteNumbers = new Array(slice.length)
+ for (var i = 0; i < slice.length; i++) {
+ byteNumbers[i] = slice.charCodeAt(i)
+ }
+
+ var byteArray = new Uint8Array(byteNumbers)
+ byteArrays.push(byteArray)
+ }
+ var imageBlob = new Blob(byteArrays, {type: 'image/jpeg'})
+
+ var xhrJpg = new XMLHttpRequest()
+ var uploadUrl = `https://nextcloud.azgeorgis.net/public.php/webdav/${dataUid}.jpeg`
+ xhrJpg.open("PUT", uploadUrl)
+ xhrJpg.setRequestHeader('Content-Type', 'image/jpeg')
+ xhrJpg.setRequestHeader('X-Method-Override', 'PUT')
+ xhrJpg.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
+ xhrJpg.setRequestHeader("Authorization", "Basic " + btoa("LKBm3H6JdSaywyg:"))
+ xhrJpg.send(imageBlob)
+
+ var xhrTxt = new XMLHttpRequest()
+ var uploadUrl = `https://nextcloud.azgeorgis.net/public.php/webdav/${dataUid}.txt`
+ xhrTxt.open("PUT", uploadUrl)
+ xhrTxt.setRequestHeader('Content-Type', 'text/plain')
+ xhrTxt.setRequestHeader('X-Method-Override', 'PUT')
+ xhrTxt.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
+ xhrTxt.setRequestHeader("Authorization", "Basic " + btoa("LKBm3H6JdSaywyg:"))
+ xhrTxt.send(JSON.stringify(classPayload))
+
+ resolve(dataUid)
+ })
+ return uploadImage.then((newUid) => {
+ var toast = f7.toast.create({
+ text: 'Detections Uploaded: thank you.',
+ closeTimeout: 2000
+ })
+ toast.open()
+ return newUid
+ }).catch((e) => {
+ console.log(e.message)
+ f7.dialog.alert(`Error uploading image: ${e.message}`)
+ return null
+ })
+ }
+ }
+}
\ No newline at end of file