فهرست منبع

major work on refactor
some additions to the Task and Task argument interface, for dealing with files
partially modified client to deal with new interface

still remaining:
- deleting files from task upon deletion in argdialog ui
- handling of messages in Client to process queue appropriately

logicp 4 سال پیش
والد
کامیت
2062ff154c
8فایلهای تغییر یافته به همراه154 افزوده شده و 193 حذف شده
  1. 3 6
      include/client/client.hpp
  2. 2 0
      include/task/instagram_task.hpp
  3. 33 1
      include/task/task.hpp
  4. 2 18
      include/ui/argdialog.h
  5. 45 37
      src/argdialog.cpp
  6. 43 122
      src/client.cpp
  7. 23 0
      src/instagram_task.cpp
  8. 3 9
      src/mainwindow.cpp

+ 3 - 6
include/client/client.hpp

@@ -24,10 +24,7 @@ static constexpr int COMMANDS_UPDATE_TYPE = 2;
 static constexpr int EVENT_UPDATE_TYPE = 3;
 static constexpr int PROCESS_REQUEST_TYPE = 4;
 
-enum TaskType {
-    INSTAGRAM = 1,
-    OTHER = 2
-};
+using namespace Scheduler;
 
 namespace TaskCode {
 static constexpr int IGTASKBYTE = 0xFF;
@@ -72,7 +69,7 @@ class Client : public QDialog {
   QString getAppName(int mask);
   int getSelectedApp();
   // Move this to private after moving responsibilities to Client
-  void scheduleTask(Scheduler::Task* task, bool file_pending);
+  void scheduleTask(Scheduler::Task* task);
   MessageHandler createMessageHandler(std::function<void()> cb);
 
  public slots:
@@ -96,7 +93,7 @@ class Client : public QDialog {
   int argc;
   char** argv;
   int m_client_socket_fd;
-  std::vector<std::string> m_task;
+  Task* m_outbound_task;
   bool executing;
   bool file_was_sent;
   CommandMap m_commands;

+ 2 - 0
include/task/instagram_task.hpp

@@ -25,6 +25,8 @@ class InstagramTask : public Scheduler::Task {
   virtual const QVector<Scheduler::KFileData> getFiles() override;
   virtual Scheduler::TaskType getType() override;
   virtual void setArgument(QString name, Scheduler::TypeVariant arg) override;
+  virtual void setArgument(QString name, Scheduler::KFileData file) override;
+  virtual bool hasFiles() override;
   virtual bool isReady() override;
   virtual void clear() override;
   virtual void setDefaultValues() override;

+ 33 - 1
include/task/task.hpp

@@ -65,7 +65,7 @@ class Task;
 using TaskQueue = QQueue<Task*>;
 using ArgumentType = const char*;
 using ArgumentValues = QVector<const QString>;
-using TypeVariant = std::variant<bool, int, QString, QVector<QString>, std::vector<KFileData>>;
+using TypeVariant = std::variant<bool, int, QString, QVector<QString>, QVector<KFileData>>;
 using TaskIterator = std::vector<std::unique_ptr<TaskArgumentBase>>::iterator;
 using TaskArguments = std::vector<std::unique_ptr<TaskArgumentBase>>;
 
@@ -76,7 +76,10 @@ class TaskArgumentBase {
  public:
   virtual const QString text() = 0;
   virtual const QString getStringValue() = 0;
+  virtual uint8_t getTypeIndex();
   virtual TypeVariant getValue() = 0;
+  virtual void insert(QString value) = 0;
+  virtual void insert(KFileData file) = 0;
   virtual void setValue(TypeVariant v) = 0;
   virtual bool isContainer() = 0;
   virtual void clear() = 0;
@@ -170,6 +173,32 @@ class TaskArgument : TaskArgumentBase {
     return (isIndex(value.index(), VariantIndex::STRVEC) || isIndex(value.index(), VariantIndex::FILEVEC));
   }
 
+  /**
+   * @brief insert
+   * @param value
+   */
+  virtual void insert(QString value) override {
+
+  }
+
+  /**
+   * @brief insert
+   * @param file
+   */
+  virtual void insert(KFileData file) override {
+    if (value.index() == VariantIndex::FILEVEC) {
+      std::get<VariantIndex::FILEVEC>(value).push_back(file);
+    }
+  }
+
+  /**
+   * @brief getTypeIndex
+   * @return
+   */
+  virtual uint8_t getTypeIndex() override {
+    return value.index();
+  }
+
  private:
   QString name;
   ArgumentType type;
@@ -185,6 +214,7 @@ class Task {
   Task(KFileData);
   Task(QVector<KFileData>);
   virtual void setArgument(QString name, TypeVariant arg) = 0;
+  virtual void setArgument(QString name, Scheduler::KFileData file) = 0;
   virtual const TaskArguments getTaskArguments() = 0;
   virtual TypeVariant getTaskArgument(QString name) = 0;
   virtual ArgumentValues getArgumentValues() = 0;
@@ -192,6 +222,8 @@ class Task {
   virtual void defineTaskArguments() = 0;
   virtual void setDefaultValues() = 0;
   virtual const QVector<KFileData> getFiles() = 0;
+  virtual void addFile(KFileData file) = 0;
+  virtual bool hasFiles() = 0;
   virtual bool isReady() = 0;
   virtual void clear() = 0;
   virtual ~Task(){};

+ 2 - 18
include/ui/argdialog.h

@@ -9,29 +9,15 @@
 #include <QMessageBox>
 #include <QPushButton>
 #include <headers/util.hpp>
+#include <include/task/instagram_task.hpp>
 #include <include/task/task.hpp>
 #include <string_view>
 #include <unordered_map>
 
 using namespace Scheduler;
 
-namespace Args {
-const QString HEADER_TYPE = "header";
-const QString DESCRIPTION_TYPE = "description";
-const QString HASHTAG_TYPE = "hashtag";
-const QString PROMOTE_TYPE = "promote/share";
-const QString LINK_BIO_TYPE = "link/bio";
-const QString REQUESTED_BY_TYPE = "requested by";
-}  // namespace Args
-
 typedef std::string Str;
 
-typedef struct KFile {
-  QString name;
-  QString path;
-  FileType type;
-} KFile;
-
 typedef struct IGPost {
   std::string header = "Learn to speak like native Korean speakers 🙆‍♀️🇰🇷";
   std::string description;
@@ -41,7 +27,6 @@ typedef struct IGPost {
   std::vector<std::string> hashtags;
   std::vector<std::string> requested_by;
   const char *requested_by_phrase = "The phrase was requested by ";
-  std::vector<KFile> files;
   std::string user;
   bool is_video;
   bool isReady() {
@@ -69,8 +54,7 @@ class ArgDialog : public QDialog {
   ~ArgDialog();
 
  signals:
-  void uploadFiles(QVector<KFileData> files);
-  void taskRequestReady(Task task, bool file_pending);
+  void taskRequestReady(Scheduler::Task *task);
 
  private:
   void clearPost();

+ 45 - 37
src/argdialog.cpp

@@ -15,36 +15,60 @@ ArgDialog::ArgDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ArgDialog),
 
   ui->argCommandButtons->button(QDialogButtonBox::Close)
       ->setStyleSheet(QString("background:%1").arg("#2f535f"));
+
   QObject::connect(ui->addFile, &QPushButton::clicked, this, [this]() {
     KFileDialog file_dialog{};
     auto file_path = file_dialog.openFileDialog(m_file_path);
     qDebug() << "Selected file:" << file_path;
+
     if (file_path.size() > 0) {
       auto slash_index = file_path.lastIndexOf("/") + 1;
       QString file_name = file_path.right(file_path.size() - slash_index);
       QString dir = file_path.left(slash_index);
-      qDebug() << "Dir is " << dir;
-      QMimeDatabase db;
-      auto is_video = db.mimeTypeForFile(file_path).name().contains("video");
-      addItem(file_name, "file");
-      m_ig_post.files.push_back(
-          KFile{.name = file_name, .path = file_path, .type = is_video ? FileType::VIDEO : FileType::IMAGE});
+      QFile file(file_path);
+
+      if (file.open(QIODevice::ReadOnly)) {
+        QMimeDatabase db;
+        auto is_video = db.mimeTypeForFile(file_path).name().contains("video");
+        addItem(file_name, "file");
+        m_task->setArgument("files", Scheduler::KFileData{
+                                         .name = file_name,
+                                         .type = is_video ? FileType::VIDEO : FileType::IMAGE,
+                                         .path = file_path,
+                                         .bytes = file.readAll()});
 
-      if (is_video) {
-        qDebug() << "File discovered to be video";
-        m_ig_post.is_video = true; // rename to "sending_video"
-        QString preview_filename = FileUtils::generatePreview(file_path, file_name);
-        // TODO: create some way of verifying preview generation was successful
-        addFile("assets/previews/" + preview_filename);
-        addItem(preview_filename, "file");
-        addFile("assets/previews/" + preview_filename);
-        m_ig_post.files.push_back(KFile{.name = preview_filename,
-                                        .path = QCoreApplication::applicationDirPath()
-                                                + "/assets/previews/" + preview_filename,
-                                        .type = is_video ? FileType::VIDEO : FileType::IMAGE});
+        if (is_video) {
+          qDebug() << "File discovered to be video";
+          m_task->setArgument("is_video", true);
+          QString preview_filename = FileUtils::generatePreview(file_path, file_name);
+
+          auto preview_file_path = QCoreApplication::applicationDirPath()
+                                   + "/assets/previews/" + preview_filename;
+          file.setFileName(preview_file_path);
+          if (file.open(QIODevice::ReadOnly)) {
+            // TODO: create some way of verifying preview generation was successful
+            addFile("assets/previews/" + preview_filename);
+            addItem(preview_filename, "file");
+            addFile("assets/previews/" + preview_filename);
+            m_task->setArgument("files", Scheduler::KFileData{
+                                             .name = preview_filename,
+                                             .type = is_video ? FileType::VIDEO : FileType::IMAGE,
+                                             .path = preview_file_path,
+                                             .bytes = file.readAll()});
+          } else {
+            qDebug() << "Could not add preview image for video";
+            QMessageBox::warning(this, tr("File Error"), tr("Could not add preview image for video"));
+          }
+        } else {
+          addFile(file_path);
+        }
       } else {
-        addFile(file_path);
+        qDebug() << "Unable to open selected file";
+        QMessageBox::warning(this, tr("File Error"), tr("Unable to open selected file"));
       }
+    } else {
+      qDebug() << "Could not read the file path";
+      QMessageBox::warning(this, tr("File Error"), tr("Could not read the file path"));
     }
   });
 
@@ -100,25 +124,9 @@ ArgDialog::ArgDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ArgDialog),
                        &QDialogButtonBox::clicked),
                    this, [this](QAbstractButton *button) {
                      if (button->text() == "Save") {
-                       if (m_ig_post.isReady()) {
+                       if (m_task->isReady()) {
                          setTaskArguments();
-                         QVector<KFileData> k_file_v{};
-                         k_file_v.reserve(m_ig_post.files.size());
-
-                         for (const auto &kfile : m_ig_post.files) {
-                           QFile file(kfile.path);
-                           if (file.open(QIODevice::ReadOnly)) {
-                             k_file_v.push_back(KFileData{
-                                 .name = kfile.name, .type = kfile.type, .path = kfile.path, .bytes = file.readAll()});
-                           } else {
-                             QMessageBox::warning(this, tr("File Error"), tr("Unable to read file"));
-                           }
-                         }
-
-                         if (!k_file_v.empty()) {
-                           emit ArgDialog::uploadFiles(k_file_v);
-                         }
-                         emit ArgDialog::taskRequestReady(m_task, true);
+                         emit ArgDialog::taskRequestReady(m_task);
                        }
                        clearPost(); // reset m_ig_post to default values
                      }

+ 43 - 122
src/client.cpp

@@ -232,112 +232,43 @@ std::string getTaskFileInfo(std::vector<SentFile> files) {
 
 /**
  * @brief Client::sendTaskEncoded
- * @param [in] {TaskType}                 type The type of task
- * @param [in] {std::vector<std::string>} args The task arguments
- */
-void Client::sendTaskEncoded(TaskType type, std::vector<std::string> args) {
-    if (type == TaskType::INSTAGRAM) {
-      if (args.size() < 8) {
-        qDebug() << "Not enough arguments to send an IGTask";
-        return;
-      }
-        auto file_info = builder.CreateString(getTaskFileInfo(sent_files));
-        auto time = builder.CreateString(args.at(0).c_str(), args.at(0).size());
-        auto description = builder.CreateString(args.at(1).c_str(), args.at(1).size());
-        auto hashtags = builder.CreateString(args.at(2).c_str(), args.at(2).size());
-        auto requested_by = builder.CreateString(args.at(3).c_str(), args.at(3).size());
-        auto requested_by_phrase = builder.CreateString(args.at(4).c_str(), args.at(4).size());
-        auto promote_share = builder.CreateString(args.at(5).c_str(), args.at(5).size());
-        auto link_bio = builder.CreateString(args.at(6).c_str(), args.at(6).size());
-        auto is_video = args.at(7) == "1";
-        auto header = builder.CreateString(args.at(8).c_str(), args.at(8).size());
-        auto user = builder.CreateString(args.at(9).c_str(), args.at(9).size());
-
-        flatbuffers::Offset<IGTask> ig_task =
-            CreateIGTask(builder, 96, file_info, time, description, hashtags,
-                         requested_by, requested_by_phrase, promote_share,
-                         link_bio, is_video, 16, header, user);
-
-        builder.Finish(ig_task);
-
-        uint8_t* encoded_message_buffer = builder.GetBufferPointer();
-        uint32_t size = builder.GetSize();
-
-        uint8_t send_buffer[MAX_PACKET_SIZE];
-        memset(send_buffer, 0, MAX_PACKET_SIZE);
-        send_buffer[0] = (size >> 24) & 0xFF;
-        send_buffer[1] = (size >> 16) & 0xFF;
-        send_buffer[2] = (size >> 8) & 0xFF;
-        send_buffer[3] = size & 0xFF;
-        send_buffer[4] = (TaskCode::IGTASKBYTE & 0xFF);
-
-        std::memcpy(send_buffer + 5, encoded_message_buffer, size);
-        qDebug() << "Ready to send:";
-        std::string message_to_send{};
-        for (unsigned int i = 0; i < (size + 5); i++) {
-            message_to_send += (char)*(send_buffer + i);
-            qDebug() << (char)*(send_buffer + i);
-        }
-        qDebug() << "Final size: " << (size + 5);
-        // Send start operation
-        ::send(m_client_socket_fd, send_buffer, size + 5, 0);
-        builder.Clear();
-        sent_files.clear();
-        m_task.clear();
-        if (!m_task_queue.isEmpty()) {
-          auto task = m_task_queue.dequeue();
-          if (!task.files.empty() && !outgoing_files.empty()) {
-            qDebug() << "There are still outgoing files left over from last "
-                        "task which were never sent. They are being deleted";
-            outgoing_files.clear();
-          }
-          // We simply need to send files. Once the last file is sent, Client
-          // will check the value of m_task and send it to Server.
-          m_task = task.args;
-          sendFiles(task.files);
-        }
-    }
-}
-
-/**
- * @brief Client::sendTaskEncoded
- * @param [in] {TaskType}                 type The type of task
- * @param [in] {std::vector<std::string>} args The task arguments
+ * @param [in] {Scheduler::Task*} task The task arguments
  */
 void Client::sendTaskEncoded(Scheduler::Task* task) {
   if (task->getType() == Scheduler::TaskType::INSTAGRAM) {
-    auto file_info = builder.CreateString(getTaskFileInfo(sent_files));
-    auto time =
-        builder.CreateString(std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("datetime")).constData());
-    auto description = builder.CreateString(
-        std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("description")).constData());
-    auto hashtags =
-        builder.CreateString(std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("hashtags")).constData());
-    auto requested_by = builder.CreateString(
-        std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("requested_by")).constData());
-    auto requested_by_phrase = builder.CreateString(
-        std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("requested_by_phrase")).constData());
-    auto promote_share = builder.CreateString(
-        std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("promote_share")).constData());
-    auto link_bio = builder.CreateString(
-        std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("link_in_bio")).constData());
-    auto is_video = std::get<Scheduler::VariantIndex::BOOLEAN>(task->getTaskArgument("is_video"));
-    auto header =
-        builder.CreateString(std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("header")).constData());
-    auto user =
-        builder.CreateString(std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("user")).constData());
-
     flatbuffers::Offset<IGTask> ig_task =
-        CreateIGTask(builder, 96, file_info, time, description, hashtags, requested_by, requested_by_phrase,
-                     promote_share, link_bio, is_video, 16, header, user);
-
+        CreateIGTask(
+            builder,
+            96,
+            builder.CreateString(getTaskFileInfo(sent_files)),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("datetime")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("description")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("hashtags")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("requested_by_phrase")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("requested_by_phrase")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("promote_share")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("link_in_bio")).constData()),
+            std::get<Scheduler::VariantIndex::BOOLEAN>(task->getTaskArgument("is_video")),
+            std::get<Scheduler::VariantIndex::INTEGER>(task->getTaskArgument("mask")),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("header")).constData()),
+            builder.CreateString(
+                std::get<Scheduler::VariantIndex::QSTRING>(task->getTaskArgument("user")).constData()));
     builder.Finish(ig_task);
 
     uint8_t* encoded_message_buffer = builder.GetBufferPointer();
     uint32_t size = builder.GetSize();
-
     uint8_t send_buffer[MAX_PACKET_SIZE];
+
     memset(send_buffer, 0, MAX_PACKET_SIZE);
+
     send_buffer[0] = (size >> 24) & 0xFF;
     send_buffer[1] = (size >> 16) & 0xFF;
     send_buffer[2] = (size >> 8) & 0xFF;
@@ -349,26 +280,23 @@ void Client::sendTaskEncoded(Scheduler::Task* task) {
     std::string message_to_send{};
     for (unsigned int i = 0; i < (size + 5); i++) {
       message_to_send += (char)*(send_buffer + i);
-      qDebug() << (char)*(send_buffer + i);
     }
     qDebug() << "Final size: " << (size + 5);
     // Send start operation
     ::send(m_client_socket_fd, send_buffer, size + 5, 0);
+    // Cleanup and process queue
     builder.Clear();
     sent_files.clear();
-    m_task.clear();
+    m_outbound_task = nullptr;
     if (!m_task_queue.isEmpty()) {
-      auto task = m_task_queue.dequeue();
+      m_outbound_task = m_task_queue.dequeue();
       // TODO work from here
-      if (!task.files.empty() && !outgoing_files.empty()) {
+      if (m_outbound_task->hasFiles() && !outgoing_files.empty()) {
         qDebug() << "There are still outgoing files left over from last "
                     "task which were never sent. They are being deleted";
         outgoing_files.clear();
       }
-      // We simply need to send files. Once the last file is sent, Client
-      // will check the value of m_task and send it to Server.
-      m_task = task.args;
-      sendFiles(task.files);
+      sendFiles(m_outbound_task);
     }
   }
 }
@@ -530,24 +458,17 @@ void Client::execute() {
  * @param [in] {std::vector<std::string>} task_args The task arguments
  * @param [in] {bool}                     file_pending A boolean indicating whether there are files being sent for this task
  */
-void Client::scheduleTask(Scheduler::Task* task, bool file_pending) {
-  m_task_queue.enqueue(*task);
-  if (file_pending) {
-    // Will this be handled automatically?
-    // Previously, we would sometimes have an outgoing task with nothing but files, and would then be providing the
-    // arguments for it later. For this reason, we would update the task, which would be in the task queue
-    //    if (m_task.empty()) {
-    //      m_task = task_args;
-    //    } else {
-    //      if (!m_task_queue.empty() && m_task_queue.front().args.empty()) {
-    //        m_task_queue.front().args.assign(task_args.begin(), task_args.end());
-    //      } else {
-    //        qDebug() << "Could not identify the queued task for updating";
-    //      }
-    //    }
+void Client::scheduleTask(Scheduler::Task* task) {
+  if (m_outbound_task == nullptr) {
+    m_outbound_task = std::move(task);
+    if (m_outbound_task->hasFiles()) {
+      sendFiles(m_outbound_task);
+    } else {
+      qDebug() << "Requesting a task to be scheduled";
+      sendTaskEncoded(m_outbound_task);
+    }
   } else {
-    qDebug() << "Requesting a task to be scheduled";
-    sendTaskEncoded(task);
+    m_task_queue.enqueue(task);
   }
 }
 
@@ -566,7 +487,7 @@ void Client::sendFiles(Scheduler::Task* task) {
   } else {
     // TODO: place in queue and check queue after we finish scheduling the
     // task associated with the outgoing files
-    m_task_queue.enqueue(task->getFiles());
+    m_task_queue.enqueue(task);
     qDebug() << "Still attempting to send a different file";
   }
 }

+ 23 - 0
src/instagram_task.cpp

@@ -82,6 +82,21 @@ void InstagramTask::setArgument(QString name, TypeVariant value) {
   }
 }
 
+/**
+ * @brief InstagramTask::setArgument
+ * @param name
+ * @param file
+ */
+void InstagramTask::setArgument(QString name, Scheduler::KFileData file) {
+  TaskIterator it =
+      std::find_if(m_arguments.begin(), m_arguments.end(), [name](auto argument) { return argument.text() == name; });
+  if (it != m_arguments.end() && it->get()->getTypeIndex() == Scheduler::VariantIndex::FILEVEC) {
+    it->get()->insert(file);
+  } else {
+    // Could not add file to container
+  }
+}
+
 /**
  * @brief InstagramTask::getTaskArgument
  * @param name
@@ -152,6 +167,14 @@ void InstagramTask::clear() {
   }
 }
 
+/**
+ * @brief InstagramTask::hasFiles
+ * @return
+ */
+bool InstagramTask::hasFiles() {
+  return !files.empty();
+}
+
 /**
  * @destructor
  */

+ 3 - 9
src/mainwindow.cpp

@@ -207,20 +207,14 @@ void MainWindow::connectClient() {
     }
   });
 
-  QObject::connect(
-      arg_ui, &ArgDialog::uploadFiles, this,
-      [this](QVector<KFileData> files) { q_client->sendFiles(files); });
-
   QObject::connect(
       arg_ui, &ArgDialog::taskRequestReady, this,
-      [this](Task task, bool file_pending) {
+      [this](Task* task) {
         auto mask = q_client->getSelectedApp();
         if (mask > -1) {
-          if (q_client->getAppName(mask) == "Instagram") {
             qDebug() << "Scheduling a task";
-            task.args.push_back(std::to_string(mask));
-            q_client->scheduleTask(task.args, file_pending);
-          }
+            task->setArgument("mask", mask);
+            q_client->scheduleTask(task);
         }
       });