Mantid
Loading...
Searching...
No Matches
RepoModel.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
10#include <utility>
11#include <vector>
12
14#include "MantidKernel/Logger.h"
15#include "MantidQtIcons/Icon.h"
16#include <QIcon>
17#include <QPixmap>
18
19#include <QCheckBox>
20#include <QDebug>
21#include <QDialogButtonBox>
22#include <QFormLayout>
23#include <QGridLayout>
24#include <QGroupBox>
25#include <QLabel>
26#include <QLineEdit>
27#include <QSettings>
28#include <QTextEdit>
29#include <QTextStream>
30#include <QVBoxLayout>
31#include <QtConcurrentRun>
32#include <stdexcept>
33
34using namespace MantidQt::API;
37
38namespace {
40Mantid::Kernel::Logger g_log("RepoModel");
41} // namespace
42
43// flag to indicate that the thread is delete thread
44const char *delete_mark = "*DELETE*";
45const char *nofile_flag = "nofile";
46
49static QString download_thread(Mantid::API::ScriptRepository_sptr &pt, const std::string &path) {
50 QString result("");
51 try {
52 pt->download(path);
54 QString info = QString::fromStdString(ex.what());
55 // make the exception a nice html message
56 info.replace("\n", "</p><p>");
57 return info;
58 }
59 return result;
60}
61
64static QString upload_thread(Mantid::API::ScriptRepository_sptr &pt, const std::string &path, const QString &email,
65 const QString &author, const QString &comment) {
66 try {
67 pt->upload(path, comment.toStdString(), author.toStdString(), email.toStdString());
69 QString info = QString::fromStdString(ex.what());
70 info.replace("\n", "</p><p>");
71 return info;
72 }
73 return QString();
74}
77static QString delete_thread(Mantid::API::ScriptRepository_sptr &pt, const std::string &path, const QString &email,
78 const QString &author, const QString &comment) {
79 try {
80 pt->remove(path, comment.toStdString(), author.toStdString(), email.toStdString());
82 QString info = QString::fromStdString(ex.what());
83 info.replace("\n", "</p><p>");
84 // it adds the mark *DELETE* so to recognize that it was used to delete an
85 // entry.
86 return info + delete_mark;
87 }
88 return delete_mark;
89}
90
91/*
92 An auxiliary nested class to help RepoModel to rebuild the hierarchical
93 tree of ScriptRepository.
94
95 It will keep track of the path that is the main key to access metadata
96 on ScriptRepository and an auxiliary label to easy the display on a
97 user nicer way.
98
99*/
100RepoModel::RepoItem::RepoItem(QString label, QString path, RepoItem *parent)
101 : m_label(std::move(label)), keypath(std::move(path)), parentItem(parent) {}
103RepoModel::RepoItem::~RepoItem() { qDeleteAll(childItems); }
107void RepoModel::RepoItem::appendChild(RepoItem *child) { childItems.append(child); }
108
120RepoModel::RepoItem *RepoModel::RepoItem::child(int row) const { return childItems.value(row); }
121
125int RepoModel::RepoItem::childCount() const { return static_cast<int>(childItems.count()); }
130 if (parentItem)
131 return static_cast<int>(parentItem->childItems.indexOf(const_cast<RepoItem *>(this)));
132 return 0;
133}
137 if (row < 0 || row >= childCount())
138 return false;
139
140 childItems.removeAt(row);
141 return true;
142}
143
145// MODEL
147
154RepoModel::RepoModel(QObject *parent) : QAbstractItemModel(parent) {
155 const ConfigServiceImpl &config = ConfigService::Instance();
156 repo_path = QString::fromStdString(config.getString("ScriptLocalRepository"));
157 rootItem = new RepoItem("/");
161 repo_ptr = ScriptRepositoryFactory::Instance().create("ScriptRepositoryImpl");
162 connect(&download_watcher, SIGNAL(finished()), this, SLOT(downloadFinished()));
163 connect(&upload_watcher, SIGNAL(finished()), this, SLOT(uploadFinished()));
167}
168
212QVariant RepoModel::data(const QModelIndex &index, int role) const {
213 using namespace Mantid::API;
214 if (!index.isValid())
215 return QVariant();
216 const auto *item = static_cast<RepoItem *>(index.internalPointer());
217 try {
218 const QString &path = item->path();
221 // return the data for the display role
222 if (role == Qt::DisplayRole) {
223 switch (index.column()) {
224 case 0: // return the label (the path of the file/folder)
225 return item->label();
226 break;
227 case 1: // ask for the status
228 if (isDownloading(index))
229 return downloadSt();
230 if (isUploading(index))
231 return uploadSt();
232 status = repo_ptr->fileStatus(path.toStdString());
233 return fromStatus(status);
234 break;
235 case 2: // autoupdate option
236 status = repo_ptr->fileStatus(path.toStdString());
237 if (status == REMOTE_ONLY || status == LOCAL_ONLY)
238 return QVariant();
239 inf = repo_ptr->fileInfo(path.toStdString());
240 return inf.auto_update ? QString("true") : QString("false");
241 break;
242 case 3: // delete action
243 inf = repo_ptr->fileInfo(path.toStdString());
244 if (inf.directory)
245 return PROTECTEDENTRY;
246 status = repo_ptr->fileStatus(path.toStdString());
247 if (!(status == LOCAL_CHANGED || status == BOTH_UNCHANGED))
248 return PROTECTEDENTRY;
249 return DELETABLEENTRY;
250 break;
251 }
252 }
253
254 // return the data for the DecorationRole
255 if (role == Qt::DecorationRole) {
256 if (index.column() == 0) {
257 inf = repo_ptr->fileInfo(path.toStdString());
258 if (inf.directory) {
259 status = repo_ptr->fileStatus(path.toStdString());
260 if (status == Mantid::API::REMOTE_ONLY) {
261 return Icons::getIcon("mdi.folder-network-outline", "black", 1.2);
262 } else
263 return Icons::getIcon("mdi.folder-open-outline", "black", 1.2);
264 } else {
265 int pos = static_cast<int>(QString(path).lastIndexOf('.'));
266 if (pos < 0)
267 return Icons::getIcon("mdi.file-question", "black", 1.2);
268 if (path.contains("readme", Qt::CaseInsensitive))
269 return Icons::getIcon("mdi.file-document-outline", "black", 1.2);
270
271 QString extension = QString(path).remove(0, pos);
272 if (extension == ".py" || extension == ".PY")
273 return Icons::getIcon("mdi.language-python", "black", 1.2);
274 else if (extension == ".ui")
275 return Icons::getIcon("mdi.file-document-box-outline", "black", 1.2);
276 else if (extension == ".docx" || extension == ".doc" || extension == ".odf")
277 return Icons::getIcon("mdi.file-outline", "black", 1.2);
278 else if (extension == ".pdf")
279 return Icons::getIcon("mdi.file-pdf-outline", "black", 1.2);
280 else
281 return Icons::getIcon("mdi.file-question", "black", 1.2);
282 }
283 }
284 } // end decorationRole
285
286 // tool tip role
287 if (role == Qt::ToolTipRole) {
288 if (index.column() == 1) {
289 if (isDownloading(index))
290 return "Downloading... Be patient.";
291 if (isUploading(index))
292 return "Uploading... Be patient.";
293 status = repo_ptr->fileStatus(path.toStdString());
294 inf = repo_ptr->fileInfo(path.toStdString());
295 switch (status) {
296
297 case REMOTE_ONLY:
298 return (inf.directory) ? "Click here to download this folder and all its files"
299 : "Click here to download this file";
300 break;
301 case BOTH_UNCHANGED:
302 return (inf.directory) ? "This folder is up-to-date" : "This file is up-to-date";
303 break;
304 case LOCAL_CHANGED:
305 return "Click here to publish your changes";
306 case REMOTE_CHANGED:
307 return (inf.directory) ? "There is a new version of the files inside "
308 "this folder. Click here to install them."
309 : "There is a new version of this file "
310 "available. Click here to install it.";
311 case BOTH_CHANGED:
312 return (inf.directory) ? "Files in this folder may have changed both locally and "
313 "remotely.\nClick here to install the remote version, "
314 "a backup of the local version will also be created."
315 : "This file may have changed both locally and "
316 "remotely.\nClick here to install the remote version, "
317 "a backup of the local version will also be created.";
318 break;
319 case LOCAL_ONLY:
320 return "Click here to share this file with the Mantid community!";
321 }
322 } else if (index.column() == 2) {
323 return "Enable or disable this item to be downloaded automatically "
324 "when new versions will be available";
325 } else if (index.column() == 3) {
326 if (isUploading(index))
327 return "Connection busy... Be patient.";
328 inf = repo_ptr->fileInfo(path.toStdString());
329 if (inf.directory)
330 return QVariant();
331 status = repo_ptr->fileStatus(path.toStdString());
332 if (!(status == LOCAL_CHANGED || status == BOTH_UNCHANGED))
333 return QVariant();
334 return "Click here to delete this file from the Central Repository";
335 }
336 } // end tool tip
338 handleExceptions(ex, "", false);
339 }
340 return QVariant();
341}
342
349 using namespace Mantid::API;
350 switch (status) {
351 case BOTH_UNCHANGED:
352 return updatedSt();
353 case REMOTE_ONLY:
354 return remoteOnlySt();
355 case LOCAL_ONLY:
356 return localOnlySt();
357 case REMOTE_CHANGED:
358 return remoteChangedSt();
359 case LOCAL_CHANGED:
360 return localChangedSt();
361 case BOTH_CHANGED:
362 return bothChangedSt();
363 }
364 return bothChangedSt();
365}
366
384bool RepoModel::setData(const QModelIndex &index, const QVariant &value, int role) {
385 if (!index.isValid())
386 return false;
387 if (role != Qt::EditRole)
388 // only EditRole is acceptable for the role
389 return false;
390 if (index.column() == 0)
391 // the path can not be changed
392 return false;
393 int count_changed = 0;
394 const auto *item = static_cast<RepoItem *>(index.internalPointer());
395 std::string path = item->path().toStdString();
396
397 bool ret = false;
398 // get the action
399 QString action = value.toString();
400 if (index.column() == 2) { // set auto update
401 bool option;
402 if (action == "setTrue")
403 option = true;
404 else if (action == "setFalse")
405 option = false;
406 else
407 return false; // only setTrue and setFalse are allowed values for set auto
408 // update.
409 count_changed = repo_ptr->setAutoUpdate(path, option);
410 ret = true;
411 }
412
413 if (index.column() == 1) { // trigger actions: Download and Upload
414 if (action == "Download") {
415 if (!download_threads.isFinished()) {
416 QWidget *father = qobject_cast<QWidget *>(QObject::parent());
417 QMessageBox::information(father, "Wait", "Downloading... ");
418 return false;
419 }
420 downloading_path = QString::fromStdString(path);
422 emit executingThread(true);
423 // Wrap in a lambda: Qt6 QtConcurrent::run mis-deduces a bare function pointer with
424 // by-reference args as the QPromise overload. The lambda works for both Qt5 and Qt6.
425 // repo_ptr is a member, so copy it to a local for capture.
426 auto repo = repo_ptr;
427 download_threads = QtConcurrent::run([repo, path]() mutable { return download_thread(repo, path); });
429 ret = true;
430 } else if (action == "Upload") {
431 if (!upload_threads.isFinished()) {
432 QWidget *father = qobject_cast<QWidget *>(QObject::parent());
433 QMessageBox::information(father, "Wait", "Uploading... ");
434 return false;
435 }
436
437 QWidget *father = qobject_cast<QWidget *>(QObject::parent());
438 if (repo_ptr->fileInfo(path).directory) {
439 QMessageBox::information(father, "Not Supported",
440 "The current version does not support "
441 "uploading recursively. Please, upload "
442 "one-by-one");
443 return false;
444 };
445
446 auto *form = new UploadForm(QString::fromStdString(path), father);
447 QSettings settings;
448 settings.beginGroup("Mantid/ScriptRepository");
449 QString email = settings.value("UploadEmail", QString()).toString();
450 QString uploadAuthor = settings.value("UploadAuthor", QString()).toString();
451 bool lastChk = settings.value("UploadSaveInfo", false).toBool();
452 if (!email.isEmpty())
453 form->setEmail(email);
454 if (!uploadAuthor.isEmpty())
455 form->setAuthor(uploadAuthor);
456 form->lastSaveOption(lastChk);
457 if (form->exec()) {
458 settings.setValue("UploadEmail", form->saveInfo() ? form->email() : "");
459 settings.setValue("UploadAuthor", form->saveInfo() ? form->author() : "");
460 settings.setValue("UploadSaveInfo", form->saveInfo());
461
462 qDebug() << "Uploading... " << QString::fromStdString(path) << form->comment() << form->author()
463 << form->email() << '\n';
464 uploading_path = QString::fromStdString(path);
466 emit executingThread(true);
467 auto repo = repo_ptr;
468 upload_threads = QtConcurrent::run(
469 [repo, path, email = form->email(), author = form->author(), comment = form->comment()]() mutable {
470 return upload_thread(repo, path, email, author, comment);
471 });
473 ret = true;
474 } else {
475 ret = false;
476 }
477 settings.endGroup();
478 delete form;
479 }
480 }
481
482 if (index.column() == 3) { // trigger actions: delete
483 using namespace Mantid::API;
484 if (action != "delete")
485 return false;
486 // used to show qwidgets
487 QWidget *father = qobject_cast<QWidget *>(QObject::parent());
488
489 SCRIPTSTATUS status = repo_ptr->fileStatus(path);
490
491 /* We do not remove files directly from the central repository, but,
492 usually,
493 this option is not available from the GUI (no button), so, just return
494 false*/
495 if (!(status == LOCAL_CHANGED || status == BOTH_UNCHANGED))
496 return false;
497
498 // it requires a new connection to the uploader server
499 if (!upload_threads.isFinished()) {
500 QWidget *mother = qobject_cast<QWidget *>(QObject::parent());
501 QMessageBox::information(mother, "Wait",
502 "The connection with the server "
503 "is busy now, wait a while and "
504 "try again. ");
505 return false;
506 }
507 // query the user if he wants to delete only locally or remote as well.
508 auto box = DeleteQueryBox(QString::fromStdString(path), father);
509
510 if (box.exec() != QMessageBox::Yes) {
511 // the user gave up deleting this entry, release memory
512 return false;
513 }
514
515 // get the options from the user
516 QString comment(box.comment());
517
518 // remove from central repository
519 // currently, directories can not be deleted recursively
520 if (repo_ptr->fileInfo(path).directory) {
521 QMessageBox::information(father, "Not Supported",
522 "The current version does not support deleting "
523 "from the central repository recursively. "
524 "Please, delete one-by-one");
525 return false;
526 };
527
528 // check if the reason was given and it is valid
529 if (comment.isEmpty()) {
530 QMessageBox::information(father, "Not Allowed", "You are not allowed to delete one file without a reason");
531 return false;
532 }
533
534 // we will not allow them to remove if they have no e-mail and author saved
535 QSettings settings;
536 settings.beginGroup("Mantid/ScriptRepository");
537 QString email = settings.value("UploadEmail", QString()).toString();
538 QString uploadAuthor = settings.value("UploadAuthor", QString()).toString();
539 settings.endGroup();
540
541 if (uploadAuthor.isEmpty() || email.isEmpty()) {
542 QMessageBox::information(father, "You have not uploaded this file",
543 "You are not allowed to remove files that you "
544 "have not updloaded through ScriptRepository");
545 return false;
546 }
547
548 // we have all we need to delete from the central repository
549 // execute the delete in a separate thread, we will use the upload
550 // established way, because,
551 // it will connect to the same server to delete.
553 uploading_path = QString::fromStdString(path);
554 emit executingThread(true);
555 auto repo = repo_ptr;
556 upload_threads = QtConcurrent::run([repo, path, email, uploadAuthor, comment]() mutable {
557 return delete_thread(repo, path, email, uploadAuthor, comment);
558 });
560 ret = true;
561 } // end delete action
562
563 if (ret)
564 emit dataChanged(index, this->index(count_changed, 0, index));
565
566 return ret;
567}
568
576Qt::ItemFlags RepoModel::flags(const QModelIndex &index) const {
577 if (!index.isValid())
578 return {};
579 if (index.column() == 0)
580 return QAbstractItemModel::flags(index);
581 // define that setData will accept the EditRole.
582 return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
583}
584
599QVariant RepoModel::headerData(int section, Qt::Orientation orientation, int role) const {
600 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
601 switch (section) {
602 case 0:
603 return "Path";
604 case 1:
605 return "Status";
606 case 2:
607 return "AutoUpdate";
608 case 3:
609 return "Delete";
610 default:
611 return QVariant();
612 }
613 }
614 return QVariant();
615}
616
631QModelIndex RepoModel::index(int row, int column, const QModelIndex &parent) const {
632 // check if the row and column are allowed,
633 // for example, it will not accept column == 3, or row = 1
634 // for parent that refers to file and not to folder.
635 if (!hasIndex(row, column, parent))
636 return QModelIndex();
637 // retrieve the pointer ot the RepoItem from the parent
638 const RepoItem *parentItem;
639 if (!parent.isValid())
640 parentItem = rootItem;
641 else
642 parentItem = static_cast<RepoItem *>(parent.internalPointer());
643
644 // given the row, we can find the childItem from the RepoItem::child method.
645 RepoItem *childItem = parentItem->child(row);
646
647 if (childItem)
648 return createIndex(row, column, childItem);
649 else
650 return QModelIndex();
651}
652
658QModelIndex RepoModel::parent(const QModelIndex &index) const {
659 if (!index.isValid())
660 return QModelIndex();
661 // the child is the RepoItem pointed by the index.
662 auto *childItem = static_cast<RepoItem *>(index.internalPointer());
663 // the parent is the parent of the RepoItem.
664 RepoItem *parentItem = childItem->parent();
665 // the root item does not have a parent
666 if (parentItem == rootItem)
667 return QModelIndex();
668 // create the index and return
669 return createIndex(parentItem->row(), 0, parentItem);
670}
677int RepoModel::rowCount(const QModelIndex &parent) const {
678 const RepoItem *parentItem;
679
680 if (parent.column() > 0)
681 return 0; // there are rows defined only of the column 0
682
683 if (!parent.isValid())
684 parentItem = rootItem;
685 else
686 parentItem = static_cast<RepoItem *>(parent.internalPointer());
687 // return the number of children
688 return parentItem->childCount();
689}
690
697int RepoModel::columnCount(const QModelIndex & /*parent*/) const { return 4; }
698
701QString RepoModel::fileDescription(const QModelIndex &index) {
702 const auto *item = static_cast<RepoItem *>(index.internalPointer());
703 if (!item)
704 return "";
705 QString desc;
706 try {
707 desc = QString::fromStdString(repo_ptr->description(item->path().toStdString()));
708 } catch (...) {
709 // just ignore
710 }
711 return desc;
712}
713
714QString RepoModel::author(const QModelIndex &index) {
715 const auto *item = static_cast<RepoItem *>(index.internalPointer());
716 QString author = "Not defined";
717 if (!item)
718 return author;
719 try {
720 author = QString::fromStdString(repo_ptr->info(item->path().toStdString()).author);
721 } catch (...) {
722 // just ignore
723 }
724 return author;
725}
731QString RepoModel::filePath(const QModelIndex &index) {
732 const auto *item = static_cast<RepoItem *>(index.internalPointer());
733 // qDebug() << "Get file path from : " << item->path()<< '\n';
734 Mantid::API::SCRIPTSTATUS state = repo_ptr->fileStatus(item->path().toStdString());
735
736 if (state == Mantid::API::REMOTE_ONLY)
737 return "";
738 Mantid::API::ScriptInfo info = repo_ptr->fileInfo(item->path().toStdString());
739 if (info.directory)
740 return "";
741 QString path = repo_path + "/" + item->path();
742 return path;
743}
744
765 // in order to speed the algorithm, check if the
766 // folder is the same of the last folder.
767 if (parents.last()->path() == folder)
768 return parents.last();
769
770 // try to find this
771 if (folder.isEmpty())
772 return parents.first(); // the parents first will always contain the root
773
774 // it will iterate through all the parents of the given folder, in order to
775 // create any folder that has not been created.
776 QStringList folder_parts = folder.split("/");
777 QString aux_folder;
778 RepoItem *father = parents.first();
779 // there is no reason to try to find entry A/B/C if the entry A/B was not
780 // found
781 bool try_to_find = true;
782
783 for (int i = 0; i < folder_parts.size(); i++) {
784 if (i == 0)
785 aux_folder = folder_parts[i];
786 else
787 aux_folder += "/" + folder_parts[i];
788
789 bool found = false;
790
791 if (try_to_find) {
792 // this means that the previous folders were found
793 const auto it = std::find_if(parents.cbegin(), parents.cend(),
794 [&aux_folder](const auto &parent) { return parent->path() == aux_folder; });
795 if (it != parents.cend()) {
796 found = true;
797 father = *it;
798 }
799 }
800 // there is not RepoItem related to the current folder,
801 // create it
802 if (!found) {
803 RepoItem *m = new RepoItem(folder_parts[i], aux_folder, father);
804 father->appendChild(m);
805 parents.append(m);
806 father = m;
807 try_to_find = false;
808 }
809 }
810 return father;
811}
812
825 // check server for updates to repository
826 repo_ptr->check4Update();
827 // get the list of entries inside the scriptrepository
828 std::vector<std::string> list = repo_ptr->listFiles();
829
830 // auxiliary list of pointers to repoitem that are related to folders
831 QList<RepoItem *> parents;
832 // the first one will always be the root
833 parents << root;
834
835 // FOREACH entry in LISTFILES
836 for (const auto &number : list) {
837 // folder or file inside the repository
838 QString lineData = QString::fromStdString(number);
839
840 // Read the column data from the rest of the line.
841 QStringList pathStrings = lineData.split("/");
842 // separate the folder and the current entry (folder or file)
843 QString current_file = pathStrings.last();
844 QString folder = "";
845 pathStrings.removeLast();
846 if (pathStrings.size() > 0)
847 folder = pathStrings.join("/");
848
849 // get parent for this entry
850 RepoItem *parentOfFolder = getParent(folder, parents);
851 // a new folder has started
852 if (parentOfFolder == root) {
853 // this test is just for the sake of performance, to reduce the numbers of
854 // parents
855 parents.clear();
856 parents << root;
857 }
858
859 // check if the current entry is a directory
860 if (repo_ptr->info(lineData.toStdString()).directory) {
861 // directories will be appended to parents list
862 RepoItem *aux = new RepoItem(current_file, lineData, parentOfFolder);
863 parentOfFolder->appendChild(aux);
864 parents << aux;
865 } else {
866 // files will just be created and appended to the parent
867 parentOfFolder->appendChild(new RepoItem(current_file, lineData, parentOfFolder));
868 }
869 }
870}
871
873 bool showWarning) const {
874 g_log.information() << "Download failed " << ex.what() << "\n Detail: " << ex.systemError() << '\n';
875 if (showWarning) {
876
877 QWidget *father = qobject_cast<QWidget *>(QObject::parent());
878 QString info = QString::fromStdString(ex.what());
879 // make the exception a nice html message
880 info.replace("\n", "</p><p>");
881 QMessageBox::warning(father, title, QString("<html><body><p>%1</p></body></html>").arg(info));
882 }
883}
884
886 QString info = download_threads.result();
887 if (!info.isEmpty()) {
888 QMessageBox::warning(qobject_cast<QWidget *>(QObject::parent()), "Download Failed",
889 QString("<html><body><p>%1</p></body></html>").arg(info));
890 }
892 auto *repo_item = static_cast<RepoItem *>(download_index.internalPointer());
893 QModelIndex top_left = createIndex(0, 0, repo_item);
894 QModelIndex bottom_right = createIndex(0, 3, repo_item);
895 emit dataChanged(top_left, bottom_right);
896 emit executingThread(false);
897}
898
899bool RepoModel::isDownloading(const QModelIndex &index) const {
900 const auto *item = static_cast<RepoItem *>(index.internalPointer());
901 if (item)
902 return item->path() == downloading_path;
903 return false;
904}
905
907 QString info = upload_threads.result();
908 QString title = "Upload Failed";
909 if (info.contains(delete_mark)) {
910 info.replace(delete_mark, "");
911 title = "Delete Failed";
912 }
913
914 if (!info.isEmpty()) {
915 QMessageBox::warning(qobject_cast<QWidget *>(QObject::parent()), title,
916 QString("<html><body><p>%1</p></body></html>").arg(info));
917 }
918
920 auto *repo_item = static_cast<RepoItem *>(upload_index.internalPointer());
921 QModelIndex top_left = createIndex(0, 0, repo_item);
922 QModelIndex bottom_right = createIndex(0, 3, repo_item);
923 emit dataChanged(top_left, bottom_right);
924 emit executingThread(false);
925}
926
927bool RepoModel::isUploading(const QModelIndex &index) const {
928 const auto *item = static_cast<RepoItem *>(index.internalPointer());
929 if (item)
930 return item->path() == uploading_path;
931 return false;
932}
933
935const QString &RepoModel::localOnlySt() { return LOCALONLY; }
937const QString &RepoModel::remoteOnlySt() { return REMOTEONLY; }
939const QString &RepoModel::localChangedSt() { return LOCALCHANGED; }
941const QString &RepoModel::remoteChangedSt() { return REMOTECHANGED; }
943const QString &RepoModel::updatedSt() { return BOTHUNCHANGED; }
945const QString &RepoModel::bothChangedSt() { return BOTHCHANGED; }
947const QString &RepoModel::downloadSt() { return DOWNLOADST; }
949const QString &RepoModel::uploadSt() { return UPLOADST; }
950
951RepoModel::UploadForm::UploadForm(const QString &file2upload, QWidget *parent) : QDialog(parent) {
952 author_le = new QLineEdit();
953 email_le = new QLineEdit();
954 save_ck = new QCheckBox("Save your personal information");
955 save_ck->setToolTip("The author and email will be saved and will be written "
956 "to you next time");
957 comment_te = new QTextEdit();
958
959 // setup the layout
960
961 auto *personalGroupBox = new QGroupBox("Personal Group Box");
962 auto *personalLayout = new QFormLayout();
963 personalLayout->addRow("Author", author_le);
964 personalLayout->addRow("Email", email_le);
965 auto *gpBox = new QVBoxLayout();
966 gpBox->addWidget(save_ck);
967 gpBox->addLayout(personalLayout);
968 personalGroupBox->setLayout(gpBox);
969
970 QLabel *cmLabel = new QLabel("Comment");
971 auto *buttonBox = new QDialogButtonBox();
972 buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
973
974 auto *layout = new QVBoxLayout();
975 layout->addWidget(personalGroupBox);
976 layout->addWidget(cmLabel);
977 layout->addWidget(comment_te);
978 layout->addWidget(buttonBox);
979 setLayout(layout);
980
981 setWindowTitle(QString("Upload - %2").arg(file2upload));
982 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
983 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
984}
986QString RepoModel::UploadForm::email() { return email_le->text(); }
987QString RepoModel::UploadForm::author() { return author_le->text(); }
988QString RepoModel::UploadForm::comment() { return comment_te->toPlainText(); }
989bool RepoModel::UploadForm::saveInfo() { return save_ck->isChecked(); }
990void RepoModel::UploadForm::setEmail(const QString &email) { email_le->setText(email); }
991void RepoModel::UploadForm::setAuthor(const QString &author) { author_le->setText(author); }
993 save_ck->setCheckState(option ? Qt::Checked : Qt::Unchecked);
994}
995
997 : QMessageBox(QMessageBox::Question, "Delete file", "", QMessageBox::Yes | QMessageBox::No, parent) {
998 using namespace Mantid::API;
999 QString info_str;
1000 QTextStream info(&info_str);
1001
1002 info << "<html><head/><body><p>Are you sure you want to delete this file "
1003 "from the Repository?</p><p align=\"center\"><span style=\" "
1004 "font-style:italic;\">"
1005 << path << "</span></p></body></html>";
1006
1007 // creation of the new widgets
1008 comment_te = nullptr;
1009
1010 setText(info_str);
1011
1012 QGridLayout *_lay = qobject_cast<QGridLayout *>(layout());
1013 if (_lay) {
1014 QLayoutItem *buttons = _lay->takeAt(_lay->count() - 1);
1015 QLabel *la = new QLabel("Please, give the reason for deleting:", this);
1016 comment_te = new QTextEdit(this);
1017 comment_te->setMaximumHeight(70);
1018 _lay->addWidget(la, _lay->rowCount(), 0, 1, -1);
1019 _lay->addWidget(comment_te, _lay->rowCount(), 0, 2, -1);
1020 _lay->addItem(buttons, _lay->rowCount(), 0, 1, -1);
1021 }
1022}
1023
1026 if (comment_te)
1027 return comment_te->toPlainText();
1028 else
1029 return QString();
1030}
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
static QString download_thread(Mantid::API::ScriptRepository_sptr &pt, const std::string &path)
Executes the download from ScriptRepository.
Definition RepoModel.cpp:49
const char * nofile_flag
Definition RepoModel.cpp:45
const char * delete_mark
Definition RepoModel.cpp:44
static QString upload_thread(Mantid::API::ScriptRepository_sptr &pt, const std::string &path, const QString &email, const QString &author, const QString &comment)
Execute the upload from ScriptRepository.
Definition RepoModel.cpp:64
static QString delete_thread(Mantid::API::ScriptRepository_sptr &pt, const std::string &path, const QString &email, const QString &author, const QString &comment)
Execute the remove from ScriptRepository.
Definition RepoModel.cpp:77
Auxiliary Dialog to get the option from the user about removing the entries from the local folder or ...
Definition RepoModel.h:153
DeleteQueryBox(const QString &path, QWidget *parent=nullptr)
A nested class to help RepoModel to implement the QAbstractItemModel.
Definition RepoModel.h:87
RepoItem * parent() const
access to the parent of this entry
Definition RepoModel.h:109
bool removeChild(int row)
allow to remove a child, which allows erasing rows from the view.
void appendChild(RepoItem *child)
This method is the very responsible to allow the reconstruction of the hierarchical tree,...
int row() const
To which row this repoItem belongs?
RepoItem(QString label, QString path="/", RepoItem *parent=nullptr)
int childCount() const
Return the number of children that this entry may find.
const QString & path() const
access to the script repository path
Definition RepoModel.h:103
RepoItem * child(int row) const
Gives access to the row_th children of RepoItem.
~RepoItem()
destruct all the childItems.
UploadForm(const QString &file2upload, QWidget *parent=nullptr)
RepoModel(QObject *parent=nullptr)
constructor
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Count how many file/folders are direct children of the given folder, through the abstraction of QMode...
Qt::ItemFlags flags(const QModelIndex &index) const override
information on the available interaction
static const QString & localChangedSt()
QFutureWatcher< QString > upload_watcher
Definition RepoModel.h:240
Mantid::API::ScriptRepository_sptr repo_ptr
pointer to the ScriptRepository
Definition RepoModel.h:211
QString filePath(const QModelIndex &index)
Return the operative system file path if it exists.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
header strings
QModelIndex download_index
Definition RepoModel.h:226
QFutureWatcher< QString > download_watcher
Definition RepoModel.h:225
bool isUploading(const QModelIndex &index) const
QString author(const QModelIndex &index)
bool isDownloading(const QModelIndex &index) const
static const QString & remoteChangedSt()
QFuture< QString > upload_threads
Definition RepoModel.h:237
QString repo_path
ScriptLocalRepository path, to be able to retrieve the absolute path.
Definition RepoModel.h:213
int columnCount(const QModelIndex &parent=QModelIndex()) const override
provide the number of the columns
const QString & fromStatus(Mantid::API::SCRIPTSTATUS status) const
auxiliary method to match the ScriptStatus to string
static const QString & updatedSt()
static const QString & uploadSt()
static const QString & bothChangedSt()
RepoItem * rootItem
pointer to the RepoItem root
Definition RepoModel.h:209
RepoItem * getParent(const QString &folder, QList< RepoItem * > &parents)
auxiliary method to help populating the model
QVariant data(const QModelIndex &index, int role) const override
access to the ScriptRepository data
void handleExceptions(const Mantid::API::ScriptRepoException &ex, const QString &title, bool showWarning=true) const
auxiliary method to deal with exceptions
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
change data
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
access to the index
QFuture< QString > download_threads
Definition RepoModel.h:224
QModelIndex parent(const QModelIndex &index) const override
access to parent
QString fileDescription(const QModelIndex &index)
Return the description of the file for a defined entry.
static const QString & localOnlySt()
void setupModelData(RepoItem *parent)
auxiliary method to populate the model
static const QString & downloadSt()
static const QString & remoteOnlySt()
~RepoModel() override
destructor
The ScriptRepository class is intended to be used mainly by the users, who will be willing to share a...
const char * what() const noexcept override
Returns the message string.
const std::string & systemError() const
Returns the error description with technical details on the origin and cause.
The ConfigService class provides a simple facade to access the Configuration functionality of the Man...
std::string getString(const std::string &keyName, bool pathAbsolute=true) const
Searches for a configuration property.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
const QString LOCALCHANGED
Definition RepoModel.h:29
const QString REMOTECHANGED
Definition RepoModel.h:30
const QString DELETABLEENTRY
Definition RepoModel.h:36
const QString BOTHUNCHANGED
Definition RepoModel.h:31
const QString REMOTEONLY
Definition RepoModel.h:27
const QString LOCALONLY
Definition RepoModel.h:28
const QString UPLOADST
Definition RepoModel.h:33
const QString BOTHCHANGED
Definition RepoModel.h:32
const QString DOWNLOADST
Definition RepoModel.h:34
const QString PROTECTEDENTRY
Definition RepoModel.h:35
std::shared_ptr< ScriptRepository > ScriptRepository_sptr
shared pointer to the function base class
Mantid::Kernel::SingletonHolder< ScriptRepositoryFactoryImpl > ScriptRepositoryFactory
SCRIPTSTATUS
Represent the possible states for a given file:
Kernel::Logger g_log("DetermineSpinStateOrder")
Mantid::Kernel::SingletonHolder< ConfigServiceImpl > ConfigService
STL namespace.
Information about the files inside the repository.
bool auto_update
Marked for auto update.
bool directory
Directory Flag to indicate if the entry is a directory.