Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/frontend/vue/components/CodeExercise/CodeOutput.vue
3376 views
<template>
  <div ref="outputDiv" class="code-output">
    <div>
      <div v-if="!kernel && !error" class="code-output__state-info">
        <bx-loading class="code-output__state-info__icon" assistive-text="Connecting to the server" type="small" />
        {{ $translate('Connecting to the server') }}
      </div>
      <div v-if="error !== ''" class="code-output__state-info">
        <div class="code-output__state-info__warning">
          <WarningIcon />
          <div>
            The kernel may have died from inactivity. Please <BasicLink class="code-output__state-info__warning-cta" @click="refreshPage">
              refresh
            </BasicLink> the page to run the code.
          </div>
        </div>
      </div>
    </div>
    <div ref="outputDiv" />
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue-demi'
import { getQiskitUser } from '../../../ts/qiskitUser'
import WarningIcon from '@carbon/icons-vue/lib/warning--alt/32'
import BasicLink from '../common/BasicLink.vue'
import { OutputArea, IOutputShellFuture, createOutputArea } from './OutputRenderer'
import { requestBinderKernel, IKernelConnection, IStreamMsg } from './KernelManager'
import 'carbon-web-components/es/components/loading/loading'

declare global {
  interface Window {
    textbook: any
  }
}

const pageRoute = window.location.pathname

export default defineComponent({
  name: 'CodeOutput',
  components: {
    BasicLink,
    WarningIcon
  },
  data () {
    return {
      kernelPromise: undefined as Promise<IKernelConnection> | undefined,
      kernel: undefined as IKernelConnection | undefined,
      outputArea: undefined as OutputArea | undefined,
      error: '',
      apiTokenPromise: undefined as Promise<string | undefined> | undefined
    }
  },
  mounted () {
    this.$emit('loadingKernel')
    const outputDivRef = (this.$refs.outputDiv as HTMLDivElement)

    this.kernelPromise = requestBinderKernel()

    this.kernelPromise.then((kernel: IKernelConnection) => {
      this.kernel = kernel
      this.$emit('kernelReady')
      this.outputArea = createOutputArea(outputDivRef)
    }, (error: Error) => {
      this.error = error.message
    })

    this.apiTokenPromise = getQiskitUser().then(user => user?.apiToken)
  },
  methods: {
    needsApiToken (code: string) {
      return this.apiTokenPromise!.then((apiToken?: string) => {
        return this.isBackendExecution(code) && !apiToken
      })
    },
    isBackendExecution (code: string) {
      return code.includes('IBMQ.')
    },
    requestExecute (code: string) {
      if (this.isBackendExecution(code)) {
        this._executeFromBackend(code)
      } else {
        this._executeCode(code)
      }
    },
    _executeFromBackend (code: string) {
      this.error = ''
      this.apiTokenPromise!.then((apiToken: string | undefined) => {
        const accountLoadingCode = `
        from qiskit import IBMQ
        try:
          IBMQ.disable_account()
        except:
          pass

        IBMQ.enable_account('${apiToken}')
        `
        const codeWithAccount = `${accountLoadingCode}${code}`

        this._executeCode(codeWithAccount)
      })
    },
    _executeCode (code: string) {
      this.error = ''
      this.outputArea!.setHidden(true)
      this.kernelPromise!.then((kernel: IKernelConnection) => {
        this.$emit('running')
        try {
          const requestFuture = kernel.requestExecute({ code })
          this.setOutputFuture(requestFuture)
          requestFuture.done.then(() => {
            this.$emit('finished')
            this.outputArea!.setHidden(false)
          })
          requestFuture.registerMessageHook((msgContainer) => {
            const message = (msgContainer as IStreamMsg)?.content?.text
            if (message && message.includes('Your answer is correct')) {
              this.$emit('correctAnswer')
            }
            return true
          })
        } catch (error: any) {
          this.error = error as string
          this.outputArea!.setHidden(false)
          // reset button back to 'Run' on inactive kernels
          setTimeout(() => this.$emit('finished'), 800)
        }
      })
    },
    setOutputFuture (future : IOutputShellFuture) {
      this.outputArea!.future = future
    },
    clearOutput () {
      window.textbook.trackClickEvent('Reset', `Scratchpad code cell, ${pageRoute}`)
      this.outputArea!.setHidden(true)
    },
    refreshPage () {
      window.location.reload()
    }
  }
})
</script>

<style lang="scss" scoped>
@import 'carbon-components/scss/globals/scss/spacing';
@import '~/../scss/variables/colors.scss';
@import 'carbon-components/scss/globals/scss/typography';

.code-output {
  &__state-info {
    padding: $spacing-07;
    display: flex;
    align-items: center;
    gap: $spacing-05;

    &__icon {
      display: block;
      width: 1.5rem;
      height: 1.5rem;
      --cds-interactive-04: #{$border-color-tertiary};
    }

    &__warning {
      @include type-style('body-long-01');
      display: flex;
      flex-flow: row;
      padding: $spacing-05;
      gap: $spacing-05;
      align-items: center;
    }
  }
}
</style>