1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "ExportSequenceViewItems.h"
23
24 #include <QDir>
25 #include <QMainWindow>
26 #include <QMessageBox>
27
28 #include <U2Core/AnnotationSelection.h>
29 #include <U2Core/AnnotationTableObject.h>
30 #include <U2Core/AppContext.h>
31 #include <U2Core/BaseDocumentFormats.h>
32 #include <U2Core/DNAAlphabet.h>
33 #include <U2Core/DNASequenceObject.h>
34 #include <U2Core/DNASequenceSelection.h>
35 #include <U2Core/DNATranslation.h>
36 #include <U2Core/DocumentUtils.h>
37 #include <U2Core/GObjectUtils.h>
38 #include <U2Core/GUrlUtils.h>
39 #include <U2Core/L10n.h>
40 #include <U2Core/LoadRemoteDocumentTask.h>
41 #include <U2Core/QObjectScopedPointer.h>
42 #include <U2Core/SelectionUtils.h>
43 #include <U2Core/U2DbiRegistry.h>
44 #include <U2Core/U2ObjectDbi.h>
45 #include <U2Core/U2OpStatusUtils.h>
46 #include <U2Core/U2SafePoints.h>
47
48 #include <U2Formats/ExportTasks.h>
49
50 #include <U2Gui/ExportAnnotations2CSVTask.h>
51 #include <U2Gui/ExportAnnotationsDialog.h>
52 #include <U2Gui/ExportObjectUtils.h>
53 #include <U2Gui/GUIUtils.h>
54 #include <U2Gui/OpenViewTask.h>
55
56 #include <U2View/ADVConstants.h>
57 #include <U2View/ADVSequenceObjectContext.h>
58 #include <U2View/ADVUtils.h>
59 #include <U2View/AnnotatedDNAView.h>
60
61 #include "ExportBlastResultDialog.h"
62 #include "ExportSelectedSeqRegionsTask.h"
63 #include "ExportSequences2MSADialog.h"
64 #include "ExportSequencesDialog.h"
65 #include "ExportUtils.h"
66 #include "GetSequenceByIdDialog.h"
67
68 namespace U2 {
69
70 //////////////////////////////////////////////////////////////////////////
71 // ExportSequenceViewItemsController
72
ExportSequenceViewItemsController(QObject * p)73 ExportSequenceViewItemsController::ExportSequenceViewItemsController(QObject *p)
74 : GObjectViewWindowContext(p, ANNOTATED_DNA_VIEW_FACTORY_ID),
75 av(nullptr) {
76 }
77
initViewContext(GObjectView * v)78 void ExportSequenceViewItemsController::initViewContext(GObjectView *v) {
79 av = qobject_cast<AnnotatedDNAView *>(v);
80 ADVExportContext *vc = new ADVExportContext(av);
81 addViewResource(av, vc);
82 }
83
buildStaticOrContextMenu(GObjectView * v,QMenu * m)84 void ExportSequenceViewItemsController::buildStaticOrContextMenu(GObjectView *v, QMenu *m) {
85 QList<QObject *> resources = viewResources.value(v);
86 assert(resources.size() == 1);
87 QObject *r = resources.first();
88 ADVExportContext *vc = qobject_cast<ADVExportContext *>(r);
89 assert(vc != nullptr);
90 vc->buildMenu(m);
91 }
92
init()93 void ExportSequenceViewItemsController::init() {
94 GObjectViewWindowContext::init();
95 if (!viewResources.value(av).isEmpty()) {
96 QMenu *actions = AppContext::getMainWindow()->getTopLevelMenu(MWMENU_ACTIONS);
97 SAFE_POINT(nullptr != actions, "Actions menu not found.", );
98 actions->clear();
99 AppContext::getMainWindow()->getMDIManager()->getActiveWindow()->setupViewMenu(actions);
100 }
101 }
102
103 //////////////////////////////////////////////////////////////////////////
104 // ADV view context
105
106 // TODO: define global BLAST text constants in CoreLibs
107 #define BLAST_ANNOTATION_NAME "blast result"
108
ADVExportContext(AnnotatedDNAView * v)109 ADVExportContext::ADVExportContext(AnnotatedDNAView *v)
110 : view(v) {
111 sequence2SequenceAction = new QAction(tr("Export selected sequence region..."), this);
112 sequence2SequenceAction->setObjectName("action_export_selected_sequence_region");
113 connect(sequence2SequenceAction, SIGNAL(triggered()), SLOT(sl_saveSelectedSequences()));
114
115 annotations2SequenceAction = new QAction(tr("Export sequence of selected annotations..."), this);
116 annotations2SequenceAction->setObjectName("action_export_sequence_of_selected_annotations");
117 connect(annotations2SequenceAction, SIGNAL(triggered()), SLOT(sl_saveSelectedAnnotationsSequence()));
118
119 annotations2CSVAction = new QAction(tr("Export annotations..."), this);
120 annotations2CSVAction->setObjectName(ACTION_EXPORT_ANNOTATIONS);
121 connect(annotations2CSVAction, SIGNAL(triggered()), SLOT(sl_saveSelectedAnnotations()));
122
123 annotationsToAlignmentAction = new QAction(QIcon(":core/images/msa.png"), tr("Align selected annotations..."), this);
124 annotationsToAlignmentAction->setObjectName("Align selected annotations");
125 connect(annotationsToAlignmentAction, SIGNAL(triggered()), SLOT(sl_saveSelectedAnnotationsToAlignment()));
126
127 annotationsToAlignmentWithTranslatedAction = new QAction(QIcon(":core/images/msa.png"), tr("Align selected annotations (amino acids)..."), this);
128 annotationsToAlignmentWithTranslatedAction->setObjectName("Align selected annotations (amino acids)...");
129 connect(annotationsToAlignmentWithTranslatedAction, SIGNAL(triggered()), SLOT(sl_saveSelectedAnnotationsToAlignmentWithTranslation()));
130
131 sequenceToAlignmentAction = new QAction(QIcon(":core/images/msa.png"), tr("Align selected sequence regions..."), this);
132 sequenceToAlignmentAction->setObjectName("action_align_selected_sequence_regions");
133 connect(sequenceToAlignmentAction, SIGNAL(triggered()), SLOT(sl_saveSelectedSequenceToAlignment()));
134
135 sequenceToAlignmentWithTranslationAction = new QAction(QIcon(":core/images/msa.png"), tr("Align selected sequence regions (amino acids)..."), this);
136 sequenceToAlignmentWithTranslationAction->setObjectName("Align selected sequence regions (amino acids)");
137 connect(sequenceToAlignmentWithTranslationAction, SIGNAL(triggered()), SLOT(sl_saveSelectedSequenceToAlignmentWithTranslation()));
138
139 sequenceById = new QAction(tr("Export sequences by 'id'"), this);
140 connect(sequenceById, SIGNAL(triggered()), SLOT(sl_getSequenceById()));
141 sequenceByAccession = new QAction(tr("Export sequences by 'accession'"), this);
142 connect(sequenceByAccession, SIGNAL(triggered()), SLOT(sl_getSequenceByAccession()));
143 sequenceByDBXref = new QAction(tr("Export sequences by 'db_xref'"), this);
144 connect(sequenceByDBXref, SIGNAL(triggered()), SLOT(sl_getSequenceByDBXref()));
145
146 blastResultToAlignmentAction = new QAction(tr("Export BLAST result to alignment"), this);
147 blastResultToAlignmentAction->setObjectName("export_BLAST_result_to_alignment");
148 connect(blastResultToAlignmentAction, SIGNAL(triggered()), SLOT(sl_exportBlastResultToAlignment()));
149
150 connect(view->getAnnotationsSelection(),
151 SIGNAL(si_selectionChanged(AnnotationSelection *, const QList<Annotation *> &, const QList<Annotation *> &)),
152 SLOT(updateActions()));
153
154 connect(view->getAnnotationsGroupSelection(),
155 SIGNAL(si_selectionChanged(AnnotationGroupSelection *, const QList<AnnotationGroup *> &, const QList<AnnotationGroup *> &)),
156 SLOT(updateActions()));
157
158 connect(view, SIGNAL(si_sequenceAdded(ADVSequenceObjectContext *)), SLOT(sl_onSequenceContextAdded(ADVSequenceObjectContext *)));
159 connect(view, SIGNAL(si_sequenceRemoved(ADVSequenceObjectContext *)), SLOT(sl_onSequenceContextRemoved(ADVSequenceObjectContext *)));
160 foreach (ADVSequenceObjectContext *sCtx, view->getSequenceContexts()) {
161 sl_onSequenceContextAdded(sCtx);
162 }
163 }
164
sl_onSequenceContextAdded(ADVSequenceObjectContext * c)165 void ADVExportContext::sl_onSequenceContextAdded(ADVSequenceObjectContext *c) {
166 connect(c->getSequenceSelection(),
167 SIGNAL(si_selectionChanged(LRegionsSelection *, const QVector<U2Region> &, const QVector<U2Region> &)),
168 SLOT(updateActions()));
169
170 updateActions();
171 }
172
sl_onSequenceContextRemoved(ADVSequenceObjectContext * c)173 void ADVExportContext::sl_onSequenceContextRemoved(ADVSequenceObjectContext *c) {
174 c->getSequenceSelection()->disconnect(this);
175 updateActions();
176 }
177
allNucleic(const QList<ADVSequenceObjectContext * > & seqs)178 static bool allNucleic(const QList<ADVSequenceObjectContext *> &seqs) {
179 foreach (const ADVSequenceObjectContext *s, seqs) {
180 if (!s->getAlphabet()->isNucleic()) {
181 return false;
182 }
183 }
184 return true;
185 }
186
updateActions()187 void ADVExportContext::updateActions() {
188 bool hasSelectedAnnotations = !view->getAnnotationsSelection()->isEmpty();
189 bool hasSelectedGroups = !view->getAnnotationsGroupSelection()->isEmpty();
190 int nSequenceSelections = 0;
191 foreach (ADVSequenceObjectContext *c, view->getSequenceContexts()) {
192 nSequenceSelections += c->getSequenceSelection()->getSelectedRegions().count();
193 }
194
195 sequence2SequenceAction->setEnabled(nSequenceSelections >= 1);
196 annotations2SequenceAction->setEnabled(hasSelectedAnnotations);
197 annotations2CSVAction->setEnabled(hasSelectedAnnotations || hasSelectedGroups);
198
199 bool _allNucleic = allNucleic(view->getSequenceContexts());
200
201 bool hasMultipleAnnotationsSelected = view->getAnnotationsSelection()->getAnnotations().size() > 1;
202 annotationsToAlignmentAction->setEnabled(hasMultipleAnnotationsSelected);
203 annotationsToAlignmentWithTranslatedAction->setEnabled(hasMultipleAnnotationsSelected && _allNucleic);
204
205 bool hasMultiSequenceSelection = nSequenceSelections > 1;
206 sequenceToAlignmentAction->setEnabled(hasMultiSequenceSelection);
207 sequenceToAlignmentWithTranslationAction->setEnabled(hasMultiSequenceSelection && _allNucleic);
208 }
209
buildMenu(QMenu * m)210 void ADVExportContext::buildMenu(QMenu *m) {
211 QMenu *alignMenu = GUIUtils::findSubMenu(m, ADV_MENU_ALIGN);
212 SAFE_POINT(alignMenu != nullptr, "alignMenu", );
213 alignMenu->addAction(sequenceToAlignmentAction);
214 alignMenu->addAction(sequenceToAlignmentWithTranslationAction);
215 alignMenu->addAction(annotationsToAlignmentAction);
216 alignMenu->addAction(annotationsToAlignmentWithTranslatedAction);
217
218 QMenu *exportMenu = GUIUtils::findSubMenu(m, ADV_MENU_EXPORT);
219 SAFE_POINT(exportMenu != nullptr, "exportMenu", );
220 exportMenu->addAction(sequence2SequenceAction);
221 exportMenu->addAction(annotations2SequenceAction);
222 exportMenu->addAction(annotations2CSVAction);
223
224 bool isShowId = false;
225 bool isShowAccession = false;
226 bool isShowDBXref = false;
227 bool isBlastResult = false;
228
229 QString name;
230 if (!view->getAnnotationsSelection()->getAnnotations().isEmpty()) {
231 name = view->getAnnotationsSelection()->getAnnotations().first()->getName();
232 }
233 foreach (const Annotation *annotation, view->getAnnotationsSelection()->getAnnotations()) {
234 if (name != annotation->getName()) {
235 name = "";
236 }
237
238 if (!isShowId && !annotation->findFirstQualifierValue("id").isEmpty()) {
239 isShowId = true;
240 } else if (!isShowAccession && !annotation->findFirstQualifierValue("accession").isEmpty()) {
241 isShowAccession = true;
242 } else if (!isShowDBXref && !annotation->findFirstQualifierValue("db_xref").isEmpty()) {
243 isShowDBXref = true;
244 }
245
246 isBlastResult = name == BLAST_ANNOTATION_NAME;
247 }
248
249 if (isShowId || isShowAccession || isShowDBXref) {
250 name = name.isEmpty() ? "" : tr("from '") + name + "'";
251 QMenu *fetchMenu = new QMenu(tr("Fetch sequences from remote database"));
252 m->insertMenu(exportMenu->menuAction(), fetchMenu);
253 if (isShowId) {
254 sequenceById->setText(tr("Fetch sequences by 'id' %1").arg(name));
255 fetchMenu->addAction(sequenceById);
256 }
257 if (isShowAccession) {
258 sequenceByAccession->setText(tr("Fetch sequences by 'accession' %1").arg(name));
259 fetchMenu->addAction(sequenceByAccession);
260 }
261 if (isShowDBXref) {
262 sequenceByDBXref->setText(tr("Fetch sequences by 'db_xref' %1").arg(name));
263 fetchMenu->addAction(sequenceByDBXref);
264 }
265 }
266 if (isBlastResult) {
267 exportMenu->addAction(blastResultToAlignmentAction);
268 }
269 }
270
sl_saveSelectedAnnotationsSequence()271 void ADVExportContext::sl_saveSelectedAnnotationsSequence() {
272 AnnotationSelection *as = view->getAnnotationsSelection();
273 AnnotationGroupSelection *ags = view->getAnnotationsGroupSelection();
274
275 QList<Annotation *> annotations = as->getAnnotations();
276 const QList<AnnotationGroup *> groups = ags->getSelection();
277 foreach (AnnotationGroup *g, groups) {
278 g->findAllAnnotationsInGroupSubTree(annotations);
279 }
280
281 if (annotations.isEmpty()) {
282 QMessageBox::warning(view->getWidget(), L10N::warningTitle(), tr("No annotations selected!"));
283 return;
284 }
285
286 bool allowComplement = true;
287 bool allowTranslation = true;
288 bool allowBackTranslation = true;
289
290 QMap<const ADVSequenceObjectContext *, QList<SharedAnnotationData>> annotationsPerSeq;
291 foreach (Annotation *a, annotations) {
292 ADVSequenceObjectContext *seqCtx = view->getSequenceContext(a->getGObject());
293 if (seqCtx == nullptr) {
294 continue;
295 }
296
297 QList<SharedAnnotationData> &annsPerSeq = annotationsPerSeq[seqCtx];
298 annsPerSeq.append(a->getData());
299 if (annsPerSeq.size() > 1) {
300 continue;
301 }
302 U2SequenceObject *seqObj = seqCtx->getSequenceObject();
303 if (GObjectUtils::findComplementTT(seqObj->getAlphabet()) == nullptr) {
304 allowComplement = false;
305 }
306 if (GObjectUtils::findAminoTT(seqObj, false) == nullptr) {
307 allowTranslation = false;
308 }
309 if (GObjectUtils::findBackTranslationTT(seqObj) == nullptr) {
310 allowBackTranslation = false;
311 }
312 }
313
314 QString fileExt = AppContext::getDocumentFormatRegistry()->getFormatById(BaseDocumentFormats::FASTA)->getSupportedDocumentFileExtensions().first();
315 QString dirPath;
316 QString fileBaseName;
317
318 GUrl seqUrl = view->getActiveSequenceContext()->getSequenceGObject()->getDocument()->getURL();
319 GUrlUtils::getLocalPathFromUrl(seqUrl, view->getActiveSequenceContext()->getSequenceGObject()->getGObjectName(), dirPath, fileBaseName);
320 GUrl defaultUrl = GUrlUtils::rollFileName(dirPath + QDir::separator() + fileBaseName + "_annotation." + fileExt, DocumentUtils::getNewDocFileNameExcludesHint());
321
322 QObjectScopedPointer<ExportSequencesDialog> d = new ExportSequencesDialog(true,
323 allowComplement,
324 allowTranslation,
325 allowBackTranslation,
326 defaultUrl.getURLString(),
327 fileBaseName,
328 BaseDocumentFormats::FASTA,
329 AppContext::getMainWindow()->getQMainWindow());
330 d->setWindowTitle("Export Sequence of Selected Annotations");
331 d->disableAllFramesOption(true); // only 1 frame is suitable
332 d->disableStrandOption(true); // strand is already recorded in annotation
333 d->disableAnnotationsOption(true); // here we do not export annotations for sequence under another annotations
334 const int rc = d->exec();
335 CHECK(!d.isNull(), );
336
337 if (rc == QDialog::Rejected) {
338 return;
339 }
340 assert(d->file.length() > 0);
341
342 ExportAnnotationSequenceTaskSettings s;
343 ExportUtils::loadDNAExportSettingsFromDlg(s.exportSequenceSettings, d.data());
344 foreach (const ADVSequenceObjectContext *seqCtx, annotationsPerSeq.keys()) {
345 ExportSequenceAItem ei;
346 ei.sequence = seqCtx->getSequenceObject();
347 ei.complTT = seqCtx->getComplementTT();
348 ei.aminoTT = d->translate ? seqCtx->getAminoTT() : nullptr;
349 if (d->useSpecificTable && ei.sequence->getAlphabet()->isNucleic()) {
350 DNATranslationRegistry *tr = AppContext::getDNATranslationRegistry();
351 ei.aminoTT = tr->lookupTranslation(ei.sequence->getAlphabet(), DNATranslationType_NUCL_2_AMINO, d->translationTable);
352 }
353 ei.annotations = annotationsPerSeq.value(seqCtx);
354 s.items.append(ei);
355 }
356 Task *t = ExportUtils::wrapExportTask(new ExportAnnotationSequenceTask(s), d->addToProject);
357 AppContext::getTaskScheduler()->registerTopLevelTask(t);
358 }
359
sl_saveSelectedSequences()360 void ADVExportContext::sl_saveSelectedSequences() {
361 ADVSequenceObjectContext *seqCtx = view->getActiveSequenceContext();
362 DNASequenceSelection *sel = nullptr;
363 if (seqCtx != nullptr) {
364 // TODO: support multi-export..
365 sel = seqCtx->getSequenceSelection();
366 }
367 if (sel == nullptr || sel->isEmpty()) {
368 QMessageBox::warning(view->getWidget(), L10N::warningTitle(), tr("No sequence regions selected!"));
369 return;
370 }
371
372 const QVector<U2Region> ®ions = sel->getSelectedRegions();
373 bool merge = regions.size() > 1;
374 bool complement = seqCtx->getComplementTT() != nullptr;
375 bool amino = seqCtx->getAminoTT() != nullptr;
376 bool nucleic = GObjectUtils::findBackTranslationTT(seqCtx->getSequenceObject()) != nullptr;
377
378 QString fileExt = AppContext::getDocumentFormatRegistry()->getFormatById(BaseDocumentFormats::FASTA)->getSupportedDocumentFileExtensions().first();
379 QString dirPath;
380 QString fileBaseName;
381
382 GUrl seqUrl = seqCtx->getSequenceGObject()->getDocument()->getURL();
383 GUrlUtils::getLocalPathFromUrl(seqUrl, seqCtx->getSequenceGObject()->getGObjectName(), dirPath, fileBaseName);
384 GUrl defaultUrl = GUrlUtils::rollFileName(dirPath + QDir::separator() + fileBaseName + "_region." + fileExt, DocumentUtils::getNewDocFileNameExcludesHint());
385
386 QObjectScopedPointer<ExportSequencesDialog> d = new ExportSequencesDialog(merge,
387 complement,
388 amino,
389 nucleic,
390 defaultUrl.getURLString(),
391 fileBaseName,
392 BaseDocumentFormats::FASTA,
393 AppContext::getMainWindow()->getQMainWindow());
394 d->setWindowTitle("Export Selected Sequence Region");
395 const int rc = d->exec();
396 CHECK(!d.isNull(), );
397 CHECK(rc != QDialog::Rejected, );
398 SAFE_POINT(!d->file.isEmpty(), "Invalid file path", );
399
400 ExportSequenceTaskSettings s;
401 ExportUtils::loadDNAExportSettingsFromDlg(s, d.data());
402
403 const DNATranslation *aminoTrans = nullptr;
404 if (d->translate) {
405 aminoTrans = d->useSpecificTable ? GObjectUtils::findAminoTT(seqCtx->getSequenceObject(), false, d->translationTable) : seqCtx->getAminoTT();
406 }
407 const DNATranslation *backTrans = d->backTranslate ? GObjectUtils::findBackTranslationTT(seqCtx->getSequenceObject(), d->translationTable) : nullptr;
408 const DNATranslation *complTrans = seqCtx->getComplementTT();
409 Task *t = ExportUtils::wrapExportTask(new ExportSelectedSeqRegionsTask(seqCtx->getSequenceObject(), seqCtx->getAnnotationObjects(true), regions, s, aminoTrans, backTrans, complTrans), d->addToProject);
410 AppContext::getTaskScheduler()->registerTopLevelTask(t);
411 }
412
sl_saveSelectedAnnotations()413 void ADVExportContext::sl_saveSelectedAnnotations() {
414 // find annotations: selected annotations, selected groups
415 AnnotationSelection *as = view->getAnnotationsSelection();
416 QList<Annotation *> annotationSet = as->getAnnotations();
417 foreach (AnnotationGroup *group, view->getAnnotationsGroupSelection()->getSelection()) {
418 group->findAllAnnotationsInGroupSubTree(annotationSet);
419 }
420
421 if (annotationSet.isEmpty()) {
422 QMessageBox::warning(view->getWidget(), L10N::warningTitle(), tr("No annotations selected!"));
423 return;
424 }
425
426 Annotation *first = *annotationSet.begin();
427 Document *doc = first->getGObject()->getDocument();
428 ADVSequenceObjectContext *sequenceContext = view->getActiveSequenceContext();
429
430 GUrl url;
431 if (doc != nullptr) {
432 url = doc->getURL();
433 } else if (sequenceContext != nullptr) {
434 url = sequenceContext->getSequenceGObject()->getDocument()->getURL();
435 } else {
436 url = GUrl("newfile");
437 }
438
439 QString fileName = GUrlUtils::getNewLocalUrlByExtension(url, "newfile", ".csv", "_annotations");
440 QObjectScopedPointer<ExportAnnotationsDialog> d = new ExportAnnotationsDialog(fileName, AppContext::getMainWindow()->getQMainWindow());
441 d->exec();
442 CHECK(!d.isNull(), );
443
444 if (QDialog::Accepted != d->result()) {
445 return;
446 }
447
448 // TODO: lock documents or use shared-data objects
449 std::stable_sort(annotationSet.begin(), annotationSet.end(), Annotation::annotationLessThan);
450
451 // run task
452 Task *t = nullptr;
453 if (d->fileFormat() == ExportAnnotationsDialog::CSV_FORMAT_ID) {
454 U2OpStatusImpl os;
455 QByteArray seqData = sequenceContext->getSequenceObject()->getWholeSequenceData(os);
456 CHECK_OP_EXT(os, QMessageBox::critical(QApplication::activeWindow(), L10N::errorTitle(), os.getError()), );
457 t = new ExportAnnotations2CSVTask(annotationSet, seqData, sequenceContext->getSequenceObject()->getSequenceName(), sequenceContext->getComplementTT(), d->exportSequence(), d->exportSequenceNames(), d->filePath());
458 } else {
459 t = ExportObjectUtils::saveAnnotationsTask(d->filePath(), d->fileFormat(), annotationSet, d->addToProject());
460 }
461 AppContext::getTaskScheduler()->registerTopLevelTask(t);
462 }
463
464 //////////////////////////////////////////////////////////////////////////
465 // alignment part
466
467 #define MAX_ALI_MODEL (10 * 1000 * 1000)
468
prepareMAFromBlastAnnotations(MultipleSequenceAlignment & ma,const QString & qualiferId,bool includeRef,U2OpStatus & os)469 void ADVExportContext::prepareMAFromBlastAnnotations(MultipleSequenceAlignment &ma, const QString &qualiferId, bool includeRef, U2OpStatus &os) {
470 SAFE_POINT_EXT(ma->isEmpty(), os.setError(tr("Illegal parameter: input alignment is not empty!")), );
471 const QList<Annotation *> &selection = view->getAnnotationsSelection()->getAnnotations();
472 CHECK_EXT(selection.size() >= 2, os.setError(tr("At least 2 annotations are required")), );
473
474 AnnotationTableObject *ao = selection.first()->getGObject();
475 ADVSequenceObjectContext *commonSeq = view->getSequenceContext(ao);
476 qint64 maxLen = commonSeq->getSequenceLength();
477 ma->setAlphabet(commonSeq->getAlphabet());
478 QSet<QString> names;
479 int rowIdx = 0;
480
481 for (const Annotation *annotation : qAsConst(selection)) {
482 SAFE_POINT(annotation->getName() == BLAST_ANNOTATION_NAME, tr("%1 is not a BLAST annotation").arg(annotation->getName()), );
483
484 ADVSequenceObjectContext *seqCtx = view->getSequenceContext(annotation->getGObject());
485 CHECK_EXT(seqCtx != nullptr, os.setError(tr("No sequence object found")), );
486 CHECK_EXT(seqCtx == commonSeq, os.setError(tr("Can not export BLAST annotations from different sequences")), );
487
488 QString qVal = annotation->findFirstQualifierValue(qualiferId);
489 CHECK_EXT(!qVal.isEmpty(), os.setError(tr("Can not find qualifier to set as a name for BLAST sequence")), );
490
491 QString rowName = ExportUtils::genUniqueName(names, qVal);
492 U2EntityRef seqRef = seqCtx->getSequenceObject()->getSequenceRef();
493
494 maxLen = qMax(maxLen, annotation->getRegionsLen());
495 CHECK_EXT(maxLen * ma->getNumRows() <= MAX_ALI_MODEL, os.setError(tr("Alignment is too large")), );
496
497 QByteArray rowSequence;
498 QString subjSeq = annotation->findFirstQualifierValue("subj_seq");
499 if (!subjSeq.isEmpty()) {
500 ma->addRow(rowName, subjSeq.toLatin1());
501 } else {
502 AnnotationSelection::getSequenceInRegions(rowSequence, annotation->getRegions(), U2Msa::GAP_CHAR, seqRef, nullptr, nullptr, os);
503 CHECK_OP(os, );
504 ma->addRow(rowName, rowSequence);
505 }
506
507 int offset = annotation->getLocation()->regions.first().startPos;
508 ma->insertGaps(rowIdx, 0, offset, os);
509 CHECK_OP(os, );
510
511 names.insert(rowName);
512 ++rowIdx;
513 }
514
515 if (includeRef) {
516 QByteArray rowSequence = commonSeq->getSequenceObject()->getWholeSequenceData(os);
517 CHECK_OP(os, );
518 ma->addRow(commonSeq->getSequenceGObject()->getGObjectName(), rowSequence, 0);
519 }
520 }
521
prepareMAFromAnnotations(MultipleSequenceAlignment & ma,bool translate,U2OpStatus & os)522 void ADVExportContext::prepareMAFromAnnotations(MultipleSequenceAlignment &ma, bool translate, U2OpStatus &os) {
523 SAFE_POINT_EXT(ma->isEmpty(), os.setError(tr("Illegal parameter: input alignment is not empty!")), );
524 const QList<Annotation *> &selection = view->getAnnotationsSelection()->getAnnotations();
525 CHECK_EXT(selection.size() >= 2, os.setError(tr("At least 2 annotations are required")), );
526
527 // check that all sequences are present and have the same alphabets
528 const DNAAlphabet *al = nullptr;
529 foreach (const Annotation *annotation, selection) {
530 AnnotationTableObject *ao = annotation->getGObject();
531 ADVSequenceObjectContext *seqCtx = view->getSequenceContext(ao);
532 CHECK_EXT(seqCtx != nullptr, os.setError(tr("No sequence object found")), );
533 if (al == nullptr) {
534 al = seqCtx->getAlphabet();
535 } else {
536 const DNAAlphabet *al2 = seqCtx->getAlphabet();
537 // BUG524: support alphabet reduction
538 CHECK_EXT(al->getType() == al2->getType(), os.setError(tr("Different sequence alphabets")), );
539 al = al->getMap().count(true) >= al2->getMap().count(true) ? al : al2;
540 }
541 }
542 qint64 maxLen = 0;
543 ma->setAlphabet(al);
544 QSet<QString> names;
545 foreach (const Annotation *annotation, selection) {
546 QString rowName = annotation->getName();
547 AnnotationTableObject *ao = annotation->getGObject();
548 ADVSequenceObjectContext *seqCtx = view->getSequenceContext(ao);
549 U2EntityRef seqRef = seqCtx->getSequenceObject()->getSequenceRef();
550
551 maxLen = qMax(maxLen, annotation->getRegionsLen());
552 CHECK_EXT(maxLen * ma->getNumRows() <= MAX_ALI_MODEL, os.setError(tr("Alignment is too large")), );
553 const DNATranslation *complTT = annotation->getStrand().isCompementary() ? seqCtx->getComplementTT() : nullptr;
554 const DNATranslation *aminoTT = translate ? seqCtx->getAminoTT() : nullptr;
555 QByteArray rowSequence;
556 AnnotationSelection::getSequenceInRegions(rowSequence, annotation->getRegions(), U2Msa::GAP_CHAR, seqRef, complTT, aminoTT, os);
557 CHECK_OP(os, );
558
559 ma->addRow(rowName, rowSequence);
560
561 names.insert(rowName);
562 }
563 }
564
prepareMAFromSequences(MultipleSequenceAlignment & ma,bool translate,U2OpStatus & os)565 void ADVExportContext::prepareMAFromSequences(MultipleSequenceAlignment &ma, bool translate, U2OpStatus &os) {
566 SAFE_POINT_EXT(ma->isEmpty(), os.setError(tr("Illegal parameter: Input alignment is not empty!")), );
567
568 const DNAAlphabet *al = translate ? AppContext::getDNAAlphabetRegistry()->findById(BaseDNAAlphabetIds::AMINO_DEFAULT()) : nullptr;
569
570 // derive alphabet
571 int nItems = 0;
572 bool forceTranslation = false;
573 foreach (ADVSequenceObjectContext *c, view->getSequenceContexts()) {
574 if (c->getSequenceSelection()->isEmpty()) {
575 continue;
576 }
577 nItems += c->getSequenceSelection()->getSelectedRegions().count();
578 const DNAAlphabet *seqAl = c->getAlphabet();
579 if (al == nullptr) {
580 al = seqAl;
581 } else if (al != seqAl) {
582 if (al->isNucleic() && seqAl->isAmino()) {
583 forceTranslation = true;
584 al = seqAl;
585 } else if (al->isAmino() && seqAl->isNucleic()) {
586 forceTranslation = true;
587 } else {
588 os.setError(tr("Can't derive alignment alphabet"));
589 return;
590 }
591 }
592 }
593
594 CHECK_EXT(nItems >= 2, os.setError(tr("At least 2 sequences required")), );
595 ma->setAlphabet(al);
596
597 // cache sequences
598 QSet<QString> names;
599 qint64 maxLen = 0;
600 foreach (ADVSequenceObjectContext *seqCtx, view->getSequenceContexts()) {
601 if (seqCtx->getSequenceSelection()->isEmpty()) {
602 continue;
603 }
604 const DNAAlphabet *seqAl = seqCtx->getAlphabet();
605 DNATranslation *aminoTT = ((translate || forceTranslation) && seqAl->isNucleic()) ? seqCtx->getAminoTT() : nullptr;
606 foreach (const U2Region &r, seqCtx->getSequenceSelection()->getSelectedRegions()) {
607 maxLen = qMax(maxLen, r.length);
608 CHECK_EXT(maxLen * ma->getNumRows() <= MAX_ALI_MODEL, os.setError(tr("Alignment is too large")), );
609 QByteArray seq = seqCtx->getSequenceData(r, os);
610 CHECK_OP(os, );
611 if (aminoTT != nullptr) {
612 int len = aminoTT->translate(seq.data(), seq.size());
613 seq.resize(len);
614 }
615 QString rowName = ExportUtils::genUniqueName(names, seqCtx->getSequenceGObject()->getGObjectName());
616 names.insert(rowName);
617 ma->addRow(rowName, seq);
618 }
619 }
620 }
621
selectionToAlignment(const QString & title,bool annotations,bool translate)622 void ADVExportContext::selectionToAlignment(const QString &title, bool annotations, bool translate) {
623 MultipleSequenceAlignment ma(MA_OBJECT_NAME);
624 U2OpStatusImpl os;
625 if (annotations) {
626 prepareMAFromAnnotations(ma, translate, os);
627 } else {
628 prepareMAFromSequences(ma, translate, os);
629 }
630 if (os.hasError()) {
631 QMessageBox::critical(nullptr, L10N::errorTitle(), os.getError());
632 return;
633 }
634
635 DocumentFormatConstraints c;
636 c.addFlagToSupport(DocumentFormatFlag_SupportWriting);
637 c.supportedObjectTypes += GObjectTypes::MULTIPLE_SEQUENCE_ALIGNMENT;
638
639 QObjectScopedPointer<ExportSequences2MSADialog> d = new ExportSequences2MSADialog(view->getWidget());
640 d->setWindowTitle(title);
641 d->setOkButtonText(tr("Create alignment"));
642 d->setFileLabelText(tr("Save alignment to file"));
643 const int rc = d->exec();
644 CHECK(!d.isNull(), );
645
646 if (rc != QDialog::Accepted) {
647 return;
648 }
649 Task *t = ExportUtils::wrapExportTask(new ExportAlignmentTask(ma, d->url, d->format), d->addToProjectFlag);
650 AppContext::getTaskScheduler()->registerTopLevelTask(t);
651 }
652
sl_saveSelectedAnnotationsToAlignment()653 void ADVExportContext::sl_saveSelectedAnnotationsToAlignment() {
654 selectionToAlignment(annotationsToAlignmentAction->text(), true, false);
655 }
656
sl_saveSelectedAnnotationsToAlignmentWithTranslation()657 void ADVExportContext::sl_saveSelectedAnnotationsToAlignmentWithTranslation() {
658 selectionToAlignment(annotationsToAlignmentAction->text(), true, true);
659 }
660
sl_saveSelectedSequenceToAlignment()661 void ADVExportContext::sl_saveSelectedSequenceToAlignment() {
662 selectionToAlignment(sequenceToAlignmentAction->text(), false, false);
663 }
664
sl_saveSelectedSequenceToAlignmentWithTranslation()665 void ADVExportContext::sl_saveSelectedSequenceToAlignmentWithTranslation() {
666 selectionToAlignment(sequenceToAlignmentWithTranslationAction->text(), false, true);
667 }
668
sl_getSequenceByDBXref()669 void ADVExportContext::sl_getSequenceByDBXref() {
670 const QList<Annotation *> &selection = view->getAnnotationsSelection()->getAnnotations();
671
672 QStringList genbankID;
673 foreach (const Annotation *ann, selection) {
674 const QString tmp = ann->findFirstQualifierValue("db_xref");
675 if (!tmp.isEmpty()) {
676 genbankID << tmp.split(":").last();
677 }
678 }
679 QString listId = genbankID.join(",");
680 fetchSequencesFromRemoteDB(listId);
681 }
682
sl_getSequenceByAccession()683 void ADVExportContext::sl_getSequenceByAccession() {
684 const QList<Annotation *> &selection = view->getAnnotationsSelection()->getAnnotations();
685
686 QStringList genbankID;
687 foreach (const Annotation *ann, selection) {
688 const QString tmp = ann->findFirstQualifierValue("accession");
689 if (!tmp.isEmpty()) {
690 genbankID << tmp;
691 }
692 }
693 QString listId = genbankID.join(",");
694 fetchSequencesFromRemoteDB(listId);
695 }
696
sl_getSequenceById()697 void ADVExportContext::sl_getSequenceById() {
698 const QList<Annotation *> &selection = view->getAnnotationsSelection()->getAnnotations();
699
700 QStringList genbankID;
701 foreach (const Annotation *ann, selection) {
702 const QString tmp = ann->findFirstQualifierValue("id");
703 if (!tmp.isEmpty()) {
704 int off = tmp.indexOf("|");
705 int off1 = tmp.indexOf("|", off + 1);
706 genbankID << tmp.mid(off + 1, off1 - off - 1);
707 }
708 }
709 QString listId = genbankID.join(",");
710 fetchSequencesFromRemoteDB(listId);
711 }
712
fetchSequencesFromRemoteDB(const QString & listId)713 void ADVExportContext::fetchSequencesFromRemoteDB(const QString &listId) {
714 const DNAAlphabet *seqAl = view->getSequenceObjectsWithContexts().first()->getAlphabet();
715
716 QString db;
717 if (seqAl->getId() == BaseDNAAlphabetIds::NUCL_DNA_DEFAULT()) {
718 db = "NCBI GenBank (DNA sequence)";
719 } else if (seqAl->getId() == BaseDNAAlphabetIds::AMINO_DEFAULT()) {
720 db = "NCBI protein sequence database";
721 } else {
722 return;
723 }
724
725 QObjectScopedPointer<GetSequenceByIdDialog> dlg = new GetSequenceByIdDialog(view->getWidget());
726 dlg->exec();
727 CHECK(!dlg.isNull(), );
728
729 if (dlg->result() == QDialog::Accepted) {
730 QString dir = dlg->getDirectory();
731 Task *t;
732 if (dlg->isAddToProject()) {
733 t = new LoadRemoteDocumentAndAddToProjectTask(listId, db, dir);
734 } else {
735 t = new LoadRemoteDocumentTask(listId, db, dir);
736 }
737 AppContext::getTaskScheduler()->registerTopLevelTask(t);
738 }
739 }
740
sl_exportBlastResultToAlignment()741 void ADVExportContext::sl_exportBlastResultToAlignment() {
742 DocumentFormatConstraints c;
743 c.addFlagToSupport(DocumentFormatFlag_SupportWriting);
744 c.supportedObjectTypes += GObjectTypes::MULTIPLE_SEQUENCE_ALIGNMENT;
745
746 QObjectScopedPointer<ExportBlastResultDialog> d = new ExportBlastResultDialog(view->getWidget());
747 const int rc = d->exec();
748 CHECK(!d.isNull(), );
749 if (rc != QDialog::Accepted) {
750 return;
751 }
752
753 MultipleSequenceAlignment ma(MA_OBJECT_NAME);
754 U2OpStatusImpl os;
755
756 prepareMAFromBlastAnnotations(ma, d->qualiferId, d->addRefFlag, os);
757
758 if (os.hasError()) {
759 QMessageBox::critical(nullptr, L10N::errorTitle(), os.getError());
760 return;
761 }
762
763 Task *t = ExportUtils::wrapExportTask(new ExportAlignmentTask(ma, d->url, d->format), d->addToProjectFlag);
764 AppContext::getTaskScheduler()->registerTopLevelTask(t);
765 }
766
767 } // namespace U2
768