Skip to content
Snippets Groups Projects
Verified Commit a595fe81 authored by BARBIER Marc's avatar BARBIER Marc
Browse files

big fixes ad added support for required items

parent d4dd5279
No related branches found
No related tags found
No related merge requests found
......@@ -4,6 +4,8 @@ import TableComponent from '@/components/TableComponent/TableComponent.vue'
import Multiselect from '@vueform/multiselect'
import switchToNewestProject from '@/switchToNewestProject'
//TODO: add optionals for other algorithms
export default defineComponent({
name: 'TransformView',
data() {
......@@ -14,7 +16,13 @@ export default defineComponent({
selectedSequenceColumns: null as string[] | null,
patternData: null as { rows: string[][], totalSize: number, rowsStartingFrom1: string[][] } | null,
selectedPatternFPOF: [] as string[],
itemsetSupport: null as number | null,
sequentialSupport: null as number | null,
dictionary: {} as { [ key:string ]: Set<number> },
selectedPatternPBAD: [] as string[],
requiredItems: "",
newRequiredItems: [] as string[],
currentColumn: null as string | null,
arePatternsLoading: false,
currentPattern: null as string | null,
selectedAlgorithm: 'PrefixSpan'
......@@ -35,6 +43,20 @@ export default defineComponent({
hasWindows() {
return localStorage.getItem('hasWindows') === 'true'
},
nbWindows(): number {
//TODO: create a route just for that instead of doing string manipulation
const windowString = this.fileItem?.stackOperations.find((val) => val.includes('make-windows'))
const windowData = windowString?.split('make-windows ')[1]
const keyValues = windowData?.split(',').map(e => e.split('='))
const nbElement = this.fileItem?.noRows
if(!keyValues || !nbElement) return 0
const windowIncrements = parseInt(keyValues[1][1])
return Math.ceil(nbElement / windowIncrements)
}
},
async mounted() {
if(!localStorage.getItem('datasetId') || !this.hasWindows) {
......@@ -52,6 +74,7 @@ export default defineComponent({
this.columns = data.flatMap(val => Object.keys(val))
this.fileItem = await (await fetch(`http://localhost:8080/rest/metadata/fileitem?id=${localStorage.getItem('datasetId')}`)).json()
this.dictionary = await(await fetch(`http://localhost:8080/rest/mining/dictionary?id=${localStorage.getItem('datasetId')}`)).json()
},
components: {
HeaderComponent,
......@@ -168,7 +191,7 @@ export default defineComponent({
const form = new FormData(domForm)
const threshold = form.get('threshold')?.toString()
if(!threshold || isNaN(parseFloat(threshold)) || parseFloat(threshold) <= 0 || parseFloat(threshold) > 1) {
if(!threshold || isNaN(parseFloat(threshold)) || parseFloat(threshold) < 0 || parseFloat(threshold) > 1) {
const input = domForm.querySelector('input')
input?.classList.add('is-invalid')
return
......@@ -247,6 +270,55 @@ export default defineComponent({
this.$toast.error('An error occurred, check the server logs for more details')
}
}
},
async filterMinMaxSupport(e: Event) {
e.preventDefault()
const currentPattern = this.currentPattern;
if(!currentPattern) {
this.$toast.error('Please select a pattern')
return
}
//@ts-expect-error we know the target is a form
const form = new FormData(e.target)
form.append('id', '' + localStorage.getItem('datasetId'))
form.append('patternFilename', currentPattern)
const response = await fetch('http://localhost:8080/rest/mining/min-max-filter-support', { method: 'POST', body: form })
if(response.status === 200) {
//this.$router.go(0)
} else {
this.$toast.error(await response.text())
}
},
addRequiredItems(e: Event) {
e.preventDefault()
const idsToIgnore: string[] = []
for(const pairs of this.requiredItems.split(',')) {
const [column, val] = pairs.split('=');
if(column !== this.currentColumn) continue
for(const item of this.newRequiredItems) {
if(item === val) idsToIgnore.push(item)
}
}
for(const item of this.newRequiredItems) {
if(!idsToIgnore.includes(item)) {
if(this.requiredItems)
this.requiredItems += `,${this.currentColumn}=${item}`
else
this.requiredItems = `${this.currentColumn}=${item}`
}
}
this.currentColumn = ""
this.newRequiredItems = []
}
},
})
......@@ -57,7 +57,8 @@
<form @submit="runSetMining">
<Multiselect mode="tags" :close-on-select="false" :options="columns" v-model="selectedPatternColumns"></Multiselect>
<label class="form-label" for="itemsetSupport">Support</label>
<input class="form-control" type="text" name="support" id="itemsetSupport" placeholder="support %">
<input class="form-control" type="text" name="support" id="itemsetSupport" placeholder="support %" v-model="itemsetSupport">
<div class="form-text" v-if="itemsetSupport">Support >= {{Math.floor(nbWindows / itemsetSupport)}}</div>
<label for="miningAlgorithm">Algorithm</label>
<input type="hidden" name="isItemset" :value="true">
<select class="form-control" name="algorithm" id="miningAlgorithm">
......@@ -82,8 +83,9 @@
<div class="accordion-body">
<form @submit="runSequenceMining">
<Multiselect mode="tags" :close-on-select="false" :options="columns" v-model="selectedSequenceColumns"></Multiselect>
<label class="form-label" for="itemsetSupport">Support</label>
<input class="form-control" type="text" name="support" id="itemsetSupport" placeholder="support %">
<label class="form-label" for="sequentialSupport">Support</label>
<input class="form-control" type="text" name="support" id="sequentialSupport" placeholder="support %" v-model="sequentialSupport">
<div class="form-text" v-if="sequentialSupport">Support >= {{Math.floor(nbWindows / sequentialSupport)}}</div>
<input type="hidden" name="isItemset" :value="false">
<label for="patternMiningAlgorithm">Algorithm</label>
<select class="form-control" name="algorithm" id="patternMiningAlgorithm" v-model="selectedAlgorithm">
......@@ -99,7 +101,25 @@
<label for="MaxPatternLength">Max pattern length</label>
<input type="text" class="form-control" name="MaxPatternLength" id="MaxPatternLength">
<label for="RequiredItems">Required items</label>
<input type="text" class="form-control" name="Required Items" id="RequiredItems">
<div class="container">
<input type="text" class="form-control" name="Required Items" id="RequiredItems" v-model="requiredItems" readonly>
<label>Columns</label>
<Multiselect
:options="selectedSequenceColumns"
v-model="currentColumn"
/>
<div v-if="currentColumn">
<label>Values</label>
<Multiselect
:searchable="true"
mode="tags"
:options="dictionary[currentColumn]"
v-model="newRequiredItems"
/>
</div>
<Button class="btn btn-primary" @click="addRequiredItems">Add</Button>
</div>
<br>
<label for="MaxGap">Max gap</label>
<input type="text" class="form-control" name="Max gap" id="MaxGap">
</div>
......@@ -131,7 +151,7 @@
<hr>
<form @submit="filterSupport">
<label class="form-label">Filter patterns on support</label>
<label class="form-label">Filter top supports</label>
<input type="text" class="form-control" name="topk" placeholder="Top-k">
<span class="form-text"> Top-k >= 0</span>
<br>
......@@ -167,6 +187,18 @@
<br>
<input type="submit" class="btn btn-primary" value="Run">
</form>
<hr>
<form @submit="filterMinMaxSupport">
<label class="form-label">Filter patterns on support</label>
<div class="input-group mb-3">
<input type="text" class="form-control" name="minfreq" placeholder="Min length">
<label class="input-group-text">-</label>
<input type="text" class="form-control" name="maxfreq" placeholder="Max length">
</div>
<input type="submit" class="btn btn-primary" value="Run">
</form>
</div>
</div>
</div>
......
......@@ -10,6 +10,7 @@ export default defineComponent({
currentProject: null as string | null,
newProjectName: "",
pageLength: 10,
windowWidth: 10 as number,
windows: false
}
},
......@@ -19,6 +20,13 @@ export default defineComponent({
},
hasWindows() {
return localStorage.getItem('hasWindows') === 'true'
},
nbElements() {
const totalSize = this.dataset?.totalSize
if(this.windows && totalSize) {
return Math.ceil(totalSize / this.windowWidth)
}
return totalSize
}
},
components: {
......@@ -31,12 +39,26 @@ export default defineComponent({
}
//on ne veut que le nombre de page
this.dataset = await this.fetchDataset(0,0)
const stats: Stats[] = await (await (await fetch(`http://localhost:8080/rest/metadata/attribute-and-statistics?id=${localStorage.getItem('datasetId')}` )).json())
if(this.hasWindows) {
const windowsAttributes = stats.find(stat => stat.attribute === 'Window')
if(windowsAttributes) {
this.windowWidth = parseInt(windowsAttributes['support-first-value'].split(':')[1]) - 1
}
}
this.dataset = await this.fetchDataset(0,0)
},
methods: {
async fetchDataset(from: number, to: number): Promise<Table> {
const id = localStorage.getItem('datasetId')
if(id) {
return ( await fetch(`http://localhost:8080/rest/load-data?id=${id}&windowed=${this.windows}&pagination=${from}-${to}`)).json()
if(this.windows)
return ( await fetch(`http://localhost:8080/rest/load-data?id=${id}&windowed=${this.windows}&pagination=${from * this.windowWidth}-${to * this.windowWidth}`)).json()
else
return ( await fetch(`http://localhost:8080/rest/load-data?id=${id}&windowed=${this.windows}&pagination=${from}-${to}`)).json()
} else {
this.$router.push('/')
//this error is thrown but will never have any impact
......
......@@ -15,7 +15,7 @@
<TableComponent
:dataCallback="fetchPage"
:pageLength="pageLength"
:nbElements="dataset?.totalSize"
:nbElements="nbElements"
v-bind:key="windows"
></TableComponent>
<template v-if="hasWindows">
......
......@@ -8,31 +8,6 @@ import switchToNewestProject from '@/switchToNewestProject'
ChartJS.register(Tooltip, BarElement, CategoryScale, LinearScale)
// most of them are string representing numbers wich is dumb
interface Stats {
max:string, //number
min: string //number,
std: string //number
type: string
mean: string //number,
IQ_01: string //number,
IQ_99: string //number
noValues: string //number,
histogram?:string //number list separated by a ,
noNotNull: string //number,
attribute: string
noDistinct: string //number,
't-distribution':string, //number list separated by a ,
'histogram-dens':string, //number list separated by a ,
'histogram-labels':string, //number list separated by a ,
'support-first-value': string //number : number
'histogram-dens-labels':string, //number list separated by a ,
'quantiles-IQ099-IQ100':string, //number list separated by a ,
'quantiles-IQ000-IQ010':string, //number list separated by a ,
't-distribution-expected':string, //number list separated by a ,
}
export type { Stats }
export default defineComponent({
name: 'TransformView',
data() {
......
......@@ -33,15 +33,15 @@
Mean: {{ parseFloat(stat.mean).toFixed(2) }}
</template>
<template v-if="stat.max">
max: {{ parseFloat(stat.max).toFixed(2) }}
Max: {{ parseFloat(stat.max).toFixed(2) }}
</template>
<template v-if="stat.min">
min: {{ parseFloat(stat.min).toFixed(2) }}
Min: {{ parseFloat(stat.min).toFixed(2) }}
</template>
<template v-if="stat.std">
std: {{ parseFloat(stat.std).toFixed(2) }}
Std: {{ parseFloat(stat.std).toFixed(2) }}
</template>
type: {{ stat.type }}
Type: {{ stat.type }}
</div>
</button>
</h2>
......
......@@ -56,3 +56,27 @@ interface Project {
items: Item[],
fileItems: Item[]
}
// most of them are string representing numbers wich is dumb
interface Stats {
max:string, //number
min: string //number,
std: string //number
type: string
mean: string //number,
IQ_01: string //number,
IQ_99: string //number
noValues: string //number,
histogram?:string //number list separated by a ,
noNotNull: string //number,
attribute: string
noDistinct: string //number,
't-distribution':string, //number list separated by a ,
'histogram-dens':string, //number list separated by a ,
'histogram-labels':string, //number list separated by a ,
'support-first-value': string //number : number
'histogram-dens-labels':string, //number list separated by a ,
'quantiles-IQ099-IQ100':string, //number list separated by a ,
'quantiles-IQ000-IQ010':string, //number list separated by a ,
't-distribution-expected':string, //number list separated by a ,
}
......@@ -3,13 +3,15 @@ export default async function switchToNewestProject(self: { $router: { go: (page
const datasetId = localStorage.getItem('datasetId')
if(!datasetId) return
const projects: Project[] = await(await fetch('http://localhost:8080/rest/projects')).json()
console.log(projects)
for(const project of projects) {
if(project.fileItems.find(e => e.id === datasetId)) {
const newDataset = project.fileItems.shift()
const dataset = project.fileItems.find(e => e.id === datasetId)
if(dataset) {
const newDataset = project.fileItems.filter(e => e.logicalName === dataset.logicalName).shift()
if(newDataset?.id) {
console.log('switching to : ' + newDataset.id)
localStorage.setItem('datasetId', encodeURIComponent(newDataset.id) || '')
localStorage.setItem('datasetId', newDataset.id)
} else {
localStorage.removeItem('datasetId')
}
......
......@@ -7,7 +7,9 @@
"@/*": ["./src/*"]
},
"lib": ["ES2019","DOM"],
"allowJs": true
"allowJs": true,
// since we are using VueJs this line is just here to tell typescript to stop warning us about js files self overwriting
"outDir": "/tmp"
},
"references": [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment