Skip to content
Snippets Groups Projects
cpg-results.vue 8.59 KiB
Newer Older
xpetrov4's avatar
xpetrov4 committed
<template>
  <div>
    <h2 v-if="sequence">
      {{ sequence.name }}
      <span v-if="sequence.name !== analysisInfo.title">: {{ analysisInfo.title }}</span>
      <!-- slot for back button -->
      <slot></slot>
    </h2>

    <!-- overview -->
    <div v-show="zdnaList">
      <heatchart
          v-if="heatmapData"
          :data="heatmapData"
          y-axis-title="Count (Z-DNA)"
          @dataPointClick="showSegment"
          @onZoom="zoomHeatchart"
      ></heatchart>
      <heatmap v-if="heatmapData" :data="heatmapData" @segmentClick="showSegment" :selected="selectedSegment">
      </heatmap>

      <table class="table table-striped table-hover">
        <tbody>
        <tr>
          <th>Analysis settings</th>
          <th>Analysis results</th>
          <th class="text-center">Export</th>
          <th v-if="sequence">Sequence info</th>
        </tr>
        <tr>
          <td>
            Minimal window size: {{ analysisInfo.minWindowSize | number(0) }}
            <br />
            Minimal GC percentage: {{ analysisInfo.minGcPercentage }}
            <br />
            Minimal observed/expected CpG: {{ analysisInfo.minObservedToExpectedCpG }}
            <br />
            Minimal island merge gap: {{ analysisInfo.minIslandMergeGap }}
            <br />
            First nucleotide: {{ analysisInfo.firstNucleotide }}
            <br />
            Second nucleotide: {{ analysisInfo.secondNucleotide }}
          </td>
          <td>
            Islands found: {{ analysisInfo.resultCount | number(0) }}
            <br />
            <span v-if="sequence">Frequency: {{ (analysisInfo.resultCount / sequence.length) * 1000 | number(3) }} / 1000 bp</span>
          </td>
          <td class="text-center">
            <a v-if="downloadToken" :href="downloadCSV()" class="btn btn-primary btn-sm">
              <span class="fa fa-cube"></span> CSV
            </a>
            <a v-if="downloadToken" :href="downloadBED()" class="btn btn-primary btn-sm">
              <span class="fa fa-bed"></span> BedGraph
            </a>
          </td>
          <td v-if="sequence">
            {{ sequence.name }}
            <br />
            {{ sequence.length | number(0) }} bp
            <br />
            GC: {{ GCCount }} ({{ GCRate | number(1) }}%)
          </td>
        </tr>
        </tbody>
      </table>

      <!-- list of results -->
      <pagination ref="pagination" @pageChange="reload" :page-size="10">
        <table class="table table-striped table-hover">
          <tbody>
          <tr>
            <th>
              <sort-toggle @sort="sortSequences" crit="start" :current="currentSort">Start</sort-toggle>
            </th>
            <th>
              <sort-toggle @sort="sortSequences" crit="end" :current="currentSort">End</sort-toggle>
            </th>
            <th>Sequence</th>
            <th>
              <sort-toggle @sort="sortSequences" crit="gcPerc" :current="currentSort">
                GC Percentage
              </sort-toggle>
            </th>
            <th>
              <sort-toggle @sort="sortSequences" crit="observedToExpectedCpG" :current="currentSort">
                Observer/expected CpG
              </sort-toggle>
            </th>
            <th class="text-center">
              <sort-toggle @sort="sortSequences" crit="length" :current="currentSort">Length</sort-toggle>
            </th>
          </tr>
          <template v-for="zdna in zdnaList">
            <tr>
              <td>
                {{ zdna.position | number(0) }}
              </td>
              <td>
                {{ zdna.end }}
              </td>
              <td class="sequence text-monospace text-bold">
                <highlight :sequence="zdna.sequence.slice(0, 20)+'...'"></highlight>
              </td>

              <td class="sequence text-monospace">{{ zdna.gcPerc  | number(3) }}</td>
              <td class="sequence text-monospace">{{ zdna.observedToExpectedCpG | number(3) }}</td>
              <td class="text-right">
                {{ zdna.length | number }}
              </td>
            </tr>
          </template>
          </tbody>
        </table>
      </pagination>

      <div v-show="zdnaList && zdnaList.length === 0">
        <p class="alert alert-warning">
          No CpG islands found.
        </p>
      </div>
    </div>
  </div>
</template>

