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 "ExtractConsensusWorker.h"
23 
24 #include <U2Algorithm/AssemblyConsensusAlgorithmRegistry.h>
25 #include <U2Algorithm/BuiltInAssemblyConsensusAlgorithms.h>
26 
27 #include <U2Core/AppContext.h>
28 #include <U2Core/BaseDocumentFormats.h>
29 #include <U2Core/FailTask.h>
30 #include <U2Core/U2AssemblyDbi.h>
31 #include <U2Core/U2OpStatusUtils.h>
32 #include <U2Core/U2SafePoints.h>
33 
34 #include <U2Designer/DelegateEditors.h>
35 
36 #include <U2Lang/ActorPrototypeRegistry.h>
37 #include <U2Lang/BaseActorCategories.h>
38 #include <U2Lang/BasePorts.h>
39 #include <U2Lang/BaseSlots.h>
40 #include <U2Lang/BaseTypes.h>
41 #include <U2Lang/WorkflowEnv.h>
42 
43 #include <U2View/AssemblyModel.h>
44 #include <U2View/ExportConsensusTask.h>
45 
46 namespace U2 {
47 namespace LocalWorkflow {
48 
49 const QString ExtractConsensusWorkerFactory::ACTOR_ID("extract-consensus");
50 
51 namespace {
52 const QString ALGO_ATTR_ID("algorithm");
53 const QString GAPS_ATTR_ID("keep-gaps");
54 }  // namespace
55 
ExtractConsensusWorker(Actor * actor)56 ExtractConsensusWorker::ExtractConsensusWorker(Actor *actor)
57     : BaseWorker(actor) {
58 }
59 
init()60 void ExtractConsensusWorker::init() {
61 }
62 
tick()63 Task *ExtractConsensusWorker::tick() {
64     if (hasAssembly()) {
65         U2OpStatusImpl os;
66         const U2EntityRef assembly = takeAssembly(os);
67         CHECK_OP(os, new FailTask(os.getError()));
68 
69         return createTask(assembly);
70     } else {
71         finish();
72         return nullptr;
73     }
74 }
75 
sl_taskFinished()76 void ExtractConsensusWorker::sl_taskFinished() {
77     ExtractConsensusTaskHelper *t = dynamic_cast<ExtractConsensusTaskHelper *>(sender());
78     CHECK(nullptr != t, );
79     CHECK(t->isFinished() && !t->hasError(), );
80     CHECK(!t->isCanceled(), );
81 
82     sendResult(context->getDataStorage()->getDataHandler(t->getResult()));
83 }
84 
cleanup()85 void ExtractConsensusWorker::cleanup() {
86 }
87 
hasAssembly() const88 bool ExtractConsensusWorker::hasAssembly() const {
89     const IntegralBus *port = ports[BasePorts::IN_ASSEMBLY_PORT_ID()];
90     SAFE_POINT(nullptr != port, "NULL assembly port", false);
91     return port->hasMessage();
92 }
93 
takeAssembly(U2OpStatus & os)94 U2EntityRef ExtractConsensusWorker::takeAssembly(U2OpStatus &os) {
95     const Message m = getMessageAndSetupScriptValues(ports[BasePorts::IN_ASSEMBLY_PORT_ID()]);
96     const QVariantMap data = m.getData().toMap();
97     if (!data.contains(BaseSlots::ASSEMBLY_SLOT().getId())) {
98         os.setError(tr("Empty assembly slot"));
99         return U2EntityRef();
100     }
101     const SharedDbiDataHandler dbiId = data[BaseSlots::ASSEMBLY_SLOT().getId()].value<SharedDbiDataHandler>();
102     const AssemblyObject *obj = StorageUtils::getAssemblyObject(context->getDataStorage(), dbiId);
103     if (nullptr == obj) {
104         os.setError(tr("Error with assembly object"));
105         return U2EntityRef();
106     }
107     return obj->getEntityRef();
108 }
109 
createTask(const U2EntityRef & assembly)110 Task *ExtractConsensusWorker::createTask(const U2EntityRef &assembly) {
111     const QString algoId = getValue<QString>(ALGO_ATTR_ID);
112     const bool keepGaps = getValue<bool>(GAPS_ATTR_ID);
113     Task *t = new ExtractConsensusTaskHelper(algoId, keepGaps, assembly, context->getDataStorage()->getDbiRef());
114     connect(t, SIGNAL(si_stateChanged()), SLOT(sl_taskFinished()));
115     return t;
116 }
117 
finish()118 void ExtractConsensusWorker::finish() {
119     IntegralBus *inPort = ports[BasePorts::IN_ASSEMBLY_PORT_ID()];
120     SAFE_POINT(nullptr != inPort, "NULL assembly port", );
121     SAFE_POINT(inPort->isEnded(), "The assembly is not ended", );
122     IntegralBus *outPort = ports[BasePorts::OUT_SEQ_PORT_ID()];
123     SAFE_POINT(nullptr != outPort, "NULL sequence port", );
124 
125     outPort->setEnded();
126     setDone();
127 }
128 
sendResult(const SharedDbiDataHandler & seqId)129 void ExtractConsensusWorker::sendResult(const SharedDbiDataHandler &seqId) {
130     QVariantMap data;
131     data[BaseSlots::DNA_SEQUENCE_SLOT().getId()] = qVariantFromValue<SharedDbiDataHandler>(seqId);
132     IntegralBus *outPort = ports[BasePorts::OUT_SEQ_PORT_ID()];
133     SAFE_POINT(nullptr != outPort, "NULL sequence port", );
134 
135     outPort->put(Message(outPort->getBusType(), data));
136 }
137 
138 /************************************************************************/
139 /* ExtractConsensusTaskHelper */
140 /************************************************************************/
ExtractConsensusTaskHelper(const QString & algoId,bool keepGaps,const U2EntityRef & assembly,const U2DbiRef & targetDbi)141 ExtractConsensusTaskHelper::ExtractConsensusTaskHelper(const QString &algoId, bool keepGaps, const U2EntityRef &assembly, const U2DbiRef &targetDbi)
142     : Task(tr("Extract consensus"), TaskFlags_NR_FOSCOE),
143       algoId(algoId),
144       keepGaps(keepGaps),
145       assembly(assembly),
146       targetDbi(targetDbi),
147       exportTask(nullptr) {
148 }
149 
prepare()150 void ExtractConsensusTaskHelper::prepare() {
151     ExportConsensusTaskSettings settings;
152 
153     settings.consensusAlgorithm = QSharedPointer<AssemblyConsensusAlgorithm>(createAlgorithm());
154     CHECK_OP(stateInfo, );
155     settings.model = QSharedPointer<AssemblyModel>(createModel());
156     CHECK_OP(stateInfo, );
157 
158     settings.region = settings.model->getGlobalRegion();
159     settings.seqObjName = settings.model->getAssembly().visualName;
160 
161     settings.saveToFile = false;
162     settings.targetDbi = targetDbi;
163     settings.addToProject = false;
164     settings.keepGaps = keepGaps;
165 
166     exportTask = new ExportConsensusTask(settings);
167     addSubTask(exportTask);
168 }
169 
getResult() const170 U2EntityRef ExtractConsensusTaskHelper::getResult() const {
171     SAFE_POINT(nullptr != exportTask, "NULL export task", U2EntityRef());
172     const U2Sequence seq = exportTask->getResult();
173     const U2EntityRef ref(targetDbi, seq.id);
174     return ref;
175 }
176 
createAlgorithm()177 AssemblyConsensusAlgorithm *ExtractConsensusTaskHelper::createAlgorithm() {
178     AssemblyConsensusAlgorithmRegistry *reg = AppContext::getAssemblyConsensusAlgorithmRegistry();
179     SAFE_POINT_EXT(nullptr != reg, setError("NULL registry"), nullptr);
180 
181     AssemblyConsensusAlgorithmFactory *f = reg->getAlgorithmFactory(algoId);
182     if (nullptr == f) {
183         setError(tr("Unknown consensus algorithm: ") + algoId);
184         return nullptr;
185     }
186 
187     return f->createAlgorithm();
188 }
189 
createModel()190 AssemblyModel *ExtractConsensusTaskHelper::createModel() {
191     const DbiConnection con(assembly.dbiRef, stateInfo);
192     CHECK_OP(stateInfo, nullptr);
193 
194     U2AssemblyDbi *dbi = con.dbi->getAssemblyDbi();
195     SAFE_POINT_EXT(nullptr != dbi, setError("NULL assembly dbi"), nullptr);
196 
197     const U2Assembly object = dbi->getAssemblyObject(assembly.entityId, stateInfo);
198     CHECK_OP(stateInfo, nullptr);
199 
200     AssemblyModel *model = new AssemblyModel(con);
201     model->setAssembly(dbi, object);
202 
203     return model;
204 }
205 
206 /************************************************************************/
207 /* ExtractConsensusWorkerFactory */
208 /************************************************************************/
ExtractConsensusWorkerFactory()209 ExtractConsensusWorkerFactory::ExtractConsensusWorkerFactory()
210     : DomainFactory(ACTOR_ID) {
211 }
212 
createWorker(Actor * actor)213 Worker *ExtractConsensusWorkerFactory::createWorker(Actor *actor) {
214     return new ExtractConsensusWorker(actor);
215 }
216 
init()217 void ExtractConsensusWorkerFactory::init() {
218     AssemblyConsensusAlgorithmRegistry *reg = AppContext::getAssemblyConsensusAlgorithmRegistry();
219     SAFE_POINT(nullptr != reg, "NULL registry", );
220 
221     const Descriptor desc(ACTOR_ID,
222                           QObject::tr("Extract Consensus from Assembly"),
223                           QObject::tr("Extract the consensus sequence from the incoming assembly."));
224 
225     QList<PortDescriptor *> ports;
226     {
227         QMap<Descriptor, DataTypePtr> inData;
228         inData[BaseSlots::ASSEMBLY_SLOT()] = BaseTypes::ASSEMBLY_TYPE();
229         DataTypePtr inType(new MapDataType(BasePorts::IN_ASSEMBLY_PORT_ID(), inData));
230         ports << new PortDescriptor(BasePorts::IN_ASSEMBLY_PORT_ID(), inType, true);
231 
232         QMap<Descriptor, DataTypePtr> outData;
233         outData[BaseSlots::DNA_SEQUENCE_SLOT()] = BaseTypes::DNA_SEQUENCE_TYPE();
234         DataTypePtr outType(new MapDataType(BasePorts::OUT_SEQ_PORT_ID(), outData));
235         ports << new PortDescriptor(BasePorts::OUT_SEQ_PORT_ID(), outType, false, true);
236     }
237 
238     QList<Attribute *> attrs;
239     {
240         const Descriptor algoDesc(ALGO_ATTR_ID,
241                                   QObject::tr("Algorithm"),
242                                   QObject::tr("The algorithm of consensus extracting."));
243         const Descriptor gapsDesc(GAPS_ATTR_ID,
244                                   QObject::tr("Keep gaps"),
245                                   QObject::tr("Set this parameter if the result consensus must keep the gaps."));
246         attrs << new Attribute(algoDesc, BaseTypes::STRING_TYPE(), true, BuiltInAssemblyConsensusAlgorithms::DEFAULT_ALGO);
247         attrs << new Attribute(gapsDesc, BaseTypes::BOOL_TYPE(), true, true);
248     }
249 
250     QMap<QString, PropertyDelegate *> delegates;
251     {
252         QVariantMap algos;
253         foreach (const QString algoId, reg->getAlgorithmIds()) {
254             AssemblyConsensusAlgorithmFactory *f = reg->getAlgorithmFactory(algoId);
255             algos[f->getName()] = algoId;
256         }
257         delegates[ALGO_ATTR_ID] = new ComboBoxDelegate(algos);
258     }
259 
260     ActorPrototype *proto = new IntegralBusActorPrototype(desc, ports, attrs);
261     proto->setPrompter(new ExtractConsensusWorkerPrompter());
262     proto->setEditor(new DelegateEditor(delegates));
263 
264     WorkflowEnv::getProtoRegistry()->registerProto(BaseActorCategories::CATEGORY_NGS_BASIC(), proto);
265     DomainFactory *localDomain = WorkflowEnv::getDomainRegistry()->getById(LocalDomainFactory::ID);
266     localDomain->registerEntry(new ExtractConsensusWorkerFactory());
267 }
268 
269 /************************************************************************/
270 /* ExtractConsensusWorkerPrompter */
271 /************************************************************************/
ExtractConsensusWorkerPrompter(Actor * actor)272 ExtractConsensusWorkerPrompter::ExtractConsensusWorkerPrompter(Actor *actor)
273     : PrompterBase<ExtractConsensusWorkerPrompter>(actor) {
274 }
275 
composeRichDoc()276 QString ExtractConsensusWorkerPrompter::composeRichDoc() {
277     QString algorithm = getParameter(ALGO_ATTR_ID).toString();
278     QString link = getHyperlink(ALGO_ATTR_ID, algorithm);
279     return tr("Extracts the consensus sequence from the incoming assembly using the %1 algorithm.").arg(link);
280 }
281 
282 }  // namespace LocalWorkflow
283 }  // namespace U2
284