import { LitElementWw } from '@webwriter/lit' import { CSSResult, TemplateResult, css, html, nothing } from 'lit' import { customElement, property, query, state } from 'lit/decorators.js' import { consume } from '@lit/context' import { globalStyles } from '@/global_styles' import type { DataSet } from '@/types/data_set' import { availableDataSetsContext } from '@/contexts/available_data_sets_context' import { SlChangeEvent, SlDialog, SlButton, SlInput, SlTextarea, SlTooltip, SlRadioGroup, SlRadioButton, } from '@shoelace-style/shoelace' import { serialize } from '@shoelace-style/shoelace/dist/utilities/form.js' import { AlertUtils } from '@/utils/alert_utils' import IconQuestionCircle from 'bootstrap-icons/icons/question-circle.svg' import IconArrowLeftCircle from 'bootstrap-icons/icons/arrow-left-circle.svg' import IconArrowRightCircle from 'bootstrap-icons/icons/arrow-right-circle.svg' import { CCard } from '../reusables/c-card' import { msg } from '@lit/localize' export class CreateDataSetDialog extends LitElementWw { static scopedElements = { 'sl-dialog': SlDialog, 'sl-textarea': SlTextarea, 'sl-tooltip': SlTooltip, 'sl-input': SlInput, 'c-card': CCard, 'sl-button': SlButton, 'sl-radio-group': SlRadioGroup, 'sl-radio-button': SlRadioButton, } @consume({ context: availableDataSetsContext, subscribe: true }) accessor availableDataSets: DataSet[] private emptyConfig: DataSet = { name: '', description: '', type: 'regression', featureDescs: [{ key: '', description: '' }], labelDesc: { key: '', description: '', classes: [ { id: 0, description: '' }, { id: 1, description: '' }, ], }, data: [], } @property() accessor config: DataSet = ( JSON.parse(JSON.stringify(this.emptyConfig)) ) @state() accessor step: number = 1 @query('sl-dialog') accessor _dialog: SlDialog @query('.dialog-form') accessor _dialogForm: HTMLFormElement // LIFECYCLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - async connectedCallback(): Promise { super.connectedCallback() await this.updateComplete } // METHODS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - async show() { await this._dialog.show() console.log(this._dialogForm) console.log(serialize(this._dialogForm).data) if (this.step != 1 || serialize(this._dialogForm).data) { AlertUtils.spawn({ message: msg( 'The progress you made in creating your own data set was successfully restored!', ), variant: 'success', icon: 'check-circle', }) } } nextStep(e: MouseEvent) { const form: any = e.target if (!form.checkValidity()) { form.reportValidity() return } if (this.step == 5) { void this.validateAndCreate() } else { this.step++ } e.preventDefault() } // step 3 (configuring the features (keys/descriptions)) addFeature() { this.config.featureDescs.push({ key: '', description: '' }) this.config = { ...this.config } } removeFeature() { this.config.featureDescs.pop() this.config = { ...this.config } } // step 4 (configuring the label (key/description) and its classes // (keys/descriptions)) addLabelClass() { this.config.labelDesc.classes.push({ id: undefined, description: '' }) this.config = { ...this.config } } removeLabelClass() { this.config.labelDesc.classes.pop() this.config = { ...this.config } } // step 5 (paste data set form) async validateAndCreate() { // get data const data: string = serialize(this._dialogForm).data if ( this.availableDataSets.find((dataSet) => dataSet.name == this.config.name) ) { AlertUtils.spawn({ message: msg('A data set with the same name already exists!'), variant: 'danger', icon: 'x-circle', }) return } // additional validation const pattern = new RegExp( `^(\\s*(?:(?:[-+]?\\d+(?:\\.\\d*)?)|(?:\\d*\\.\\d+))(?:\\s*,\\s*(?:(?:[-+]?\\d+(?:\\.\\d*)?)|(?:\\d*\\.\\d+))){${this.config.featureDescs.length}}\\s*)+$`, ) const result = pattern.test(data) if (!result) { AlertUtils.spawn({ message: msg( 'The provided data does not match the required format! Please check again', ), variant: 'danger', icon: 'x-circle', }) return } // checks all passed, we can proceed parsing data const lines = data.split('\n') for (const line of lines) { // remove spaces in the beginning and end with trim and use split to // convert into array of the values const values: string[] = line.trim().split(',') console.log(values) // parse feature and label data (config.featureDescs.length * features and // one label) const features: number[] = [] let index = 0 for (const _feature of this.config.featureDescs) { features.push(parseInt(values[index].trim())) index += 1 } const label: number = parseInt(values[index]) // add parsed data from this line to the data array this.config.data.push({ features, label, }) } const dataSet: DataSet = JSON.parse(JSON.stringify(this.config)) this.dispatchEvent( new CustomEvent('add-data-set', { detail: dataSet, bubbles: true, composed: true, }), ) this.dispatchEvent( new CustomEvent('select-data-set', { detail: dataSet, bubbles: true, composed: true, }), ) this.config = JSON.parse(JSON.stringify(this.emptyConfig)) this.step = 1 this._dialogForm.reset() AlertUtils.spawn({ message: msg( 'A new data set was successfully created and automatically selected!', ), variant: 'success', icon: 'check-circle', }) await this._dialog.hide() } // STYLES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static styles: CSSResult[] = [ globalStyles, css` sl-dialog::part(base) { position: absolute; height: 100%; width: 100%; } sl-dialog::part(overlay) { position: absolute; width: 100%; } sl-dialog::part(body) { text-align: center; } .form-main { margin: 15px 0; } p, sl-input, sl-textarea, sl-button, c-card { margin-bottom: 10px; } form *[label] { text-align: left; } .step-chooser { display: flex; flex-direction: row; justify-content: space-evenly; align-items: center; margin-bottom: 20px; } `, ] // RENDER - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - render(): TemplateResult<1> { return html`
1 2 3 4 5
{ this.nextStep(e) }} >