<script>
import Formatters from '@/formatters'
import SortToggle from '@/components/core/sort-toggle'
import Pagination from '@/components/core/pagination'
import SequenceStats from '@/components/sequence/helpers/sequence-stats'
import Helpers from '@/helpers'
import Heatchart from '@/components/core/visualisation/heatchart'
import Heatmap from '@/components/core/visualisation/heatmap'
import Highlight from '@/components/core/visualisation/highlight'

/**
 * list of results for sequence analysis
 */
export default {
  props: ['analysisInfo', 'downloadToken'],
  data() {
    return {
      sequence: null,
      zdnaList: null,
      avgZdnaLen: null,
      totalZdnaLenM1: null,
      totalZdnaLenM2: null,
      heatmapData: null,
      selectedSegment: undefined,
      sequenceStart: undefined,
      sequenceLength: undefined,
      currentSort: {
        crit: 'position',
        asc: true,
      },
    }
  },
  methods: {
    getSequenceInfo() {
      return this.$http.get('sequence/' + this.analysisInfo.sequenceId).then(response => {
        this.sequence = response.data.payload
      })
    },
    countNucleic() {
      this.$http.patch('sequence/' + this.analysisInfo.sequenceId + '/nucleic-counts').catch(err => {
        this.httpError = err
      })
    },
    reload() {
      return this.$http
          .get('analyse/cpg/' + this.analysisInfo.id + '/cpg', {
            params: {
              sort: this.currentSort.crit,
              order: this.currentSort.asc ? 'ASC' : 'DESC',
              sequenceStart: this.sequenceStart,
              sequenceLength: this.sequenceLength,
              pageSize: this.$refs.pagination.getPageSize(),
              pageNumber: this.$refs.pagination.getPageCurrent() - 1,
            },
          })
          .then(response => {
            this.$refs.pagination.setPageCount(response.data.page.totalElements)
            Helpers.setUIState(response.data.items, 'showDetails', false)
            this.zdnaList = response.data.items
            this.countNucleic()
          })
    },
    toggleDetails(zdna) {
      zdna.ui.showDetails = !zdna.ui.showDetails
    },
    getAvgRizLen() {
      return this.$http.get('analyse/cpg/' + this.analysisInfo.id + '/average/length').then(response => {
        this.avgZdnaLen = response.data
      })
    },
    
    sortSequences(info) {
      this.currentSort = info
      this.reload()
    },
    async downloadHeatmap() {
      const total = this.sequence.length
      const { sequenceFrom: from = 0, sequenceTo: to = total, analysisInfo } = this
      const segments = 80
      const { data } = await this.$http.get(`analyse/cpg/${analysisInfo.id}/heatmap`, {
        params: { segments, from, to },
      })

      this.heatmapData = { ...data, from, to, total }
    },
    /**
     * click in heatmap
     *
     * @param seg
     */
    showSegment(pos, size) {
      if (this.selectedSegment === pos) {
        this.selectedSegment = undefined
        this.sequenceStart = undefined
        this.sequenceLength = undefined
      } else {
        this.selectedSegment = pos
        this.sequenceStart = Math.floor(pos * size)
        this.sequenceLength = Math.ceil(size)
      }
      this.reload()
    },
    async zoomHeatchart(from, to, onSuccess) {
      this.sequenceFrom = from
      this.sequenceTo = to
      await this.downloadHeatmap()
      onSuccess()
    },
    downloadCSV() {
      return `${this.$http.defaults.baseURL}analyse/cpg/${this.analysisInfo.id}/cpg.csv`
    },
    downloadBED() {
      return `${this.$http.defaults.baseURL}analyse/cpg/${this.analysisInfo.id}/cpg.bedgraph`
    },
  },
  mounted() {
    this.reload()
    this.getSequenceInfo().then(() => {
      this.getAvgRizLen().then(() => {
        this.downloadHeatmap()
      })
    })
  },
  computed: {
    GCCount() {
      return SequenceStats.calcGCCount(this.sequence)
    },
    GCRate() {
      return SequenceStats.calcGCRate(this.sequence)
    },
  },
  components: {
    highlight: Highlight,
    'sort-toggle': SortToggle,
    pagination: Pagination,
    heatmap: Heatmap,
    heatchart: Heatchart,
  },
  filters: {
    number: Formatters.number,
    genePos: Formatters.genePos,
  },
}
</script>

<style scoped>
td,
th {
  padding: 0.5rem;
}
td.sequence {
  max-width: 400px;
}
td.details {
  max-width: 600px;
}
td.details .feature {
  border: 1px solid red;
}
td .toggle {
  cursor: pointer;
}
</style>