Skip to content

Commit

Permalink
generate chr_aliases on the fly
Browse files Browse the repository at this point in the history
  • Loading branch information
emiliorighi committed Oct 15, 2024
1 parent 8c9feeb commit 314808c
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 118 deletions.
14 changes: 6 additions & 8 deletions biogenome-client/src/components/genome-browser/Jbrowse2.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,12 @@ function parseAssembly(assembly: Assembly, chromosomes: ChromosomeInterface[]) {
},
},
}
if (assembly.has_chromosomes_aliases) {
assemblyAdapter.refNameAliases = {
adapter: {
type: "RefNameAliasAdapter",
location: {
uri: `${baseURL}/assemblies/${assembly.accession}/chr_aliases`,
locationType: "UriLocation"
}
assemblyAdapter.refNameAliases = {
adapter: {
type: "RefNameAliasAdapter",
location: {
uri: `${baseURL}/assemblies/${assembly.accession}/chr_aliases`,
locationType: "UriLocation"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps<{
items: Record<string, any>
items: Record<string, any>[]
}>()
</script>
234 changes: 128 additions & 106 deletions biogenome-client/src/pages/cms/annotation/components/FileForm.vue
Original file line number Diff line number Diff line change
@@ -1,60 +1,65 @@
<template>
<va-form tag="form" @submit.prevent="handleSubmit">
<div class="row justify-center">
<div class="flex">
<va-button-toggle v-model="uploadMode" preset="secondary" border-color="primary"
:options="uploadModes" />
</div>
<!-- Toggle Upload Mode -->
<div class="row justify-center">
<div class="flex">
<va-button-toggle v-model="uploadMode" preset="secondary" border-color="primary" :options="uploadModes" />
</div>
<div class="row" v-if="uploadMode === 'links'">
<va-input class="flex lg12 md12 xs12 sm12" :disabled="annotationStore.annotationForm.gzipAnnotation"
v-model="annotationStore.annotationForm.gff_gz_location" label="gzipped gff3 url"
:messages="['URL of the gzipped gff file']" />
<va-input class="flex lg12 md12 xs12 sm12" :disabled="annotationStore.annotationForm.tabixAnnotation"
v-model="annotationStore.annotationForm.tab_index_location" label="tabindexed gff3 url"
:messages="['URL of the tabindexed gzipped gff file']" />
</div>

<!-- Upload Mode: Links -->
<div class="row" v-if="uploadMode === 'links'">
<va-input class="flex lg12 md12 xs12 sm12" :disabled="annotationStore.annotationForm.gzipAnnotation"
v-model="annotationStore.annotationForm.gff_gz_location" label="gzipped gff3 url"
:messages="['URL of the gzipped gff file']" />
<va-input class="flex lg12 md12 xs12 sm12" :disabled="annotationStore.annotationForm.tabixAnnotation"
v-model="annotationStore.annotationForm.tab_index_location" label="tabindexed gff3 url"
:messages="['URL of the tabindexed gzipped gff file']" />
</div>

<!-- Upload Mode: Files -->
<div class="row" v-else>
<div v-if="isLocal" class="flex lg12 md12 sm12 xs12">
<p>
Update of uploaded files is not supported. If you need to change the file, it is necessary to delete
this
annotation and create a new one.
</p>
</div>
<div v-else class="flex lg12 md12 sm12 xs12">
<va-file-upload :disabled="isInputDisabled(annotationStore.annotationForm.gff_gz_location)" type="single"
v-model="annotationStore.annotationForm.gzipAnnotation" dropzone
drop-zone-text="Upload a gzipped GFF file" file-types=".gz" />
<va-file-upload :disabled="isInputDisabled(annotationStore.annotationForm.tab_index_location)" type="single"
v-model="annotationStore.annotationForm.tabixAnnotation" dropzone
drop-zone-text="Upload the GFF tabix index file" file-types=".tbi" />
</div>
<div class="row" v-else>
<div v-if="isLocalAssembly" class="flex lg12 md12 sm12 xs12">
<p>
Update of uploaded files is not supported. If you need to change the file, it is necessary to delete
this
assembly
and create a new one
</p>
</div>
<div v-else class="flex lg12 md12 sm12 xs12">
<va-file-upload :disabled="isValid(annotationStore.annotationForm.gff_gz_location)" type="single"
v-model="annotationStore.annotationForm.gzipAnnotation" dropzone
drop-zone-text="Upload a gff gzipped file" file-types=".gz" />
<va-file-upload :disabled="isValid(annotationStore.annotationForm.tab_index_location)" type="single"
v-model="annotationStore.annotationForm.tabixAnnotation" dropzone
drop-zone-text="Upload your GFF evidences" file-types=".tbi" />
</div>
</div>

<!-- Extra Attributes Section -->
<va-divider>Extra Attributes</va-divider>
<div v-for="(mt, index) in metadataList" :key="index" class="row align-center justify-between">
<div class="flex lg8 md8 sm8 xs8">
<va-input v-model="mt.key" label="Attribute name" class="mt-3" :error="isDuplicateAttribute(mt.key)"
:error-messages="[`Attribute name '${mt.key}' is already present`]" />
<va-input v-model="mt.value" label="Attribute value" class="mt-3" type="textarea" />
</div>
<va-divider>Extra Attributes</va-divider>
<div v-for="(mt, index) in metadataList" :key="index" class="row align-center justify-between">
<div class="flex lg8 md8 sm8 xs8">
<va-input v-model="mt.key" label="attribute name" class="mt-3"
:error="metadataList.filter((m) => m.key === mt.key).length > 1"
:error-messages="[`Attribute name ${mt.key} is already present`]" />
<va-input v-model="mt.value" label="attribute value" class="mt-3" type="textarea" />
</div>
<div class="flex">
<va-button icon="delete" color="danger" @click="metadataList.splice(index, 1)">
Delete Attribute
</va-button>
</div>
<div class="flex">
<va-button icon="delete" color="danger" @click="removeAttribute(index)">
Delete Attribute
</va-button>
</div>
<va-button class="mt-3" icon="add" @click="metadataList.push({ key: '', value: '' })">Add new
attribute</va-button>
<va-card-actions align="between">
<va-button type="reset" color="danger" @click="resetForm">Reset</va-button>
<va-button type="submit">Submit</va-button>
</va-card-actions>
</va-form>
</div>

<!-- Add New Attribute Button -->
<va-button class="mt-3" icon="add" @click="addNewAttribute">Add new attribute</va-button>

<!-- Form Actions -->
<va-card-actions align="between">
<va-button color="danger" @click="resetForm">Reset</va-button>
<va-button @click="handleSubmit">Submit</va-button>
</va-card-actions>
</template>

<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { useToast } from 'vuestic-ui'
Expand All @@ -66,114 +71,131 @@ import { useRouter } from 'vue-router'
const { init } = useToast()
const annotationStore = useAnnotationStore()
const uploadMode = ref('files')
const uploadMode = ref<'files' | 'links'>('files')
const uploadModes = [
{ label: 'Upload files', value: 'files' },
{ label: 'Insert links', value: 'links' },
]
const props = defineProps<{
name?: string
}>()
const router = useRouter()
const emits = defineEmits(['onLoading'])
watch(() => props.name, (v) => {
const isLocal = ref(false)
const metadataList = ref<{ key: string, value: any }[]>([])
const props = defineProps<{ name?: string }>()
watch(() => props.name, async (name) => {
resetForm()
if (props.name) {
if (name) {
uploadMode.value = 'links'
await retrieveAnnotation(name)
}
})
// Initialize the component when mounted
onMounted(async () => {
if (props.name) {
await retrieveAnnotation(props.name)
uploadMode.value = 'links'
}
})
const isLocalAssembly = ref(false)
const metadataList = ref<Record<string, string>[]>([])
// Reset form and metadata list
function resetForm() {
annotationStore.resetForm()
metadataList.value = []
isLocalAssembly.value = false
isLocal.value = false
}
// Retrieve annotation data for the form
async function retrieveAnnotation(name: string) {
try {
const { data } = await AnnotationService.getAnnotation(name)
isLocalAssembly.value = Boolean(data.external)
Object.keys(data)
.filter((k: string) => Object.keys(annotationStore.annotationForm).includes(k))
.forEach((k) => {
annotationStore.annotationForm[k] = data[k]
})
//parse metadata
const parsedMetadata = Object.keys(data.metadata).map((k) => {
return {
key: k,
value: data[k],
isLocal.value = Boolean(data.external)
Object.entries(data).forEach(([key, value]) => {
if (key in annotationStore.annotationForm) {
annotationStore.annotationForm[key] = value
}
})
if (parsedMetadata.length) {
metadataList.value.push(...parsedMetadata)
}
// Parse metadata
metadataList.value = Object.entries(data.metadata || {}).map(([key, value]) => ({ key, value }))
} catch {
init({ message: "Something happened, unable to retrieve annotation", color: 'danger' })
init({ message: "Unable to retrieve annotation data.", color: 'danger' })
}
}
// Handle form submission
async function handleSubmit() {
if (!annotationStore.annotationForm.assembly_accession) {
init({ message: "Select an assembly", color: 'danger' })
init({ message: "Please select an assembly.", color: 'danger' })
return
}
//parse form data
const requestData = parseRequestData()
try {
emits('onLoading', true)
const { data } = props.name ? await AuthService.updateAnnotation(props.name, requestData) : await AuthService.createAnnotation(requestData)
init({ message: data + ' saved!', color: 'success' })
router.push({name:'cms-assemblies'})
const { data } = props.name
? await AuthService.updateAnnotation(props.name, requestData)
: await AuthService.createAnnotation(requestData)
init({ message: `${data} saved successfully!`, color: 'success' })
router.push({ name: 'cms-assemblies' })
} catch (error) {
const axiosError = error as AxiosError
if (axiosError.response && axiosError.response.data) {
init({ message: axiosError.response.data as string, color: 'danger' })
} else {
init({ message: "Something happened", color: 'danger' })
}
handleError(error)
} finally {
emits('onLoading', false)
}
}
function isValid(value: any) {
if (value === undefined || value === null || (typeof value === 'string' && value.trim() === '')) {
// Value is considered empty, continue checking the rest of the object
return false
}
// If a non-empty primitive value is found, return false
return true;
}
// Parse form data to be sent
function parseRequestData() {
const metadata = Object.fromEntries(metadataList.value.filter((m) => m.key && m.value)
.map(m => [m.key, m.value]))
const metadata = Object.fromEntries(
metadataList.value.filter(m => m.key && m.value).map(m => [m.key, m.value])
)
const request = new FormData()
for (const [key, value] of Object.entries(annotationStore.annotationForm)) {
if (value) {
request.append(key, value);
}
for (const [k, v] of Object.entries(metadata)) {
const keyName = `metadata.${k}`
request.append(keyName, v)
request.append(key, value)
}
}
Object.entries(metadata).forEach(([key, value]) => {
request.append(`metadata.${key}`, value)
})
return request
}
// Error handling for Axios responses
function handleError(error: unknown) {
const axiosError = error as AxiosError
const errorMessage = axiosError.response?.data as string || "Something went wrong."
init({ message: errorMessage, color: 'danger' })
}
// Add a new attribute
function addNewAttribute() {
metadataList.value.push({ key: '', value: '' })
}
// Remove an attribute
function removeAttribute(index: number) {
metadataList.value.splice(index, 1)
}
// Check if an attribute name is duplicate
function isDuplicateAttribute(key: string): boolean {
return metadataList.value.filter(m => m.key === key).length > 1
}
// Validate input disabling logic
function isInputDisabled(value: any) {
return !!value
}
</script>
1 change: 0 additions & 1 deletion server/parsers/chromosome.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from db.models import Chromosome

def parse_chromosomes_from_ncbi_datasets(sequences):
##filter out scaffolds
chromosomes_to_save=[]
for sequence in sequences:
if sequence.get('role') == 'assembled-molecule':
Expand Down
7 changes: 5 additions & 2 deletions server/rest/assembly/assemblies_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,12 @@ def get_chr_aliases_file(accession):

# Assuming chromosomes is a list of dictionaries with fields 'name' and 'accession_version'
for chromosome in chromosomes:
chr_name = chromosome.get('metadata', {}).get('name')
print(chromosome)
name = chromosome.get('metadata', {}).get('chr_name')
if not name:
name = chromosome.get('metadata', {}).get('name')
accession_version = chromosome.get('accession_version')
tsv_data.write(f"{chr_name}\t{accession_version}\n")
tsv_data.write(f"{name}\t{accession_version}\n")

tsv_data.seek(0) # Go back to the start of the StringIO object

Expand Down

0 comments on commit 314808c

Please sign in to comment.