Merge "Use database to store ota generate history." am: f051c6f342 am: 692e5f1f06
Original change: https://android-review.googlesource.com/c/platform/development/+/1736940 Change-Id: I7f8f731ef05504fafa2511b71f57f02e2a3214a4
This commit is contained in:
@@ -2,52 +2,170 @@ import subprocess
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import pipes
|
import pipes
|
||||||
from threading import Lock
|
import threading
|
||||||
|
from dataclasses import dataclass, asdict, field
|
||||||
import logging
|
import logging
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class JobInfo:
|
||||||
|
"""
|
||||||
|
A class for ota job information
|
||||||
|
"""
|
||||||
|
id: str
|
||||||
|
target: str
|
||||||
|
incremental: str = ''
|
||||||
|
verbose: bool = False
|
||||||
|
partial: list[str] = field(default_factory=list)
|
||||||
|
output: str = ''
|
||||||
|
status: str = 'Running'
|
||||||
|
downgrade: bool = False
|
||||||
|
extra: str = ''
|
||||||
|
stdout: str = ''
|
||||||
|
stderr: str = ''
|
||||||
|
start_time: int = 0
|
||||||
|
finish_time: int = 0
|
||||||
|
isPartial: bool = False
|
||||||
|
isIncremental: bool = False
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if not self.output:
|
||||||
|
self.output = os.path.join('output', self.id, '.zip')
|
||||||
|
if not self.stdout:
|
||||||
|
self.stdout = os.path.join('output/stdout.'+self.id)
|
||||||
|
if not self.stderr:
|
||||||
|
self.stderr = os.path.join('output/stderr.'+self.id)
|
||||||
|
|
||||||
|
def enforce_bool(t): return t if isinstance(t, bool) else bool(t)
|
||||||
|
self.verbose, self.downgrade = map(
|
||||||
|
enforce_bool,
|
||||||
|
[self.verbose, self.downgrade])
|
||||||
|
if self.incremental:
|
||||||
|
self.isIncremental = True
|
||||||
|
if self.partial:
|
||||||
|
self.isPartial = True
|
||||||
|
|
||||||
|
def to_sql_form_dict(self):
|
||||||
|
sql_form_dict = asdict(self)
|
||||||
|
sql_form_dict['partial'] = ','.join(sql_form_dict['partial'])
|
||||||
|
def bool_to_int(t): return 1 if t else 0
|
||||||
|
sql_form_dict['verbose'], sql_form_dict['downgrade'] = map(
|
||||||
|
bool_to_int,
|
||||||
|
[sql_form_dict['verbose'], sql_form_dict['downgrade']])
|
||||||
|
return sql_form_dict
|
||||||
|
|
||||||
|
def to_dict_basic(self):
|
||||||
|
basic_info = asdict(self)
|
||||||
|
basic_info['target_name'] = self.target.split('/')[-1]
|
||||||
|
if self.isIncremental:
|
||||||
|
basic_info['incremental_name'] = self.incremental.split('/')[-1]
|
||||||
|
return basic_info
|
||||||
|
|
||||||
|
def to_dict_detail(self, target_lib, offset=0):
|
||||||
|
detail_info = asdict(self)
|
||||||
|
try:
|
||||||
|
with open(self.stdout, 'r') as fout:
|
||||||
|
detail_info['stdout'] = fout.read()
|
||||||
|
with open(self.stderr, 'r') as ferr:
|
||||||
|
detail_info['stderr'] = ferr.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
detail_info['stdout'] = 'NO STD OUTPUT IS FOUND'
|
||||||
|
detail_info['stderr'] = 'NO STD ERROR IS FOUND'
|
||||||
|
target_info = target_lib.get_build_by_path(self.target)
|
||||||
|
detail_info['target_name'] = target_info.file_name
|
||||||
|
detail_info['target_build_version'] = target_info.build_version
|
||||||
|
if self.incremental:
|
||||||
|
incremental_info = target_lib.get_build_by_path(
|
||||||
|
self.incremental)
|
||||||
|
detail_info['incremental_name'] = incremental_info.file_name
|
||||||
|
detail_info['incremental_build_version'] = incremental_info.build_version
|
||||||
|
return detail_info
|
||||||
|
|
||||||
|
|
||||||
class ProcessesManagement:
|
class ProcessesManagement:
|
||||||
def __init__(self):
|
"""
|
||||||
self.__container = {}
|
A class manage the ota generate process
|
||||||
self.__lock = Lock()
|
"""
|
||||||
|
|
||||||
def set(self, name, value):
|
def __init__(self, path='ota_database.db'):
|
||||||
with self.__lock:
|
"""
|
||||||
self.__container[name] = value
|
create a table if not exist
|
||||||
|
"""
|
||||||
|
self.path = path
|
||||||
|
with sqlite3.connect(self.path) as connect:
|
||||||
|
cursor = connect.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE TABLE if not exists Jobs (
|
||||||
|
ID TEXT,
|
||||||
|
TargetPath TEXT,
|
||||||
|
IncrementalPath TEXT,
|
||||||
|
Verbose INTEGER,
|
||||||
|
Partial TEXT,
|
||||||
|
OutputPath TEXT,
|
||||||
|
Status TEXT,
|
||||||
|
Downgrade INTEGER,
|
||||||
|
OtherFlags TEXT,
|
||||||
|
STDOUT TEXT,
|
||||||
|
STDERR TEXT,
|
||||||
|
StartTime INTEGER,
|
||||||
|
FinishTime INTEGER
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
def get(self, name):
|
def get_status_by_ID(self, id):
|
||||||
with self.__lock:
|
with sqlite3.connect(self.path) as connect:
|
||||||
return self.__container[name]
|
cursor = connect.cursor()
|
||||||
|
logging.info(id)
|
||||||
def get_keys(self):
|
cursor.execute("""
|
||||||
with self.__lock:
|
SELECT ID, TargetPath, IncrementalPath, Verbose, Partial, OutputPath, Status, Downgrade, OtherFlags, STDOUT, STDERR, StartTime, FinishTime
|
||||||
return self.__container.keys()
|
FROM Jobs WHERE ID=(?)
|
||||||
|
""", (id,))
|
||||||
def get_status_by_ID(self, id=0, details=False):
|
row = cursor.fetchone()
|
||||||
status = {}
|
status = JobInfo(*row)
|
||||||
if not id in self.get_keys():
|
return status
|
||||||
return '{}'
|
|
||||||
else:
|
|
||||||
status['id'] = id
|
|
||||||
if self.get(id).poll() == None:
|
|
||||||
status['status'] = 'Running'
|
|
||||||
elif self.get(id).poll() == 0:
|
|
||||||
status['status'] = 'Finished'
|
|
||||||
status['path'] = os.path.join('output', str(id) + '.zip')
|
|
||||||
else:
|
|
||||||
status['status'] = 'Error'
|
|
||||||
try:
|
|
||||||
if details:
|
|
||||||
with open(os.path.join('output', 'stdout.' + str(id)), 'r') as fout:
|
|
||||||
status['stdout'] = fout.read()
|
|
||||||
with open(os.path.join('output', 'stderr.' + str(id)), 'r') as ferr:
|
|
||||||
status['stderr'] = ferr.read()
|
|
||||||
except FileNotFoundError:
|
|
||||||
status['stdout'] = 'NO STD OUTPUT IS FOUND'
|
|
||||||
status['stderr'] = 'NO STD OUTPUT IS FOUND'
|
|
||||||
return status
|
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
return [self.get_status_by_ID(id=id) for id in self.get_keys()]
|
with sqlite3.connect(self.path) as connect:
|
||||||
|
cursor = connect.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT ID, TargetPath, IncrementalPath, Verbose, Partial, OutputPath, Status, Downgrade, OtherFlags, STDOUT, STDERR, StartTime, FinishTime
|
||||||
|
FROM Jobs
|
||||||
|
""")
|
||||||
|
rows = cursor.fetchall()
|
||||||
|
statuses = [JobInfo(*row) for row in rows]
|
||||||
|
return statuses
|
||||||
|
|
||||||
|
def update_status(self, id, status, finish_time):
|
||||||
|
with sqlite3.connect(self.path) as connect:
|
||||||
|
cursor = connect.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
UPDATE Jobs SET Status=(?), FinishTime=(?)
|
||||||
|
WHERE ID=(?)
|
||||||
|
""",
|
||||||
|
(status, finish_time, id))
|
||||||
|
|
||||||
|
def ota_run(self, command, id):
|
||||||
|
# Start a subprocess and collect the output
|
||||||
|
stderr_pipes = pipes.Template()
|
||||||
|
stdout_pipes = pipes.Template()
|
||||||
|
ferr = stderr_pipes.open(os.path.join(
|
||||||
|
'output', 'stderr.'+str(id)), 'w')
|
||||||
|
fout = stdout_pipes.open(os.path.join(
|
||||||
|
'output', 'stdout.'+str(id)), 'w')
|
||||||
|
try:
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
command, stderr=ferr, stdout=fout)
|
||||||
|
except FileNotFoundError:
|
||||||
|
logging.error('ota_from_target_files is not set properly')
|
||||||
|
self.update_status(id, 'Error', int(time.time()))
|
||||||
|
return
|
||||||
|
exit_code = proc.wait()
|
||||||
|
if exit_code == 0:
|
||||||
|
self.update_status(id, 'Finished', int(time.time()))
|
||||||
|
else:
|
||||||
|
self.update_status(id, 'Error', int(time.time()))
|
||||||
|
|
||||||
def ota_generate(self, args, id=0):
|
def ota_generate(self, args, id=0):
|
||||||
command = ['ota_from_target_files']
|
command = ['ota_from_target_files']
|
||||||
@@ -71,15 +189,28 @@ class ProcessesManagement:
|
|||||||
command.append(args['partial'])
|
command.append(args['partial'])
|
||||||
command.append(args['target'])
|
command.append(args['target'])
|
||||||
command.append(args['output'])
|
command.append(args['output'])
|
||||||
# Start a subprocess and collect the output
|
job_info = JobInfo(id,
|
||||||
stderr_pipes = pipes.Template()
|
target=args['target'],
|
||||||
stdout_pipes = pipes.Template()
|
incremental=args['incremental'] if args['isIncremental'] else '',
|
||||||
ferr = stderr_pipes.open(os.path.join(
|
verbose=args['verbose'],
|
||||||
'output', 'stderr.'+str(id)), 'w')
|
partial=args['partial'].split(
|
||||||
fout = stdout_pipes.open(os.path.join(
|
' ') if args['isPartial'] else [],
|
||||||
'output', 'stdout.'+str(id)), 'w')
|
output=args['output'],
|
||||||
self.set(id, subprocess.Popen(
|
status='Running',
|
||||||
command, stderr=ferr, stdout=fout))
|
extra=args['extra'],
|
||||||
|
start_time=int(time.time())
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
thread = threading.Thread(target=self.ota_run, args=(command, id))
|
||||||
|
with sqlite3.connect(self.path) as connect:
|
||||||
|
cursor = connect.cursor()
|
||||||
|
cursor.execute("""
|
||||||
|
INSERT INTO Jobs (ID, TargetPath, IncrementalPath, Verbose, Partial, OutputPath, Status, Downgrade, OtherFlags, STDOUT, STDERR, StartTime)
|
||||||
|
VALUES (:id, :target, :incremental, :verbose, :partial, :output, :status, :downgrade, :extra, :stdout, :stderr, :start_time)
|
||||||
|
""", job_info.to_sql_form_dict())
|
||||||
|
thread.start()
|
||||||
|
except AssertionError:
|
||||||
|
raise SyntaxError
|
||||||
logging.info(
|
logging.info(
|
||||||
'Starting generating OTA package with id {}: \n {}'
|
'Starting generating OTA package with id {}: \n {}'
|
||||||
.format(id, command))
|
.format(id, command))
|
||||||
47
tools/otagui/src/components/JobConfiguration.vue
Normal file
47
tools/otagui/src/components/JobConfiguration.vue
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<ul v-if="job">
|
||||||
|
<li>Start Time: {{ formDate(job.start_time) }}</li>
|
||||||
|
<li v-if="job.finish_time > 0">
|
||||||
|
Finish Time: {{ formDate(job.finish_time) }}
|
||||||
|
</li>
|
||||||
|
<li v-if="job.isIncremental">
|
||||||
|
Incremental source: {{ job.incremental_name }}
|
||||||
|
</li>
|
||||||
|
<li v-if="job.isIncremental && buildDetail">
|
||||||
|
Incremental source version: {{ job.incremental_build_version }}
|
||||||
|
</li>
|
||||||
|
<li>Target source: {{ job.target_name }}</li>
|
||||||
|
<li v-if="buildDetail">
|
||||||
|
Target source version: {{ job.target_build_version }}
|
||||||
|
</li>
|
||||||
|
<li v-if="job.isPartial">
|
||||||
|
Partial: {{ job.partial }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import FormDate from '../services/FormDate.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
FormDate,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
job: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
buildDetail: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formDate(unixTime) {
|
||||||
|
return FormDate.formDate(unixTime)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,26 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<router-link
|
<router-link :to="{ name: 'JobDetails', params: { id: job.id } }">
|
||||||
:to="{ name: 'JobDetails', params: {id: job.id} }"
|
|
||||||
>
|
|
||||||
<div class="job-display">
|
<div class="job-display">
|
||||||
<span>Status of Job.{{ job.id }}</span>
|
<span>Status of Job.{{ job.id }}</span>
|
||||||
<h4>{{ job.status }}</h4>
|
<h4>{{ job.status }}</h4>
|
||||||
|
<div v-show="active">
|
||||||
|
<JobConfiguration
|
||||||
|
:job="job"
|
||||||
|
:build-detail="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import JobConfiguration from '../components/JobConfiguration.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
JobConfiguration
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
job: {
|
job: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style>
|
||||||
.job-display {
|
.job-display {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
|
|||||||
18
tools/otagui/src/services/FormDate.js
Normal file
18
tools/otagui/src/services/FormDate.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export default{
|
||||||
|
formDate(unixTime) {
|
||||||
|
let formTime = new Date(unixTime * 1000)
|
||||||
|
let date =
|
||||||
|
formTime.getFullYear() +
|
||||||
|
'-' +
|
||||||
|
(formTime.getMonth() + 1) +
|
||||||
|
'-' +
|
||||||
|
formTime.getDate()
|
||||||
|
let time =
|
||||||
|
formTime.getHours() +
|
||||||
|
':' +
|
||||||
|
formTime.getMinutes() +
|
||||||
|
':' +
|
||||||
|
formTime.getSeconds()
|
||||||
|
return date + ' ' + time
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="job">
|
<div v-if="job">
|
||||||
<h3>Job. {{ job.id }} {{ job.status }}</h3>
|
<h3>Job. {{ job.id }} {{ job.status }}</h3>
|
||||||
|
<JobConfiguration
|
||||||
|
:job="job"
|
||||||
|
:build-detail="true"
|
||||||
|
/>
|
||||||
<div>
|
<div>
|
||||||
<h4>STDERR</h4>
|
<h4>STDERR</h4>
|
||||||
<div class="stderr">
|
<div
|
||||||
|
ref="stderr"
|
||||||
|
class="stderr"
|
||||||
|
>
|
||||||
{{ job.stderr }}
|
{{ job.stderr }}
|
||||||
<p ref="stderr_bottom" />
|
<p ref="stderrBottom" />
|
||||||
</div>
|
</div>
|
||||||
<h4>STDOUT</h4>
|
<h4>STDOUT</h4>
|
||||||
<div class="stdout">
|
<div
|
||||||
|
ref="stdout"
|
||||||
|
class="stdout"
|
||||||
|
>
|
||||||
{{ job.stdout }}
|
{{ job.stdout }}
|
||||||
<p ref="stdout_bottom" />
|
<p ref="stdoutBottom" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
@@ -22,9 +32,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
import ApiService from '../services/ApiService.js'
|
import ApiService from '../services/ApiService.js'
|
||||||
|
import JobConfiguration from '../components/JobConfiguration.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
ApiService,
|
||||||
|
JobConfiguration,
|
||||||
|
},
|
||||||
props: ['id'],
|
props: ['id'],
|
||||||
|
setup() {
|
||||||
|
const stderr = ref()
|
||||||
|
const stdout = ref()
|
||||||
|
const stderrBottom = ref()
|
||||||
|
const stdoutBottom = ref()
|
||||||
|
return { stderr, stdout, stderrBottom, stdoutBottom }
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
job: null,
|
job: null,
|
||||||
@@ -32,7 +56,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
download() {
|
download() {
|
||||||
return 'http://localhost:8000/download/' + this.job.path
|
return 'http://localhost:8000/download/' + this.job.output
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -44,15 +68,27 @@ export default {
|
|||||||
try {
|
try {
|
||||||
let response = await ApiService.getJobById(this.id)
|
let response = await ApiService.getJobById(this.id)
|
||||||
this.job = response.data
|
this.job = response.data
|
||||||
await this.$refs.stdout_bottom.scrollIntoView({ behavior: 'smooth' })
|
} catch (err) {
|
||||||
await this.$refs.stderr_bottom.scrollIntoView({ behavior: 'smooth' })
|
console.log(err)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.$nextTick(() => {
|
||||||
|
this.stderr.scrollTo({
|
||||||
|
top: this.stderrBottom.offsetTop,
|
||||||
|
behavior: 'smooth',
|
||||||
|
})
|
||||||
|
this.stdout.scrollTo({
|
||||||
|
top: this.stdoutBottom.offsetTop,
|
||||||
|
behavior: 'smooth',
|
||||||
|
})
|
||||||
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
if (this.job.status == 'Running') {
|
if (this.job.status == 'Running') {
|
||||||
setTimeout(this.updateStatus, 1000)
|
setTimeout(this.updateStatus, 1000)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -60,7 +96,7 @@ export default {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.stderr,
|
.stderr,
|
||||||
.stdout {
|
.stdout {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -4,6 +4,9 @@
|
|||||||
v-for="job in jobs"
|
v-for="job in jobs"
|
||||||
:key="job.id"
|
:key="job.id"
|
||||||
:job="job"
|
:job="job"
|
||||||
|
:active="overStatus.get(job.id)"
|
||||||
|
@mouseover="mouseOver(job.id, true)"
|
||||||
|
@mouseout="mouseOver(job.id, false)"
|
||||||
/>
|
/>
|
||||||
<button @click="updateStatus">
|
<button @click="updateStatus">
|
||||||
Update
|
Update
|
||||||
@@ -23,6 +26,7 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
jobs: null,
|
jobs: null,
|
||||||
|
overStatus: new Map()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created (){
|
created (){
|
||||||
@@ -36,6 +40,9 @@ export default {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mouseOver(id, status) {
|
||||||
|
this.overStatus.set(id, status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ import FileSelect from '@/components/FileSelect.vue'
|
|||||||
import ApiService from '../services/ApiService.js'
|
import ApiService from '../services/ApiService.js'
|
||||||
import UploadFile from '@/components/UploadFile.vue'
|
import UploadFile from '@/components/UploadFile.vue'
|
||||||
import PartialCheckbox from '@/components/PartialCheckbox.vue'
|
import PartialCheckbox from '@/components/PartialCheckbox.vue'
|
||||||
|
import FormDate from '../services/FormDate.js'
|
||||||
import { uuid } from 'vue-uuid'
|
import { uuid } from 'vue-uuid'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -111,6 +112,7 @@ export default {
|
|||||||
UploadFile,
|
UploadFile,
|
||||||
FileSelect,
|
FileSelect,
|
||||||
PartialCheckbox,
|
PartialCheckbox,
|
||||||
|
FormDate,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -162,7 +164,8 @@ export default {
|
|||||||
partial: '',
|
partial: '',
|
||||||
isPartial: false,
|
isPartial: false,
|
||||||
extra: '',
|
extra: '',
|
||||||
}
|
},
|
||||||
|
this.partitionInclude = new Map()
|
||||||
},
|
},
|
||||||
async sendForm(e) {
|
async sendForm(e) {
|
||||||
try {
|
try {
|
||||||
@@ -189,20 +192,7 @@ export default {
|
|||||||
this.input.output += String(this.id) + '.zip'
|
this.input.output += String(this.id) + '.zip'
|
||||||
},
|
},
|
||||||
formDate(unixTime) {
|
formDate(unixTime) {
|
||||||
let formTime = new Date(unixTime * 1000)
|
return FormDate.formDate(unixTime)
|
||||||
let date =
|
|
||||||
formTime.getFullYear() +
|
|
||||||
'-' +
|
|
||||||
(formTime.getMonth() + 1) +
|
|
||||||
'-' +
|
|
||||||
formTime.getDate()
|
|
||||||
let time =
|
|
||||||
formTime.getHours() +
|
|
||||||
':' +
|
|
||||||
formTime.getMinutes() +
|
|
||||||
':' +
|
|
||||||
formTime.getSeconds()
|
|
||||||
return date + ' ' + time
|
|
||||||
},
|
},
|
||||||
selectTarget(path) {
|
selectTarget(path) {
|
||||||
this.input.target = path
|
this.input.target = path
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ class BuildInfo:
|
|||||||
|
|
||||||
|
|
||||||
class TargetLib:
|
class TargetLib:
|
||||||
|
"""
|
||||||
|
A class that manages the builds in database.
|
||||||
|
"""
|
||||||
def __init__(self, path='ota_database.db'):
|
def __init__(self, path='ota_database.db'):
|
||||||
"""
|
"""
|
||||||
Create a build table if not existing
|
Create a build table if not existing
|
||||||
@@ -142,7 +145,7 @@ class TargetLib:
|
|||||||
FROM Builds""")
|
FROM Builds""")
|
||||||
return list(map(self.sql_to_buildinfo, cursor.fetchall()))
|
return list(map(self.sql_to_buildinfo, cursor.fetchall()))
|
||||||
|
|
||||||
def get_builds_by_path(self, path):
|
def get_build_by_path(self, path):
|
||||||
"""
|
"""
|
||||||
Get a build in the database by its path
|
Get a build in the database by its path
|
||||||
Return:
|
Return:
|
||||||
@@ -153,6 +156,6 @@ class TargetLib:
|
|||||||
cursor = connect.cursor()
|
cursor = connect.cursor()
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
SELECT FileName, Path, UploadTime, BuildID, BuildVersion, BuildFlavor, Partitions
|
SELECT FileName, Path, UploadTime, BuildID, BuildVersion, BuildFlavor, Partitions
|
||||||
WHERE Path==(?)
|
FROM Builds WHERE Path==(?)
|
||||||
""", (path, ))
|
""", (path, ))
|
||||||
return self.sql_to_buildinfo(cursor.fetchone())
|
return self.sql_to_buildinfo(cursor.fetchone())
|
||||||
|
|||||||
@@ -66,22 +66,19 @@ class RequestHandler(CORSSimpleHTTPHandler):
|
|||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
if self.path.startswith('/check'):
|
if self.path.startswith('/check'):
|
||||||
if self.path == '/check' or self.path == '/check/':
|
if self.path == '/check' or self.path == '/check/':
|
||||||
status = jobs.get_status()
|
statuses = jobs.get_status()
|
||||||
self._set_response(type='application/json')
|
self._set_response(type='application/json')
|
||||||
self.wfile.write(
|
self.wfile.write(
|
||||||
json.dumps(status).encode()
|
json.dumps([status.to_dict_basic()
|
||||||
|
for status in statuses]).encode()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
id = self.path[7:]
|
id = self.path[7:]
|
||||||
status = jobs.get_status_by_ID(id=id, details=True)
|
status = jobs.get_status_by_ID(id=id)
|
||||||
self._set_response(type='application/json')
|
self._set_response(type='application/json')
|
||||||
self.wfile.write(
|
self.wfile.write(
|
||||||
json.dumps(status).encode()
|
json.dumps(status.to_dict_detail(target_lib)).encode()
|
||||||
)
|
)
|
||||||
logging.info(
|
|
||||||
"GET request:\nPath:%s\nHeaders:\n%s\nBody:\n%s\n",
|
|
||||||
str(self.path), str(self.headers), status
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
elif self.path.startswith('/file'):
|
elif self.path.startswith('/file'):
|
||||||
if self.path == '/file' or self.path == '/file/':
|
if self.path == '/file' or self.path == '/file/':
|
||||||
|
|||||||
Reference in New Issue
Block a user