${msg('Welcome')}

${msg( 'This tour will guide you through creating your own data set in a few simple steps. Everything is stored automatically, so you can close this modal at any time and resume.', )}

${msg('General info about the data set')}

${msg( 'Choose a short but meaningful name for your data set and write a description.', )}

{ this.config.name = (e.target).value this.config = { ...this.config } }} > { this.config.description = (e.target).value this.config = { ...this.config } }} >

${msg( "Choose 'regression' if you want to predict continous values like house or gas prices", )}

${msg( "Choose 'classification' if you want information about the affiliation of the feature(s) to a class (e.g. what animal can be seen in this image? A dog, cat or horse?)", )}

${msg('Choose a type')}

${msg('Regression')} ${msg('Classification')}

${msg('Features')}

${msg( 'Which data will be put into the neural network? Create arbitrary many features!', )}

${this.config.featureDescs.map( (featureDesc, index) => html`
{ this.config.featureDescs[index].key = ( e.target as HTMLInputElement ).value this.config = { ...this.config } }} > { this.config.featureDescs[index].description = ( e.target as HTMLInputElement ).value this.config = { ...this.config } }} >
`, )} ${this.config.featureDescs.length >= 2 ? html` ${msg('Remove feature')} ` : html``} ${msg('Add feature')}

${msg('Label')}

${msg('What shall be the output of the network?')}

{ this.config.labelDesc.key = (e.target).value this.config = { ...this.config } }} > { this.config.labelDesc.description = (( e.target )).value this.config = { ...this.config } }} > ${this.config.type == 'classification' ? html`

Classes

${this.config.labelDesc.classes?.map( (clazz, index) => html`
{ this.config.labelDesc.classes[index].id = parseInt((e.target as HTMLInputElement).value) this.config = { ...this.config } }} > { this.config.labelDesc.classes[ index ].description = (( e.target )).value this.config = { ...this.config } }} >
`, )} ${this.config.labelDesc.classes.length >= 3 ? html` ${msg('Remove class')} ` : html``} ${msg('Add class')} ` : html``}

${msg('You are nearly done')}

${msg('Now add your data in the following format')}*:

${this.config.featureDescs.map( (featureDesc) => html` , `, )}
` } }