Skip to content
Snippets Groups Projects
cpg-results.vue 10.6 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 -->
xpetrov4's avatar
xpetrov4 committed
    <div v-show="cpgList">
xpetrov4's avatar
xpetrov4 committed
      <heatchart
xpetrov4's avatar
xpetrov4 committed
        v-if="heatmapData"
        :data="heatmapData"
        y-axis-title="Count (CpG)"
        @dataPointClick="showSegment"
        @onZoom="zoomHeatchart"
xpetrov4's avatar
xpetrov4 committed
      ></heatchart>
xpetrov4's avatar
xpetrov4 committed
      <heatmap
        v-if="heatmapData"
        :data="heatmapData"
        @segmentClick="showSegment"
        :selected="selectedSegment"
      >
xpetrov4's avatar
xpetrov4 committed
      </heatmap>

      <table class="table table-striped table-hover">
        <tbody>
xpetrov4's avatar
xpetrov4 committed
          <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 C{{ analysisInfo.secondNucleotide }} percentage: {{ analysisInfo.minGcPercentage }}
xpetrov4's avatar
xpetrov4 committed
              <br />
              Minimal observed/expected Cp{{ analysisInfo.secondNucleotide }}:
xpetrov4's avatar
xpetrov4 committed
              {{ 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>
xpetrov4's avatar
xpetrov4 committed
        </tbody>
      </table>

      <!-- list of results -->
      <pagination ref="pagination" @pageChange="reload" :page-size="10">
        <table class="table table-striped table-hover">
          <tbody>
            <tr>
xpetrov4's avatar
xpetrov4 committed
              <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 class="text-center">&nbsp;</th>
              <th>Sequence</th>
              <th>
                <sort-toggle
                  @sort="sortSequences"
                  crit="gcPerc"
                  :current="currentSort"
                >
                  C{{ analysisInfo.secondNucleotide }} Percentage
xpetrov4's avatar
xpetrov4 committed
                </sort-toggle>
              </th>
              <th>
                <sort-toggle
                  @sort="sortSequences"
                  crit="observedToExpectedCpG"
                  :current="currentSort"
                >
                  Observer/expected Cp{{ analysisInfo.secondNucleotide }}
xpetrov4's avatar
xpetrov4 committed
                </sort-toggle>
              </th>
              <th class="text-center">
                <sort-toggle @sort="sortSequences" crit="length" :current="currentSort">Length</sort-toggle>
              </th>
xpetrov4's avatar
xpetrov4 committed
            </tr>
xpetrov4's avatar
xpetrov4 committed
            <template v-for="cpg in cpgList">
              <tr>
                <td>
                  {{ cpg.position | number(0) }}
                </td>
                <td>
                  {{ cpg.end }}
                </td>
                <td class="text-center">
                  <span
                    class="toggle fa fa-search"
                    title="Toggle details"
                    @click="toggleSequence(cpg)"
                  ></span>
                </td>
                <td class="sequence text-monospace text-bold">
                  <highlight
                    v-if="cpg.ui.showDetails"
                    :sequence="cpg.sequence"
                  ></highlight>
                  <highlight
                    v-else
                    :sequence="cpg.sequence.slice(0, 20)+'...'"
                  ></highlight>
                </td>

                <td class="sequence text-monospace">
xpetrov4's avatar
xpetrov4 committed
                  {{ cpg.gcPerc * 100 | number(3) }} %
xpetrov4's avatar
xpetrov4 committed
                </td>
                <td class="sequence text-monospace">
                  {{ cpg.observedToExpectedCpG | number(3) }}
                </td>
                <td class="text-right">
                  {{ cpg.length | number }}
                </td>
              </tr>
            </template>
xpetrov4's avatar
xpetrov4 committed
          </tbody>
        </table>
      </pagination>

xpetrov4's avatar
xpetrov4 committed
      <div v-show="cpgList && cpgList.length === 0">
xpetrov4's avatar
xpetrov4 committed
        <p class="alert alert-warning">No CpG islands found.</p>
xpetrov4's avatar
xpetrov4 committed
      </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 {
xpetrov4's avatar
xpetrov4 committed
  props: {
        analysisInfo: {
            type: Object,
            default: () => ({})
        },
        downloadToken: {
            type: String,
            default: '' 
        },
xpetrov4's avatar
xpetrov4 committed
    data() {
        return {
            sequence: null,
            cpgList: null,
            avgCpgLen: null,
            heatmapData: null,
            selectedSegment: undefined,
            sequenceStart: undefined,
            sequenceLength: undefined,
            currentSort: {
                crit: 'position',
                asc: true,
xpetrov4's avatar
xpetrov4 committed
            },
xpetrov4's avatar
xpetrov4 committed
        }
xpetrov4's avatar
xpetrov4 committed
    },
xpetrov4's avatar
xpetrov4 committed
    methods: {
        toggleSequence(cpg) {
        cpg.ui.showDetails = !cpg.ui.showDetails;
        },
        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.cpgList = response.data.items
                        this.countNucleic()
                    })
        },
        getAvgRizLen() {
            return this.$http.get('analyse/cpg/' + this.analysisInfo.id + '/average/length').then(response => {
                this.avgCpgLen = response.data
            })
        },
xpetrov4's avatar
xpetrov4 committed

xpetrov4's avatar
xpetrov4 committed
        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`
        },
xpetrov4's avatar
xpetrov4 committed
    },
xpetrov4's avatar
xpetrov4 committed
    mounted() {
        this.reload()
        this.getSequenceInfo().then(() => {
            this.getAvgRizLen().then(() => {
                this.downloadHeatmap()
            })
        })
xpetrov4's avatar
xpetrov4 committed
    },
xpetrov4's avatar
xpetrov4 committed
    computed: {
        GCCount() {
            return SequenceStats.calcGCCount(this.sequence)
        },
        GCRate() {
            return SequenceStats.calcGCRate(this.sequence)
        },
xpetrov4's avatar
xpetrov4 committed
    },
xpetrov4's avatar
xpetrov4 committed
    components: {
        highlight: Highlight,
        'sort-toggle': SortToggle,
        pagination: Pagination,
        heatmap: Heatmap,
        heatchart: Heatchart,
xpetrov4's avatar
xpetrov4 committed
    },
xpetrov4's avatar
xpetrov4 committed
    filters: {
        number: Formatters.number,
        genePos: Formatters.genePos,
xpetrov4's avatar
xpetrov4 committed
    },
}
</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>