import { LitElementWw } from '@webwriter/lit' import { CSSResult, TemplateResult, css, html } from 'lit' import { customElement, query } from 'lit/decorators.js' import { choose } from 'lit/directives/choose.js' import { consume } from '@lit/context' import { globalStyles } from '@/global_styles' import type { CNetwork } from '@/components/network/network' import { networkContext } from '@/contexts/network_context' import type { DataSet } from '@/types/data_set' import type { FeatureDesc } from '@/types/feature_desc' import { dataSetContext } from '@/contexts/data_set_context' import { DataSetUtils } from '@/utils/data_set_utils' import type { ModelConf } from '@/types/model_conf' import { modelConfContext } from '@/contexts/model_conf_context' import { ModelUtils } from '@/utils/model_utils' import { serialize } from '@shoelace-style/shoelace/dist/utilities/form.js' import { CCard } from '../reusables/c-card' import { CDataInfo } from '../reusables/c-data-info' import SlInput from '@shoelace-style/shoelace/dist/components/input/input.component.js' import SlIcon from '@shoelace-style/shoelace/dist/components/icon/icon.component.js' import SlButton from '@shoelace-style/shoelace/dist/components/button/button.component.js' import IconSend from 'bootstrap-icons/icons/send.svg' import { msg } from '@lit/localize' export class PredictCard extends LitElementWw { static scopedElements = { 'c-card': CCard, 'c-data-info': CDataInfo, 'sl-input': SlInput, 'sl-button': SlButton, 'sl-icon': SlIcon, } @consume({ context: networkContext, subscribe: true }) accessor network: CNetwork @consume({ context: dataSetContext, subscribe: true }) accessor dataSet: DataSet @consume({ context: modelConfContext, subscribe: true }) accessor modelConf: ModelConf @query('#predictForm') accessor _predictForm: HTMLFormElement // LIFECYCLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - async connectedCallback() { super.connectedCallback() await this.updateComplete this._predictForm.addEventListener('submit', (e: SubmitEvent) => this.handlePredict(e), ) } // METHODS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - handlePredict(e: SubmitEvent): void { e.preventDefault() const formData = >serialize(this._predictForm) const inputs: Record = {} Object.keys(formData).forEach((k) => { inputs[k] = parseFloat(formData[k]) }) this.dispatchEvent( new CustomEvent>('predict-model', { detail: inputs, bubbles: true, composed: true, }), ) } prepareNewPrediction(): void { this._predictForm.reset() this.dispatchEvent( new Event('delete-prediction', { bubbles: true, composed: true, }), ) } // STYLES - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - static styles: CSSResult[] = [ globalStyles, css` :host { position: relative; width: 100%; } .inputs-grid { width: 100%; display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); overflow: hidden; } `, ] // RENDER - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - render(): TemplateResult<1> { const requiredFeatureDescs: Set = new Set() for (const inputLayer of this.network.getInputLayers()) { for (const dataSetInput of DataSetUtils.getFeatureDescsByKeys( this.dataSet, inputLayer.conf.featureKeys, )) { requiredFeatureDescs.add(dataSetInput) } } return html`
${msg('Predict')}

Inputs

${Array.from(requiredFeatureDescs).map( (featureDesc) => html`
`, )}
${this.modelConf.predictedValue ? html`

${msg('Predicted label')}

${choose( this.dataSet.type, [ [ 'classification', () => { const index = (( this.modelConf.predictedValue )).indexOf( Math.max( ...(this.modelConf.predictedValue), ), ) return html`${this.dataSet.labelDesc.classes[ index ].id.toString()} ${msg('with a probability of')} ${ModelUtils.formatWeight( (this.modelConf.predictedValue)[index], )} ` }, ], [ 'regression', () => html`${ModelUtils.formatWeight( this.modelConf.predictedValue, )}`, ], ], () => html`

${msg('Error')}

`, )}
${msg('Make another prediction')} ` : html` ${msg('Predict')} `}
` } }