This commit is contained in:
Andy Bunce 2020-10-22 22:19:51 +01:00
parent 5d6aa4e12b
commit a20b64c6bd
48 changed files with 1418 additions and 1263 deletions

View file

@ -8,7 +8,9 @@ Includes:
* localforage for persistence
## Icons
* https://material.io/resources/icons/?style=baseline
* https://vuetifyjs.com/en/customization/icons/
## Tests
### Cypress
@ -23,7 +25,14 @@ or...
npx cypress run
```
## Settings
Required BaseX options https://docs.basex.org/wiki/Options
```
CHOP = false
LOGTRACE = false
RESTXQERRORS = false
GZIP = true
```
### Other
Global `settings` provides `getItem(name)` and `setItem(name.value)`
Example usage
```

View file

@ -158,7 +158,7 @@
model: false,
children: [
{href: '/tasks',text: 'Task list',icon: 'assignment'},
{href: '/history/tasks',text: 'History',icon: 'history'}
{href: '/history/tasks',text: 'Run history',icon: 'history'}
]},
{
icon: 'folder_open',

View file

@ -0,0 +1,108 @@
<!DOCTYPE html>
<!--
perfstats
https://adamwathan.me/renderless-components-in-vuejs/
-->
<template id="qd-perfstats">
<div>
<slot name="actions" :add="add" :repeats="repeats" :reset="reset" :clip="clip"></slot>
<hr/>
<slot name="table" :items="items" :headers="headers" :run="run"></slot>
<slot name="response" :data="response" ></slot>
</div>
</template>
<script>{
props: {
initial: {default: function(){ return []}
}
},
data: function(){
return {
items: [],
headers: [
{ text: 'Action', value: 'id'},
{ text: 'Repeat', value: 'repeat' },
{ text: 'Count', value: 'count', align: 'right' },
{ text: 'Max', value: 'max' , align: 'right' },
{ text: 'Min', value: 'min' , align: 'right'},
{ text: 'Median', value: 'median', align: 'right' },
{ text: 'Last', value: 'last' , align: 'right'},
{ text: 'Average', value: 'avg' , align: 'right'}
],
response: null
}
},
methods:{
add(id,method,url){
var obj={index:this.items.length, id: id, method:method, url:url, repeat: false,
count: 0, max: null, min: null, total:0, median:null, last:null, avg:null}
this.items.push(obj)
},
run(index){
this.update(this.items[index])
},
clear(index){
var data=this.items[index]
data.count= data.total= 0
data.max= data.min= data.last= data.avg= data.median= null
},
update (item) {
var _start = performance.now();
HTTP.request(item.url, {method: item.method, headers: {accept: 'application/json'}})
.then(r=>{
var elapsed=Math.floor(performance.now() - _start);
this.response=r.data
this.log(item,elapsed)
if(item.repeat) this.update(item);
})
},
// update item stats
log(item,val){
item.last= val
item.total+= val;
item.count+= 1;
if(item.count==1){
item.max=val;
item.min=val;
item.median=val;
}else{
if(val<item.min)item.min=val;
if(val>item.max)item.max=val;
};
//https://jeremykun.com/2012/06/14/streaming-median/
if (item.median > val)
item.median-= 1
else if( item.median < val)
item.median += 1;
item.avg=(item.total / item.count).toFixed(2);
},
repeats(b){
this.$nextTick(() => {
this.items.forEach(item=>item.repeat=b)
})
},
reset(){
this.items.forEach(item=>this.clear(item.index))
},
clip(){
var txt=this.items.map(item=>item.id + ',' + item.avg).join("\n")
navigator.clipboard.writeText(txt).then(function() {
/* clipboard successfully set */
}, function() {
alert("clipboard write failed")
});
}
},
created:function(){
console.log("qd-perfstats:",this.initial);
this.initial.forEach(item=>this.add(item.id,item.method,item.url))
}
}
</script>

View file

@ -30,12 +30,20 @@
</v-btn>
</template>
<v-card >
<v-toolbar color="cyan lighten-2">
<v-card-title >Actions</v-card-title>
</v-toolbar>
<v-app-bar dense color="cyan lighten-2" >
<v-card-title dense>Actions</v-card-title>
</v-app-bar>
<v-card-text>
<v-list dense>
<slot name="actions"></slot>
<v-list-item @click="copy">
<v-list-item-avatar><v-icon>content_copy</v-icon></v-list-item-avatar>
<v-list-item-title>Copy selection</v-list-item-title>
</v-list-item>
</v-list>
</v-card-text>
</v-card>
</v-menu>
@ -89,6 +97,7 @@
class="elevation-1"
:fixed-header="true"
:no-data-text="noDataMsg"
>
<template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope"><slot :name="slot" v-bind="scope"/></template>
@ -100,15 +109,22 @@
<script>{
props: {
headers: {default: [ { text: 'Name', value: 'id'} ]},
dataUri:{ default: "data/dice.entity"},
dataUri:{ default: null},
itemKey:{ default: "id"},
noDataMsg:{ default: "No data found."},
title:{ default: "" },
entity:{ },
query: {default: function(){return {filter:null}}},
showSelect: { default: false },
multiSort: { default: false }
multiSort: { default: false },
customFilter: {default: function(value, search, item) {
return value != null &&
search != null &&
typeof value === 'string' &&
value.toString().indexOf(search) !== -1}
}
},
data: function(){
return {
selected: [],
@ -119,8 +135,10 @@
autoRefreshL: false
}
},
methods:{
getItems(){
if(this.dataUri === null) return;
this.loading=true;
HTTP.get(this.dataUri)
.then(r=>{
@ -129,6 +147,20 @@
this.items=r.data.items;
if(this.autoRefreshL) this.timer=setTimeout(()=>{ this.getItems() }, 10000);
})
},
copy(){
var flds=this.headers.map(h=>h.value)
var row=function(item){return flds.map(f=>item.hasOwnProperty(f)?item[f]:'').join(",")}
var txt=flds.join(",")
var txt=this.selected.map(item=>row(item)).join("\n")
txt= txt=flds.join(",") +"\n" + txt
navigator.clipboard.writeText(txt).then(function() {
/* clipboard successfully set */
}, function() {
alert("clipboard write failed")
});
}
},

View file

@ -4,13 +4,13 @@
-->
<template id="vp-notifications">
<v-card>
<v-app-bar class="amber white--text" >
<v-toolbar-title >Notifications </v-toolbar-title>
{{ $notification.nextId }}
<v-app-bar dense class="amber white--text" >
<v-toolbar-title > {{ $notification.nextId }} Notifications </v-toolbar-title>
<v-btn @click="refresh" icon><v-icon>refresh</v-icon></v-btn>
<v-spacer></v-spacer>
<v-btn @click="set(false)" icon><v-icon>close</v-icon></v-btn>
</v-app-bar>
<v-card-text>
<v-list three-line>
<template v-for="msg in $notification.messages" >

View file

@ -8,9 +8,12 @@
<v-toolbar color="blue lighten-3" dense>
<v-card-title >{{ description }}</v-card-title>
<v-spacer></v-spacer>
<router-link :to="{name:'edit', query:{url: url}}">
<v-icon :title="url">history</v-icon>{{ name }}
</router-link>
<v-btn @click="clear()" id="btn-clear"
>Clear</v-btn>
<v-btn @click="reset()"
>Reset</v-btn>
<v-btn @click="zlog()"
>console</v-btn>
</v-toolbar>
<v-card-text>
<v-form ref="form" lazy-validation>
@ -43,15 +46,7 @@
</v-form>
</v-card-text>
<v-card-actions>
<v-btn @click="clear()" id="btn-clear"
>Clear</v-btn>
<v-btn @click="reset()"
>Reset</v-btn>
</v-card-actions>
<v-btn @click="zlog()"
>console</v-btn>
</v-card-actions>
</v-card>
</template>

View file

@ -24,15 +24,20 @@ axios.interceptors.response.use(function (response) {
// errors displayed by interceptor
const HTTP = axios.create(AXIOS_CONFIG);
HTTP.interceptors.request.use((config) => {
HTTP.interceptors.request.use(
(config) => {
config.qdStartTime=performance.now();
return config;
});
return config;}
);
HTTP.interceptors.response.use((response) => {
HTTP.interceptors.response.use(
(response) => {
// Do something with response data
if( response && response.config && response.config.qdStartTime){
var s=Math.floor(performance.now() - response.config.qdStartTime);
response.headers["X-response-ms"]= s // custom header
//console.log("AXIOS H:",response.headers)
var c=response.config;
var url=response.config.url + "?" + c.paramsSerializer(c.params);
//console.log("interceptors time:",s, response.config);
@ -40,7 +45,8 @@ HTTP.interceptors.response.use((response) => {
Notification.add({html: b, elapsed: s});
}
return response;
});
}
);
// errors hidden

View file

@ -0,0 +1,24 @@
<entity name="basex.database" xmlns="https://github.com/Quodatum/app-doc/entity">
<description>BaseX databases</description>
<fields>
<field name="name" type="xs:string">
<description>database name</description>
<xpath>.</xpath>
</field>
<field name="documents" type="xs:integer">
<description>number of documents in database</description>
<xpath>db:property(.,'documents')</xpath>
</field>
<field name="binaries" type="xs:integer">
<description>non xml documents</description>
<xpath>db:property(.,'binaries')</xpath>
</field>
<field name="timestamp" type="xs:string">
<description>last update</description>
<xpath>db:property(.,'timestamp')</xpath>
</field>
</fields>
<views iconclass="library_books"/>
<data type="xs:string">db:list()</data>
</entity>

View file

@ -0,0 +1,3 @@
# BaseX entities
Objects mapping BaseX features

View file

@ -0,0 +1,3 @@
# Dice entities
The entities

View file

@ -1,4 +1,4 @@
<entity name="filehistory" xmlns="https://github.com/Quodatum/app-doc/entity">
<entity name="history.file" xmlns="https://github.com/Quodatum/app-doc/entity">
<description>vue-poc file view events </description>
<namespace prefix="h" uri="urn:quodatum:vue-poc.history" />
<fields>

View file

@ -0,0 +1,3 @@
# History entities
Logs for web application

View file

@ -1,4 +1,4 @@
<entity name="taskhistory" xmlns="https://github.com/Quodatum/app-doc/entity">
<entity name="history.task" xmlns="https://github.com/Quodatum/app-doc/entity">
<description>vue-poc task view events </description>
<namespace prefix="h" uri="urn:quodatum:vue-poc.history" />
<fields>
@ -14,16 +14,24 @@
<description>id</description>
<xpath>@id</xpath>
</field>
<field name="protocol" type="xs:string">
<description>mode eg file basexdb</description>
<xpath>h:file/@mode</xpath>
<field name="task" type="xs:string">
<description>name of task</description>
<xpath>h:task/@task</xpath>
</field>
<field name="url" type="xs:string">
<description>path</description>
<xpath>h:file/@url</xpath>
<xpath>h:task/@url</xpath>
</field>
<field name="arity" type="xs:integer">
<description>number of parameters</description>
<xpath>count(h:task/*:param)</xpath>
</field>
<field name="summary" type="xs:string">
<description>Parameter summary</description>
<xpath>string-join(h:task/h:param/concat(string(@name),'=',string(.)),'; ')</xpath>
</field>
</fields>
<views iconclass="calendar_today"/>
<data type="element(h:event)">doc("vue-poc/history.xml")/h:history/h:event[task]</data>
<data type="element(h:event)">doc("vue-poc/history.xml")/h:history/h:event[h:task]</data>
</entity>

View file

@ -0,0 +1,3 @@
# Quodatum entities
Logs for web application

View file

@ -1,5 +1,6 @@
<entity name="quodatum.task" xmlns="https://github.com/Quodatum/app-doc/entity">
<description>Predefined queries with parameters, listed in taskdef.xml </description>
<namespace prefix="t" uri="https://github.com/Quodatum/task" />
<fields>
<field name="to" type="xs:string">
<description>name for task</description>
@ -11,13 +12,13 @@
</field>
<field name="title" type="xs:string">
<description>title</description>
<xpath>title</xpath>
<xpath>t:title</xpath>
</field>
<field name="description" type="xs:string">
<description>task description</description>
<xpath>fn:serialize(description/node())</xpath>
<xpath>fn:serialize(t:description/node())</xpath>
</field>
</fields>
<views iconclass="update"/>
<data type="element(task)">doc("tasks/taskdef.xml")/tasks/task</data>
<data type="element(t:task)">doc("tasks/taskdef.xml")/t:tasks/t:task</data>
</entity>

View file

@ -1,38 +1,21 @@
<history next-id="13" xmlns="urn:quodatum:vue-poc.history">
<event id="1" user="admin" when="">
<file mode="webfile" url="/vue-poc/app.xqm" />
</event>
<event id="2">
<file mode="webfile" url="/vue-poc/data/vue-poc/samples/ch4d1.xml" />
</event>
<event id="3">
<file mode="webfile" url="/vue-poc/static/app-gen.js" />
</event>
<event id="4">
<file mode="webfile" url="/vue-poc/static/app.html" />
</event>
<event id="5">
<file mode="webfile" url="/vue-poc/static/app.css" />
</event>
<event id="6">
<file mode="webfile" url="/vue-poc/logo.svg" />
</event>
<event id="7">
<file mode="webfile" url="/vue-poc/static/resources/semantic/sparql.rq" />
</event>
<event id="8">
<file mode="webfile" url="/vue-poc/static/resources/semantic/turtle.ttl" />
</event>
<event id="9">
<file mode="webfile" url="/vue-poc/static/resources/task.xsd" />
</event>
<event id="10">
<file mode="webfile" url="/vue-poc/static/resources/schematron/ark.sch" />
</event>
<event id="11">
<file mode="basexdb" url="/abide/abide.xml" />
</event>
<event id="12">
<collection mode="basexdb" url="/vue-poc" />
</event>
<history xmlns="urn:quodatum:vue-poc.history" next-id="6">
<hist:event xmlns:hist="urn:quodatum:vue-poc.history" id="ev-5" when="2020-10-20T22:36:42.7+01:00" user="admin">
<task task="log.enrich" url="file:///C:/Users/andy/basex.home/webapp/vue-poc/features/tasks/task/log.enrich.xq"/>
</hist:event>
<hist:event xmlns:hist="urn:quodatum:vue-poc.history" id="ev-4" when="2020-10-20T22:36:16.206+01:00" user="admin">
<task task="model" url="file:///C:/Users/andy/basex.home/webapp/vue-poc/features/tasks/model.build/tx-model.xq">
<param name="target">C:/Users/andy/git/vue-poc/src/vue-poc/models.gen.xqm</param>
<param name="efolder">C:/Users/andy/git/vue-poc/src/vue-poc/data/vue-poc/entities</param>
</task>
</hist:event>
<hist:event xmlns:hist="urn:quodatum:vue-poc.history" id="ev-3" when="2020-10-20T22:35:57.352+01:00" user="admin">
<task task="primes" url="file:///C:/Users/andy/basex.home/webapp/vue-poc/tasks/primes.xq">
<param name="MAX">2020</param>
</task>
</hist:event>
<hist:event xmlns:hist="urn:quodatum:vue-poc.history" id="2" when="2020-10-14T21:50:53.073+01:00" user="admin">
<task task="primes" url="file:///C:/Users/andy/basex.home/webapp/vue-poc/tasks/primes.xq">
<param name="MAX">1000</param>
</task>
</hist:event>
</history>

View file

@ -0,0 +1,38 @@
<tasks xmlns="https://github.com/Quodatum/task">
<task name="model" url="model.build/tx-model.xq">
<title>Generate model.gen.xqm</title>
<description> Generate <code>model.gen.xqm</code> from files in entity folder
</description>
</task>
<task name="import2" url="task/tx-dbimport2.xq">
<title>Import files from drive into a database</title>
<description>Load files into database</description>
</task>
<task name="log.enrich" url="task/log.enrich.xq">
<title>log enrich</title>
<description>Enrich log files in collection /logs database vue-poc
by adding @start and @end attributes where calculatable</description>
</task>
<task name="vuecompile">
<title>vue compile</title>
<description>compile</description>
</task>
<task name="xqdoca" url="file:///C:/Users/andy/git/xqdoca/src/main/xqdoca.xq">
<title>Create xqDoc</title>
<description>xquery documentation</description>
</task>
<task name="primes" url="../../tasks/primes.xq">
<title>Calculate primes</title>
<description>performance</description>
</task>
<task name="newapp" url="task/tx-newapp.xq">
<title>application template</title>
<description>application template</description>
<params>
<param name="appname">Name for new application</param>
</params>
</task>
</tasks>

View file

@ -52,7 +52,8 @@
<v-list-item><a href="https://material.io/tools/icons/?style=baseline"
target="new">icons (material)</a></v-list-item>
<v-list-item><a href="../api"
target="new">wadl</a></v-list-item>
</v-list>
</v-flex>
<v-flex xs6>

View file

@ -275,10 +275,6 @@ v-on:annotation="annotation"></vue-ace>
var r=type.mode
this.mode=r?r:"text"
},
onResize(){
var h=window.innerHeight
console.log("height:",h)
},
leaving(event) {
event.returnValue = "event seems to need to be set";
//debugger;

View file

@ -17,14 +17,13 @@ declare namespace c="http://www.w3.org/ns/xproc-step";
:)
declare
%rest:GET %rest:path("/vue-poc/api/edit")
%rest:query-param("url", "{$url}")
%rest:query-param("url", "{ $url }")
%rest:query-param("protocol", "{ $protocol }")
%rest:produces("application/json")
%output:method("json")
function vue-api:edit-get($url as xs:string)
function vue-api:edit-get($url as xs:string, $protocol as xs:string)
{
let $a:=analyze-string($url,"^\w*:")=>trace("PROTO")
let $protocol:=$a/fn:match/string()
let $protocol:=if ($protocol) then $protocol else "webfile"
let $reader := map{
"webfile": vue-api:get-webfile#1,
"xmldb": vue-api:get-basexdb#1

View file

@ -32,9 +32,15 @@
:search="q"
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td >AA: <router-link :to="'tasks/' + props.item.to" v-text="props.item.title"></router-link></td>
<td >{{ props.item.description }}</td>
<template v-slot:item.id="{ item }" >
<router-link :to="{path: '/tasks/' + item.task + '/run', query:{ id: item.id}}">
{{ item.id }}
</router-link>
</template>
<template v-slot:item.task="{ item }" >
<router-link :to="{path: '/tasks/' + item.task + '/run', query:{ id: item.id}}">
{{ item.task }}
</router-link>
</template>
<template slot="no-data">
<v-alert :value="true" icon="warning">
@ -56,17 +62,20 @@
loading: false,
q: null,
headers: [
{ text: 'Task', value: 'title' },
{ text: 'Description', value: 'description' },
{ text: 'Id', value: 'id' },
{ text: 'Task', value: 'task' },
{ text: 'Created', value: 'created' },
{ text: 'Summary', value: 'summary' },
{ text: 'Params', value: 'arity' }
]
}
},
methods:{
getTasks(){
this.loading= true;
HTTP.get("tasks")
HTTP.get("data/history.task")
.then(r=>{
this.items=r.data;
this.items=r.data.items;
this.loading= false;
})
}

View file

@ -19,7 +19,8 @@ declare
%perm:check('/vue-poc')
function vue-login:check-app() {
let $user := session:get('id')
let $_:=trace($user,"CHECK")
let $m:=``[path: `{ request:path() }`, user: `{ $user }`.]``
let $_:=trace($m,"CHECK: ")
return ()
};

View file

@ -46,15 +46,17 @@
<v-expansion-panels v-model="panel" multiple>
<v-expansion-panel expand >
<v-expansion-panel-header>
<v-layout>
<v-flex xs12>
<v-avatar><v-icon>{{ item.iconclass }}</v-icon></v-avatar>
<span class="font-weight-black">{{ item.name }}</span>
</v-expansion-panel-header>
<v-expansion-panel-content>
<v-layout>
<v-flex xs1>
<v-avatar color="teal" size="62"><v-icon x-large>{{ item.iconclass }}</v-icon></v-avatar>
</v-flex>
<v-flex xs11>
{{item.description}}
</v-flex>
</v-layout>
</v-expansion-panel-header>
<v-expansion-panel-content>
<pre v-if="xml"><code>{{ xml }}</code></pre>
</v-expansion-panel-content>
<v-expansion-panel>
@ -109,7 +111,7 @@
{text: "description", value: "description"},
{text: "xpath", value: "xpath"}
],
panel: [1,2]
panel: [0,1,2]
}
},
methods:{

View file

@ -3,9 +3,9 @@
<v-container fluid>
<qd-table :headers="headers" data-uri="data/namespace" entity="namespace" item-key="xmlns">
<template v-slot:item.xmlns="{ item }" >
<td ><router-link :to="{name:'namespace1', query:{ id: item.xmlns}}">
<router-link :to="{name:'namespace1', query:{ id: item.xmlns}}">
{{ item.xmlns }}
</router-link></td>
</router-link>
</template>
<template slot="no-results">

View file

@ -6,7 +6,7 @@ xquery version "3.1";
: @since oct 2019
:)
module namespace _ = 'quodatum.model.namespaces';
module namespace _ = 'urn:quodatum:model.namespaces';
import module namespace entity ='quodatum.models.generated' at "../../models.gen.xqm";

View file

@ -2,79 +2,59 @@
<template id="dicetest">
<v-container fluid>
<v-card>
<v-toolbar >
<v-toolbar-title>Dice entity list</v-toolbar-title>
<v-toolbar-title>Read json data for 1st page for entity</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn @click="reset()">Reset</v-btn>
</v-toolbar>
<v-card-text>
<p>Read json data for 1st page for entity.</p>
<v-flex xs12 sm6>
<v-combobox
v-model="entity"
:items="entities" item-text="name"
label="Select target" clearable open-on-clear>
<template v-slot:item="{ parent, item }">
<v-icon>{{ item.iconclass }}</v-icon>
{{ item.name }}
<qd-perfstats>
<v-app-bar slot="actions" slot-scope="{ add , repeats, reset, clip }">
<v-select v-model="selectedEntities" :items="entities" item-text="name"
return-object label="Entities to test"
multiple chips>
<template v-slot:prepend-item>
<v-list-item ripple @click="toggle" class="green lighten">
<v-list-item-action>
<v-icon :color="selectedEntities.length > 0 ? 'indigo darken-4' : ''">{{ icon }}</v-icon>
</v-list-item-action>
<v-list-item-content>
<v-list-item-title>Select All</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-divider class="mt-2"></v-divider>
</template>
</v-combobox>
</v-select>
<div v-if="selectedEntities.length">
<v-btn @click="setSel(add)">Set</v-btn>
<v-btn @click="repeats(false)">Repeat off</v-btn>
<v-btn @click="repeats(true)">Repeat on</v-btn>
<v-btn @click="reset()">Reset</v-btn>
<v-btn @click="clip()" icon title="copy to clipboard"><v-icon>content_copy</v-icon></v-btn>
</div>
</v-app-bar>
</v-flex>
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th v-for="col in columns " class="text-left">
{{ col.label }}
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{{ entity && entity.name }}
</td>
<td>
<v-btn @click="get()" :disabled="!entity" >
Read <v-icon right>compare_arrows</v-icon>
</v-btn>
</td>
<td>
<v-switch v-on:change="gchange" v-model="repeat.get"></v-switch>
<v-data-table slot="table" slot-scope="{ headers, items, run }" :hide-default-footer="true" :disable-pagination="true"
:headers="headers" :items="items" dense >
<template v-slot:item.id="{ item }" >
<v-btn @click="run(item.index)" :title="item.index">{{ item.id }}</v-btn>
</template>
<template v-slot:item.repeat="{ item }" >
<v-simple-checkbox v-model="item.repeat"></v-simple-checkbox>
</template>
</v-data-table>
</td>
<td>
<span >{{getValues.last}}</span>
</td>
<td>
<span >{{getValues.count}}</span>
</td>
<td>
<span >{{getValues.avg | round(2)}}</span>
</td>
<td>
<span >{{getValues.min}}</span>
</td>
<td>
<span >{{getValues.max}}</span>
</td>
<td>
<span >{{getValues.median}}</span>
</td>
</tr>
<div slot="response" slot-scope="{ data }">
<h3>Counter: <v-chip color="amber" text-color="white">counter</v-chip></h3>
<pre>{{ data | pretty}}</pre>
</div>
</qd-perfstats>
</tbody>
</template>
</v-simple-table>
<h3>Value: <v-chip color="amber" text-color="white">{{counter}}</v-chip></h3>
<pre>{{ result | pretty}}</pre>
</v-card-text>
</v-card>
</v-container>
@ -83,68 +63,61 @@
<script>{
data: function(){
return {
getValues: new perfStat(),
repeat: {get:false},
entity: null,
selectedEntities: [],
counter: 0,
result: null,
entities: null,
columns: [
{label:"Entity"},
{label:"Action"},
{label:"Repeat"},
{label:"Last"},
{label:"Count"},
{label:"Avg"},
{label:"min"},
{label:"max"},
{label:"Median"}
]
entities: [],
result: null
}
},
methods:{
get(){
var _start = performance.now();
console.log("entity:", this.entity)
HTTP.get(this.entity.datalink,axios_json)
.then(r=>{
var elapsed=Math.floor(performance.now() - _start);
this.counter++;
this.result=r.data;
Object.assign(this.getValues,this.getValues.log(elapsed))
this.$forceUpdate()
if(this.repeat.get){
this.get(); //does this leak??
}
})
},
gchange(v){
if(v)this.get()
},
reset(){
Object.assign(this.getValues,this.getValues.clear());
this.$forceUpdate()
},
getentities(){
HTTP.get("data/entity",axios_json)
.then(r=>{
console.log("entities: ",r.data);
//console.log("entities: ",r.data);
this.entities=r.data.items
})
},
setSel(add){
console.log("setSel",this.selectedEntities)
this.selectedEntities.forEach(item=>add(item.name,'GET',item.datalink))
},
toggle () {
this.$nextTick(() => {
if (this.all) {
this.selectedEntities = []
} else {
this.selectedEntities = this.entities.slice()
}
})
}
},
computed:{
all () {
return this.selectedEntities.length === this.entities.length
},
some () {
return this.selectedEntities.length > 0 && !this.all
},
icon () {
if (this.all) return 'mdi-close-box'
if (this.some) return 'mdi-minus-box'
return 'mdi-checkbox-blank-outline'
}
},
created(){
console.log("GET entities: ");
this.getentities()
},
beforeRouteLeave(to, from, next){
var on=this.repeat.get
var on=this.some // @TODO this.repeat.get
if (on) {
alert("running!") //<--undefined
return next(false)
return next()
} else {
return next()
}

View file

@ -5,93 +5,29 @@
<v-toolbar >
<v-toolbar-title>Simple response counter</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn @click="reset()">Reset</v-btn>
</v-toolbar>
<v-card-text>
<p>Read or increment a database value. This measures round trip times browser-database-browser.</p>
<h3>Value: <v-chip color="amber" text-color="white">{{counter}}</v-chip></h3>
<table class="v-table">
<thead>
<tr>
<th class="col-md-1">Action</th>
<th class="col-md-1">Repeat</th>
<th class="col-md-1 text-right">Last</th>
<th class="col-md-1 text-right">Count</th>
<th class="col-md-1 text-right">Avg</th>
<th class="col-md-1 text-right">min</th>
<th class="col-md-1 text-right">max</th>
<th class="col-md-1 text-right">Median</th>
</tr>
</thead>
<tbody>
<qd-perfstats :initial="initial">
<div slot="actions" slot-scope="{ add , repeats, reset }">
<v-btn @click="repeats(false)">Repeat off</v-btn>
<v-btn @click="reset()">Reset</v-btn>
</div>
<tr>
<td>
<v-btn @click="get()" >
Read Db<v-icon right>compare_arrows</v-icon>
</v-btn>
<v-data-table slot="table" slot-scope="{ headers, items, run }" :hide-default-footer="true"
:headers="headers" :items="items" >
<template v-slot:item.id="{ item }" >
<v-btn @click="run(item.index)">{{ item.id }}</v-btn>
</template>
<template v-slot:item.repeat="{ item }" >
<v-switch v-model="item.repeat"></v-switch>
</template>
</v-data-table>
</td>
<td>
<v-switch v-on:change="gchange" v-model="repeat.get"></v-switch>
</td>
<td>
<p class="text-right">{{getValues.last}}</p>
</td>
<td>
<p class="text-right" >{{getValues.count}}</p>
</td>
<td>
<p class="text-right" >{{getValues.avg | round(2)}}</p>
</td>
<td>
<p class="text-right" >{{getValues.min}}</p>
</td>
<td>
<p class="text-right" >{{getValues.max}}</p>
</td>
<td>
<p class="text-right" >{{getValues.median}}</p>
</td>
</tr>
<tr>
<td>
<v-btn @click="update()" >
Write Db<v-icon right>compare_arrows</v-icon>
</v-btn>
</td>
<td>
<v-switch v-on:change="pchange" v-model="repeat.post"></v-switch>
</td>
<td class="text-right">
<span >{{postValues.last}}</span>
</td>
<td class="text-right">
<span >{{postValues.count}}</span>
</td >
<td class="text-right">
<span >{{postValues.avg | round(2)}}</span>
</td>
<td class="text-right">
<span >{{postValues.min}}</span>
</td>
<td class="text-right">
<span >{{postValues.max}}</span>
</td>
<td class="text-right">
<span >{{postValues.median}}</span>
</td>
</tr>
</tbody>
</table>
</qd-perfstats>
</v-card-text>
</v-card>
@ -102,67 +38,15 @@
export default {
data: function(){
return {
nothingValues: new perfStat(),
staticValues: new perfStat(),
getValues: new perfStat(),
postValues: new perfStat(),
repeat: {get: false, post: false, staticx: false, nothing: false},
counter: "(unread)"
}
},
methods:{
update () {
var _start = performance.now();
HTTP.post("ping",axios_json)
.then(r=>{
var elapsed=Math.floor(performance.now() - _start);
this.counter=r.data
Object.assign(this.postValues,this.postValues.log(elapsed))
if(this.repeat.post){
this.update(); //does this leak??
}
})
},
get(){
var _start = performance.now();
HTTP.get("ping",axios_json)
.then(r=>{
var elapsed=Math.floor(performance.now() - _start);
this.counter=r.data
Object.assign(this.getValues,this.getValues.log(elapsed))
this.$forceUpdate()
if(this.repeat.get){
this.get(); //does this leak??
}
})
},
nothing () {
var _start = performance.now();
HTTP.post("nothing",axios_json)
.then(r=>{
var elapsed=Math.floor(performance.now() - _start);
this.counter=r.data
Object.assign(this.nothingValues,this.nothingValues.log(elapsed))
if(this.repeat.nothing){
this.nothing(); //does this leak??
}
})
},
gchange(v){
if(v)this.get()
},
pchange(v){
if(v)this.update()
},
reset(){
Object.assign(this.getValues,this.getValues.clear());
Object.assign(this.postValues,this.postValues.clear());
this.$forceUpdate()
}
initial:[
{ id: 'WRITE DB', method: 'POST', url: 'performance/ping'},
{ id: 'READ DB', method: 'GET', url: 'performance/ping'},
{ id: 'NO DB', method: 'GET', url: 'performance/nodb'}
],
counter: 0}
},
beforeRouteLeave(to, from, next){
var on=this.repeat.get || this.repeat.post
var on=false // @TODO this.repeat.get || this.repeat.post
if (on) {
alert("running!") //<--undefined

View file

@ -10,7 +10,7 @@ declare %basex:lazy variable $ping:state as element(state):=db:open($ping:db,"/s
: incr counter
:)
declare %updating
%rest:POST %rest:path("/vue-poc/api/ping")
%rest:POST %rest:path("/vue-poc/api/performance/ping")
%output:method("text")
function ping:dopost()
{(
@ -23,7 +23,7 @@ function ping:dopost()
:)
declare
%output:method("text")
%rest:GET %rest:path("/vue-poc/api/ping")
%rest:GET %rest:path("/vue-poc/api/performance/ping")
function ping:dostate()
{
$ping:state/ping
@ -34,7 +34,7 @@ function ping:dostate()
:)
declare
%output:method("text")
%rest:GET %rest:path("/vue-poc/api/nodb")
%rest:GET %rest:path("/vue-poc/api/performance/nodb")
function ping:nodb()
{
"ok"

View file

@ -14,6 +14,20 @@
<v-card-text>
<qd-table :headers="headers" data-uri="server/basexsettings2" item-key="name" >
<template v-slot:item.name="{ item }" >
<v-chip>{{ item.name }}</v-chip>
</template>
<template v-slot:item.changed="{ item }" >
<v-simple-checkbox v-model="item.changed" disabled></v-simple-checkbox>
</template>
<template v-slot:item.description="{ item }" >
<qd-link :href="'http://docs.basex.org/wiki/Options#' + item.name.toUpperCase()">BaseX doc</qd-link>
</template>
</qd-table>
<v-data-table
:headers="headers"
:items="filtered"
@ -46,7 +60,7 @@
{text: "current", value: "current"},
{text: "changed", value: "changed"},
{text: "default", value: "default"},
{text: "description"}
{text: "description", value: "description"}
],
pagination: {
descending: false,
@ -66,6 +80,13 @@
this.items=r.data
})
},
customFilter(value, search, item) {
return item.changed == this.changed &&
value != null &&
search != null &&
typeof value === 'string' &&
value.toString().indexOf(search) !== -1
}
},
computed: {

View file

@ -28,3 +28,27 @@ return <_ type="object">
</json>
};
declare
%rest:GET %rest:path("/vue-poc/api/server/basexsettings2")
%rest:produces("application/json")
%output:method("json")
function set:values2()
{
let $defaults:=doc("basexsettings-921.xml")//*[not(*)]
let $dm:=map:merge($defaults!map:entry(name(.),string(.)))
let $settings:=db:system()//*[not(*)]
let $sm:=map:merge($settings!map:entry(name(.),string(.)))
let $names:=distinct-values((map:keys( $dm),map:keys($sm)))=>sort()
return <json type="object">
<items type="array">
{for $name in $names
let $change:=$dm($name) ne $sm($name)
return <_ type="object">
<name>{$name}</name>
<default>{$dm($name)}</default>
<current>{$sm($name)}</current>
<changed type="boolean">{ if ($change) then $change else false() }</changed>
</_>}
</items>
</json>
};

View file

@ -7,7 +7,10 @@
<router-link :to="{name: 'repo1', query: {id: item.name }}">{{ item.name }} </router-link>
</template>
<template v-slot:actions>
<v-btn>action here</v-btn>
<v-list-item @click="remove">
<v-list-item-avatar><v-icon>delete</v-icon></v-list-item-avatar>
<v-list-item-title>Delete</v-list-item-title>
</v-list-item>
</template>
</qd-table>
</v-container>
@ -27,6 +30,11 @@
]
}
},
methods:{
remove(){
alert("Not yet")
}
},
watch: {
"query":{
handler:function(vnew,vold){

View file

@ -35,11 +35,15 @@ as map(*){{
(:~
: generate xquery for to return field value in the format: "name":function($_){}
:)
declare function bf:accessfn($f as element(ent:field)) as xs:string
declare function bf:accessfn(
$f as element(ent:field)
) as xs:string
{
let $type:=$f/@type/fn:string()
return <field>
"{$f/@name/fn:string()}": function($_ as element()) as {$type} {{$_/{$f/ent:xpath } }}</field>
let $name:=$f/@name/fn:string()
let $resulttype:=$f/@type/fn:string()
let $srctype:=$f/ancestor::ent:entity/ent:data/@type/fn:string()
return ``[
'`{ $name }`': function($_ as `{ $srctype }`) as `{$resulttype}` { $_! `{ $f/ent:xpath }` }]``
};
declare function bf:generate($e as element(ent:entity)) as xs:string
@ -78,10 +82,9 @@ declare function bf:generate($e as element(ent:entity)) as xs:string
declare function bf:entities($path as xs:string)
as element(ent:entity)*
{
let $_:=fn:trace($path,"DD")
let $p:=fn:resolve-uri($path) || "/"
return for $f in file:list($p,fn:true())
where not(ends-with(trace($f),file:dir-separator()))
return for $f in file:list($p,fn:true(),"*.xml")
where not(ends-with($f,file:dir-separator()))
order by $f
return fn:doc(fn:concat($p,$f))/ent:entity
};
@ -111,6 +114,7 @@ as xs:string
let $opt:=fn:contains($type,"?")
let $repeat:=fn:contains($type,"*")
let $json-type:=bf:json-type($type)
let $srctype:=$f/ancestor::ent:entity/ent:data/@type/fn:string()
let $mult:=if($repeat) then "*" else "?"
let $at:=if($json-type ne "string")
@ -119,7 +123,7 @@ as xs:string
(: generate json xml :)
let $simple:=function() as xs:string{
<field>(: {$type} :)
fn:data($_/{$f/ent:xpath })!element {$name} {{ {$at} .}}
fn:data($_!{ $f/ent:xpath })!element { $name } {{ { $at } .}}
</field>
}
let $array:=function() as xs:string{
@ -138,7 +142,7 @@ as xs:string
}
return <field>
"{$name}": function($_ as element()) as element({$name}){$mult} {{
"{$name}": function($_ as { $srctype }) as element({ $name }){ $mult } {{
{if($repeat)then
$array()
else if($type="element()") then

View file

@ -1,38 +0,0 @@
(:~
: Update `generated/models.xqm` from files in `data/models`
: using file:///C:/Users/andy/workspace/app-doc/src/doc/data/doc/models
: $efolder:="file:///C:/Users/andy/workspace/app-doc/src/doc/data/doc/models"
: $target:="file:///C:/Users/andy/workspace/app-doc/src/doc/generated/models.xqm"
:)
module namespace vue-api = 'quodatum:vue.api';
import module namespace bf = 'quodatum.tools.buildfields' at "entity-gen.xqm";
import module namespace query-a = 'vue-poc/query-a' at "../../../lib/query-a.xqm";
declare variable $vue-api:query:="tx-model.xq";
(:~
: Returns a file content.
:)
declare
%rest:POST %rest:path("/vue-poc/api/tasks/model")
%rest:produces("application/json")
%output:method("json")
%updating
function vue-api:model( )
{
let $u:=resolve-uri($vue-api:query)
return query-a:update($u,query-a:params($u))
};
(:~
: model settings.
:)
declare
%rest:GET %rest:path("/vue-poc/api/tasks/model")
%rest:produces("application/json")
%output:method("json")
function vue-api:settings()
{
let $xq:=resolve-uri($vue-api:query)
return query-a:inspect($xq)
};

View file

@ -23,7 +23,7 @@ let $config:='import module namespace cfg = "quodatum:media.image.configure" at
let $src:=bf:module(bf:entities($efolder),$config)
return (
prof:variables(),
(: prof:variables(), :)
file:write-text($target,$src),
update:output(<json type="object"><msg>Updated: {$target}</msg></json>)
)

View file

@ -30,7 +30,7 @@
<v-card-text>
<v-container fluid>
<vp-paramform v-if="!loading" ref="params" :endpoint="'tasks/'+task"></vp-paramform>
<vp-paramform v-if="!loading" ref="params" :endpoint="endpoint"></vp-paramform>
</v-container>
</v-card-text>
<v-snackbar v-model="snackbar.show"
@ -67,7 +67,7 @@
this.loading= false
this.id=r.data.id;
this.snackbar= {show:true,
msg: r.data && r.data.msg,
msg: r.result,
context:"success"
};
console.log(r)
@ -79,16 +79,14 @@
});
}
},
computed:{
endpoint(){
return 'tasks/'+this.task + (this.id? "?id=" + this.id:'')
}
},
watch:{
id(v){
this.$router.push({ query: { id: this.id }})
},
$route(vnew,vold){
console.log("ROUTE",vnew,vold)
var id=this.$route.query.id
this.id=id?id:null;
if(vnew.query.url != vold.query.url) alert("gg")
}
},

View file

@ -4,6 +4,8 @@
module namespace vue-rest = 'quodatum:vue.tasks';
import module namespace query-a = 'vue-poc/query-a' at "../../lib/query-a.xqm";
import module namespace hlog = 'quodatum.data.history' at '../../lib/history.xqm';
declare namespace hist="urn:quodatum:vue-poc.history";
(:~
: list tasks
:)
@ -30,14 +32,23 @@ function vue-rest:tasks()
:)
declare
%rest:GET %rest:path("/vue-poc/api/tasks/{$task}")
%rest:query-param("id", "{ $id }")
%rest:produces("application/json")
%output:method("json")
function vue-rest:task($task)
function vue-rest:task($task,$id)
{
let $taskdef:=doc("taskdef.xml")/tasks/task[@name=$task]
let $url:=resolve-uri($taskdef/@url)
let $info:=query-a:inspect($url)
return $info
let $h:=if($id) then hlog:get($id) else ()
return if($h) then (: use old values :)
let $v:=<values type="object">{
$h/hist:task/hist:param!element{@name}{string(.)}
}</values> =>trace("O/P")
return $info transform with {replace node ./values with $v}
else
$info
};
(:~
@ -51,14 +62,20 @@ declare
function vue-rest:runtask($task)
{
let $taskdef:=doc("taskdef.xml")/tasks/task[@name=$task]
let $url:=resolve-uri($taskdef/@url)
let $url:=resolve-uri($taskdef/@url)=>trace("RUNTASK")
let $params:=query-a:params($url)
let $log:=<task task="{ $task }" url="{ $url }">
{ map:keys($params)!<param name="{.}">{map:get($params,.)}</param> }
</task>
return (
query-a:run($url, $params, map{}),
hlog:save($log)
hlog:save($log),
let $a:=update:cache(true())
let $r:=<json type="object">
<result>{$a[1]}</result>
<id>{$a[2]}</id>
</json>
return update:output($r)
)
};

View file

@ -1,14 +1,9 @@
<tasks>
<tasks >
<task name="model" url="model.build/tx-model.xq">
<title>Generate model.gen.xqm</title>
<description> Generate <code>model.gen.xqm</code> from files in entity folder
</description>
</task>
<task name="model2" url="model.build/tx-model.xq">
<title>Generate model.gen.xqm</title>
<description> Generate <code>model.gen.xqm</code> from files in entity folder
</description>
</task>
<task name="import2" url="task/tx-dbimport2.xq">
<title>Import files from drive into a database</title>
<description>Load files into database</description>

View file

@ -7,15 +7,23 @@ declare namespace hist="urn:quodatum:vue-poc.history";
declare variable $hlog:doc as element(hist:history):=db:open("vue-poc","/history.xml")/hist:history;
(:~
wrap $item in <hist:event id=".." when=".." > node and insert in /history.xml in database vue-poc
: write history event /history.xml in database vue-poc
: wrap $item in <hist:event id=".." when=".." > node and insert as first
:)
declare
%updating
function hlog:save($item as element(*))
{
let $id:=$hlog:doc/@next-id/string(.)
let $n:=<hist:event id="{$id}" when="{fn:current-dateTime()}" user="admin">{$item}</hist:event>
let $id:=$hlog:doc/@next-id/string()=>trace("Saving: ")
let $n:=<hist:event id="ev-{$id}" when="{fn:current-dateTime()}" user="admin">{$item}</hist:event>
return (insert node $n as first into $hlog:doc,
replace value of node $hlog:doc/@next-id with number($id)+1
replace value of node $hlog:doc/@next-id with number($id)+1,
update:output("ev-" || $id)
)
};
(:~ get history record for id :)
declare function hlog:get($id as xs:string) as element(hist:event)?
{
$hlog:doc/hist:event[@id=$id]
};

View file

@ -9,6 +9,7 @@
module namespace query-a = 'vue-poc/query-a';
import module namespace request = "http://exquery.org/ns/request";
import module namespace xquery = "http://basex.org/modules/xquery";
(:~
@ -78,7 +79,7 @@ function query-a:run($query as xs:string,
$options as map(*)
)
{
let $query := xs:anyURI($query)
let $query := xs:anyURI($query) =>trace("query-a:run")
let $updating:=xquery:parse-uri($query)/@updating/boolean(.)
return if($updating) then
xquery:eval-update($query, $bindings, $options)

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
"description": "App framework experiments, Frontend vuetify, backend: basex",
"dependencies": {
"ace-builds": "1.4.12",
"vuetify": "2.3.13",
"vuetify": "2.3.14",
"vue": "2.6.11",
"vuex": "3.1.0",
"vue-router": "3.1.6",

View file

@ -1,2 +1,3 @@
Vue-poc
Min BaseX: 9.3+ https://docs.basex.org/wiki/Update_Module#Changelog

View file

@ -128,7 +128,8 @@ const router = new VueRouter({
},
{ path: '/history', component: { template: '<router-view/>' }
,children: [
{ path: 'files', component: Filehistory, meta:{title: "File History"} }
{ path: 'files', component: Filehistory, meta:{title: "File History"} },
{ path: 'tasks', name: "taskhistory", component: Taskhistory, meta:{title: "Task History"} }
]
},
{ path: '/labs', component: { template: '<router-view/>' }
@ -158,7 +159,6 @@ const router = new VueRouter({
{ path: '/tasks', component: { template: '<router-view/>' } , children:[
{ path: '', component: Tasks, meta:{title:"Runnable tasks"} },
{ path: 'model', component: Model, meta:{title:"build model"} },
{ path: 'vuecompile', component: Vuecompile, meta:{title:"vue compile"} },
{ path: ':task', props: true, component: { template: '<router-view/>' },
children:[

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic"/>
<link rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons"/>
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" >
<link rel="stylesheet" href="//unpkg.com/vuetify@2.3.13/dist/vuetify.min.css" type="text/css"/>
<link rel="stylesheet" href="//unpkg.com/vuetify@2.3.14/dist/vuetify.min.css" type="text/css"/>
<link rel="stylesheet" href="//unpkg.com/@riophae/vue-treeselect@0.0.29/dist/vue-treeselect.min.css"/>
<link rel="stylesheet" href="/vue-poc/ui/prism/prism.css" type="text/css"/>
<link rel="stylesheet" href="//unpkg.com/leaflet@1.6.0/dist/leaflet.css"/>
@ -39,7 +39,7 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.js" crossorigin="anonymous"></script>
<script src="//unpkg.com/vuex@3.1.0/dist/vuex.js" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/qs/6.4.0/qs.js" crossorigin="anonymous" ></script>
<script src="//unpkg.com/vuetify@2.3.13/dist/vuetify.min.js" crossorigin="anonymous"></script>
<script src="//unpkg.com/vuetify@2.3.14/dist/vuetify.min.js" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ext-language_tools.js" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ext-linking.js" crossorigin="anonymous" charset="utf-8"></script>
@ -65,7 +65,6 @@
<script src="//unpkg.com/vue-native-websocket@2.0.8/dist/build.js" crossorigin="anonymous"></script>
<script src="/vue-poc/ui/svg/d3-svg.js"></script>
<script src="/vue-poc/ui/state.js"></script>
<script src="/vue-poc/ui/perf-stat.js"></script>
<script src="/vue-poc/ui/app-gen.js"></script>
<script>

View file

@ -1,40 +0,0 @@
// performance monitoring. of value stream
// var t=new perfStat()
// t.log(newValue)
// stores max min etc
function perfStat() {
this.data={count:0,max:null,min:null,total:0,median:0,last:null,avg:null};
// add a value return updated stats
this.log=function(val){
var data=this.data
data.last=val;
data.total+=val;
data.count+=1;
if(data.count==1){
data.max=val;
data.min=val;
data.median=val;
}else{
if(val<data.min)data.min=val;
if(val>data.max)data.max=val;
};
//https://jeremykun.com/2012/06/14/streaming-median/
if (data.median > val)
data.median-= 1
else if( data.median < val)
data.median += 1;
data.avg=data.total / data.count;
// console.log("stats",data);
return data;
};
// clear stats
this.clear=function(){
this.data={count:0,max:null,min:null,total:0,median:0,last:null,avg:null};
return this.data
};
// return values
this.values=function(){
return this.data;
};
}