From dddef5cc1c5b613e1d1f4d6720112b1c88dc3209 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 11:38:24 +0300 Subject: [PATCH 01/37] config smtp --- backend/config/params.php | 2 +- common/config/params.php | 7 +++++-- console/config/params.php | 2 +- frontend/config/params.php | 2 +- frontend/models/ContactForm.php | 12 ++++++++++-- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/backend/config/params.php b/backend/config/params.php index 7f754b9..231bded 100755 --- a/backend/config/params.php +++ b/backend/config/params.php @@ -1,4 +1,4 @@ 'admin@example.com', + 'adminEmail' => 'supp0rt.friday@yandex.ru', ]; diff --git a/common/config/params.php b/common/config/params.php index 4ec9ba6..52a439b 100755 --- a/common/config/params.php +++ b/common/config/params.php @@ -1,6 +1,9 @@ 'admin@example.com', - 'supportEmail' => 'support@example.com', + 'adminEmail' => 'supp0rt.friday@yandex.ru', + 'supportEmail' => 'supp0rt.friday@yandex.ru', + 'senderEmail' => 'supp0rt.friday@yandex.ru', + 'senderName' => 'Friday.support mailer', 'user.passwordResetTokenExpire' => 3600, + 'user.passwordMinLength' => 8, ]; diff --git a/console/config/params.php b/console/config/params.php index 7f754b9..231bded 100755 --- a/console/config/params.php +++ b/console/config/params.php @@ -1,4 +1,4 @@ 'admin@example.com', + 'adminEmail' => 'supp0rt.friday@yandex.ru', ]; diff --git a/frontend/config/params.php b/frontend/config/params.php index 7f754b9..231bded 100755 --- a/frontend/config/params.php +++ b/frontend/config/params.php @@ -1,4 +1,4 @@ 'admin@example.com', + 'adminEmail' => 'supp0rt.friday@yandex.ru', ]; diff --git a/frontend/models/ContactForm.php b/frontend/models/ContactForm.php index 8c00d47..c96be3a 100755 --- a/frontend/models/ContactForm.php +++ b/frontend/models/ContactForm.php @@ -51,10 +51,18 @@ class ContactForm extends Model public function sendEmail($email) { return Yii::$app->mailer->compose() - ->setTo($email) - ->setFrom([$this->email => $this->name]) + ->setTo($this->email) + ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']]) +// ->setReplyTo([$this->email => $this->name]) ->setSubject($this->subject) ->setTextBody($this->body) ->send(); + +// return Yii::$app->mailer->compose() +// ->setTo($email) +// ->setFrom([$this->email => $this->name]) +// ->setSubject($this->subject) +// ->setTextBody($this->body) +// ->send(); } } From e712f2fc36ffec7fde96c8343be0c1a69788961c Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 14:25:40 +0300 Subject: [PATCH 02/37] fix tasks --- common/models/ProjectTask.php | 61 +++++++++++++++++++++ common/services/TaskService.php | 2 +- frontend/modules/api/models/ProjectTask.php | 58 -------------------- 3 files changed, 62 insertions(+), 59 deletions(-) diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index be19aac..a717f54 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -97,6 +97,67 @@ class ProjectTask extends ActiveRecord ]; } + /** + * @return string[] + */ + public function fields(): array + { + return [ + 'id', + 'project_id', + 'project_name' => function () { + return $this->project->name ?? null; + }, + 'title', + 'created_at', + 'updated_at', + 'dead_line', + 'description', + 'status', + 'column_id', + 'user_id', + 'user' => function () { + return [ + "fio" => $this->user->userCard->fio ?? $this->user->id, + "avatar" => $this->user->userCard->photo ?? '', + ]; + }, + 'executor_id', + 'priority', + 'executor' => function () { + if ($this->executor) { + return [ + "fio" => $this->executor->userCard->fio ?? $this->executor->username, + "avatar" => $this->executor->userCard->photo ?? '', + ]; + } + + return null; + }, + 'comment_count' => function () { + return Comment::find()->where(['entity_id' => $this->id, 'entity_type' => 2, 'status' => Comment::STATUS_ACTIVE])->count(); + }, + 'taskUsers', + 'mark' + ]; + } + + /** + * @return string[] + */ + public function extraFields(): array + { + return [ + 'timers', + 'column' => function () { + return [ + 'column_title' => $this->column->title ?? null + ]; + }, + 'mark' + ]; + } + /** * @return string[] */ diff --git a/common/services/TaskService.php b/common/services/TaskService.php index c2a9b92..835cd82 100644 --- a/common/services/TaskService.php +++ b/common/services/TaskService.php @@ -2,8 +2,8 @@ namespace common\services; +use common\models\ProjectTask; use common\models\ProjectTaskUser; -use frontend\modules\api\models\ProjectTask; class TaskService { diff --git a/frontend/modules/api/models/ProjectTask.php b/frontend/modules/api/models/ProjectTask.php index ed80fd9..78d6de0 100644 --- a/frontend/modules/api/models/ProjectTask.php +++ b/frontend/modules/api/models/ProjectTask.php @@ -152,63 +152,5 @@ namespace frontend\modules\api\models; */ class ProjectTask extends \common\models\ProjectTask { - /** - * @return string[] - */ - public function fields(): array - { - return [ - 'id', - 'project_id', - 'project_name' => function () { - return $this->project->name ?? null; - }, - 'title', - 'created_at', - 'updated_at', - 'dead_line', - 'description', - 'status', - 'column_id', - 'user_id', - 'user' => function () { - return [ - "fio" => $this->user->userCard->fio ?? $this->user->id, - "avatar" => $this->user->userCard->photo ?? '', - ]; - }, - 'executor_id', - 'priority', - 'executor' => function () { - if ($this->executor) { - return [ - "fio" => $this->executor->userCard->fio ?? $this->executor->username, - "avatar" => $this->executor->userCard->photo ?? '', - ]; - } - return null; - }, - 'comment_count' => function () { - return Comment::find()->where(['entity_id' => $this->id, 'entity_type' => 2, 'status' => Comment::STATUS_ACTIVE])->count(); - }, - 'taskUsers', - ]; - } - - /** - * @return string[] - */ - public function extraFields(): array - { - return [ - 'timers', - 'column' => function () { - return [ - 'column_title' => $this->column->title ?? null - ]; - }, - 'mark' - ]; - } } \ No newline at end of file From b004f6e21b876cd980316c25fb3701e9ff1bc6f0 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 15:44:16 +0300 Subject: [PATCH 03/37] Updated validation for the project --- common/models/Project.php | 5 +-- ...ey_in_project_from_owner_id_to_user_id.php | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 console/migrations/m231013_122324_add_foreign_key_in_project_from_owner_id_to_user_id.php diff --git a/common/models/Project.php b/common/models/Project.php index 21a56e8..8f59342 100755 --- a/common/models/Project.php +++ b/common/models/Project.php @@ -23,6 +23,7 @@ use yii\helpers\ArrayHelper; * * @property FieldsValue[] $fieldsValues * @property Company $company + * @property User $owner * @property ProjectUser[] $projectUsers * @property Mark[] $mark * @property MarkEntity[] $markEntity @@ -55,8 +56,8 @@ class Project extends \yii\db\ActiveRecord public function rules() { return [ - ['name', 'unique'], - [['name', 'status'], 'required'], + [['name', 'owner_id', 'status'], 'required'], + [['owner_id', 'name'], 'unique', 'targetAttribute' => ['owner_id', 'name']], [['description'], 'string'], [['created_at', 'updated_at'], 'safe'], [['status'], 'exist', 'skipOnError' => true, 'targetClass' => Status::class, 'targetAttribute' => ['status' => 'id']], diff --git a/console/migrations/m231013_122324_add_foreign_key_in_project_from_owner_id_to_user_id.php b/console/migrations/m231013_122324_add_foreign_key_in_project_from_owner_id_to_user_id.php new file mode 100644 index 0000000..1a7c014 --- /dev/null +++ b/console/migrations/m231013_122324_add_foreign_key_in_project_from_owner_id_to_user_id.php @@ -0,0 +1,31 @@ +addForeignKey( + 'project_user', + 'project', + 'owner_id', + 'user', + 'id' + ); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropForeignKey('project_user', 'project'); + } +} From 1c3eeb4cf39c9bfadd7dfc8bdff3002d0ab33e44 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 15:52:39 +0300 Subject: [PATCH 04/37] Updated validation for the project column --- common/models/ProjectColumn.php | 1 + 1 file changed, 1 insertion(+) diff --git a/common/models/ProjectColumn.php b/common/models/ProjectColumn.php index b3d91c0..cc8795e 100644 --- a/common/models/ProjectColumn.php +++ b/common/models/ProjectColumn.php @@ -65,6 +65,7 @@ class ProjectColumn extends \yii\db\ActiveRecord return [ [['title', 'project_id'], 'required'], [['project_id', 'status', 'priority'], 'integer'], + [['project_id', 'title'], 'unique', 'targetAttribute' => ['project_id', 'title']], [['created_at', 'updated_at'], 'safe'], [['title'], 'string', 'max' => 255], [['project_id'], 'exist', 'skipOnError' => true, 'targetClass' => Project::className(), 'targetAttribute' => ['project_id' => 'id']], From 5ce77f2ef0db70489cce752e55517b2c56f8b4f0 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 16:31:22 +0300 Subject: [PATCH 05/37] Add the ability to select a priority when creating a task (low, medium, high) --- .../task/controllers/TaskController.php | 8 ++---- .../{TaskSearch.php => ProjectTaskSearch.php} | 15 ++++++----- backend/modules/task/views/task/_form.php | 8 +++++- backend/modules/task/views/task/_search.php | 2 +- backend/modules/task/views/task/index.php | 10 ++++++- backend/modules/task/views/task/view.php | 8 +++++- common/models/ProjectTask.php | 27 +++++++++++++++++++ .../api/controllers/TaskController.php | 2 +- 8 files changed, 62 insertions(+), 18 deletions(-) rename backend/modules/task/models/{TaskSearch.php => ProjectTaskSearch.php} (74%) diff --git a/backend/modules/task/controllers/TaskController.php b/backend/modules/task/controllers/TaskController.php index 0d718d2..735e2db 100644 --- a/backend/modules/task/controllers/TaskController.php +++ b/backend/modules/task/controllers/TaskController.php @@ -2,14 +2,10 @@ namespace backend\modules\task\controllers; -use backend\modules\project\models\ProjectUser; -use backend\modules\task\models\ProjectTaskUser; -use common\classes\Debug; use yii\data\ActiveDataProvider; -use yii\web\Response; use Yii; use backend\modules\task\models\ProjectTask; -use backend\modules\task\models\TaskSearch; +use backend\modules\task\models\ProjectTaskSearch; use yii\web\Controller; use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; @@ -43,7 +39,7 @@ class TaskController extends Controller */ public function actionIndex() { - $searchModel = new TaskSearch(); + $searchModel = new ProjectTaskSearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams); return $this->render('index', [ diff --git a/backend/modules/task/models/TaskSearch.php b/backend/modules/task/models/ProjectTaskSearch.php similarity index 74% rename from backend/modules/task/models/TaskSearch.php rename to backend/modules/task/models/ProjectTaskSearch.php index 54513c7..7fe8958 100644 --- a/backend/modules/task/models/TaskSearch.php +++ b/backend/modules/task/models/ProjectTaskSearch.php @@ -10,7 +10,7 @@ use backend\modules\task\models\ProjectTask; /** * TaskSearch represents the model behind the search form of `backend\modules\task\models\Task`. */ -class TaskSearch extends ProjectTask +class ProjectTaskSearch extends ProjectTask { /** * {@inheritdoc} @@ -18,7 +18,7 @@ class TaskSearch extends ProjectTask public function rules() { return [ - [['id', 'project_id', 'status'], 'integer'], // 'card_id_creator', 'card_id' + [['id', 'project_id', 'status', 'priority'], 'integer'], // 'card_id_creator', 'card_id' [['title', 'created_at', 'updated_at', 'description'], 'safe'], ]; } @@ -60,14 +60,15 @@ class TaskSearch extends ProjectTask // grid filtering conditions $query->andFilterWhere([ 'id' => $this->id, - 'task.project_id' => $this->project_id, - 'task.status' => $this->status, - 'task.created_at' => $this->created_at, - 'task.updated_at' => $this->updated_at, + 'project_task.project_id' => $this->project_id, + 'project_task.status' => $this->status, + 'project_task.priority' => $this->priority, + 'project_task.created_at' => $this->created_at, + 'project_task.updated_at' => $this->updated_at, ]); $query->andFilterWhere(['like', 'title', $this->title]) - ->andFilterWhere(['like', 'task.description', $this->description]); + ->andFilterWhere(['like', 'project_task.description', $this->description]); return $dataProvider; } diff --git a/backend/modules/task/views/task/_form.php b/backend/modules/task/views/task/_form.php index ac6327b..93661fd 100644 --- a/backend/modules/task/views/task/_form.php +++ b/backend/modules/task/views/task/_form.php @@ -2,6 +2,7 @@ use backend\modules\card\models\UserCard; use backend\modules\project\models\Project; +use backend\modules\task\models\ProjectTask; use common\helpers\StatusHelper; use kartik\select2\Select2; use yii\helpers\Html; @@ -57,7 +58,12 @@ use yii\widgets\ActiveForm; field($model, 'description')->textarea(['rows' => '6']) ?> - field($model, 'priority')->input('number') ?> + field($model, 'priority')->dropDownList( + ProjectTask::priorityList(), + [ + 'prompt' => 'Выберите' + ] + ) ?>
'btn btn-success']) ?> diff --git a/backend/modules/task/views/task/_search.php b/backend/modules/task/views/task/_search.php index f42ac3b..acb1a83 100644 --- a/backend/modules/task/views/task/_search.php +++ b/backend/modules/task/views/task/_search.php @@ -4,7 +4,7 @@ use yii\helpers\Html; use yii\widgets\ActiveForm; /* @var $this yii\web\View */ -/* @var $model backend\modules\task\models\TaskSearch */ +/* @var $model backend\modules\task\models\ProjectTaskSearch */ /* @var $form yii\widgets\ActiveForm */ ?> diff --git a/backend/modules/task/views/task/index.php b/backend/modules/task/views/task/index.php index 7978a72..c5038e3 100644 --- a/backend/modules/task/views/task/index.php +++ b/backend/modules/task/views/task/index.php @@ -12,7 +12,7 @@ use yii\helpers\Html; use yii\grid\GridView; /* @var $this yii\web\View */ -/* @var $searchModel backend\modules\task\models\TaskSearch */ +/* @var $searchModel backend\modules\task\models\ProjectTaskSearch */ /* @var $dataProvider yii\data\ActiveDataProvider */ $this->title = 'Задачи'; @@ -58,6 +58,14 @@ $this->params['breadcrumbs'][] = $this->title; return StatusHelper::statusLabel($model->status); } ], + [ + 'attribute' => 'priority', + 'format' => 'raw', + 'filter' => ProjectTask::priorityList(), + 'value' => function($model){ + return ProjectTask::getPriority($model->status); + } + ], [ 'attribute' => 'created_at', 'format' => ['datetime', 'php:d.m.Y H:i'] diff --git a/backend/modules/task/views/task/view.php b/backend/modules/task/views/task/view.php index c5a1ed6..77487e3 100644 --- a/backend/modules/task/views/task/view.php +++ b/backend/modules/task/views/task/view.php @@ -1,5 +1,6 @@ ArrayHelper::getValue($model, 'executor.userCard.fio'), ], 'description', - 'priority', + [ + 'attribute' => 'priority', + 'value' => function($model){ + return ProjectTask::getPriority($model->status); + } + ], ], ]) ?> diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index a717f54..70eae57 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -37,6 +37,32 @@ class ProjectTask extends ActiveRecord const STATUS_ACTIVE = 1; const STATUS_DISABLE = 0; + const PRIORITY_LOW = 0; + const PRIORITY_MEDIUM = 1; + const PRIORITY_HIGH = 2; + + /** + * @return string[] + */ + public static function priorityList() :array + { + return [ + self::PRIORITY_LOW => 'Низкий', + self::PRIORITY_MEDIUM => 'Средний', + self::PRIORITY_HIGH => 'Высокий', + ]; + } + + /** + * @param $priority + * @return string + * @throws \Exception + */ + public static function getPriority($priority): string + { + return ArrayHelper::getValue(self::priorityList(), $priority); + } + /** * {@inheritdoc} */ @@ -66,6 +92,7 @@ class ProjectTask extends ActiveRecord [['project_id', 'status', 'title', 'description',], 'required'], [['project_id', 'status', 'column_id', 'user_id', 'executor_id', 'priority'], 'integer'], [['created_at', 'updated_at', 'dead_line'], 'safe'], + ['status', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], ['title', 'unique', 'targetAttribute' => ['title', 'project_id'], 'message' => 'Такая задача уже создана'], [['title'], 'string', 'max' => 255], [['description'], 'string', 'max' => 1500], diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index 75db62d..5ca190a 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -72,7 +72,7 @@ class TaskController extends ApiController * @OA\Property( * property="priority", * type="integer", - * description="Приоритет задачи", + * description="Приоритет задачи. (0 -low, 1 - medium, 2 - high)", * ), * @OA\Property( * property="column_id", From 5b3f6518dcb9845b08cacee6bec93e451de6fe35 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 17:00:01 +0300 Subject: [PATCH 06/37] rollback last changes --- .../modules/task/models/ProjectTaskSearch.php | 3 +- backend/modules/task/views/task/_form.php | 14 +++--- backend/modules/task/views/task/index.php | 16 +++--- backend/modules/task/views/task/view.php | 13 ++--- common/models/ProjectTask.php | 50 +++++++++---------- .../api/controllers/TaskController.php | 2 +- 6 files changed, 50 insertions(+), 48 deletions(-) diff --git a/backend/modules/task/models/ProjectTaskSearch.php b/backend/modules/task/models/ProjectTaskSearch.php index 7fe8958..6b60bee 100644 --- a/backend/modules/task/models/ProjectTaskSearch.php +++ b/backend/modules/task/models/ProjectTaskSearch.php @@ -18,7 +18,7 @@ class ProjectTaskSearch extends ProjectTask public function rules() { return [ - [['id', 'project_id', 'status', 'priority'], 'integer'], // 'card_id_creator', 'card_id' + [['id', 'project_id', 'status'], 'integer'], // 'card_id_creator', 'card_id' [['title', 'created_at', 'updated_at', 'description'], 'safe'], ]; } @@ -62,7 +62,6 @@ class ProjectTaskSearch extends ProjectTask 'id' => $this->id, 'project_task.project_id' => $this->project_id, 'project_task.status' => $this->status, - 'project_task.priority' => $this->priority, 'project_task.created_at' => $this->created_at, 'project_task.updated_at' => $this->updated_at, ]); diff --git a/backend/modules/task/views/task/_form.php b/backend/modules/task/views/task/_form.php index 93661fd..7ae2466 100644 --- a/backend/modules/task/views/task/_form.php +++ b/backend/modules/task/views/task/_form.php @@ -58,12 +58,14 @@ use yii\widgets\ActiveForm; field($model, 'description')->textarea(['rows' => '6']) ?> - field($model, 'priority')->dropDownList( - ProjectTask::priorityList(), - [ - 'prompt' => 'Выберите' - ] - ) ?> + field($model, 'priority')->input('number') ?> + +field($model, 'priority')->dropDownList( +// ProjectTask::priorityList(), +// [ +// 'prompt' => 'Выберите' +// ] +// ) ?>
'btn btn-success']) ?> diff --git a/backend/modules/task/views/task/index.php b/backend/modules/task/views/task/index.php index c5038e3..b130a43 100644 --- a/backend/modules/task/views/task/index.php +++ b/backend/modules/task/views/task/index.php @@ -58,14 +58,14 @@ $this->params['breadcrumbs'][] = $this->title; return StatusHelper::statusLabel($model->status); } ], - [ - 'attribute' => 'priority', - 'format' => 'raw', - 'filter' => ProjectTask::priorityList(), - 'value' => function($model){ - return ProjectTask::getPriority($model->status); - } - ], +// [ +// 'attribute' => 'priority', +// 'format' => 'raw', +// 'filter' => ProjectTask::priorityList(), +// 'value' => function($model){ +// return ProjectTask::getPriority($model->status); +// } +// ], [ 'attribute' => 'created_at', 'format' => ['datetime', 'php:d.m.Y H:i'] diff --git a/backend/modules/task/views/task/view.php b/backend/modules/task/views/task/view.php index 77487e3..c13a900 100644 --- a/backend/modules/task/views/task/view.php +++ b/backend/modules/task/views/task/view.php @@ -60,12 +60,13 @@ YiiAsset::register($this); 'value' => ArrayHelper::getValue($model, 'executor.userCard.fio'), ], 'description', - [ - 'attribute' => 'priority', - 'value' => function($model){ - return ProjectTask::getPriority($model->status); - } - ], + 'priority', +// [ +// 'attribute' => 'priority', +// 'value' => function($model){ +// return ProjectTask::getPriority($model->status); +// } +// ], ], ]) ?> diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index 70eae57..37f94e4 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -37,31 +37,31 @@ class ProjectTask extends ActiveRecord const STATUS_ACTIVE = 1; const STATUS_DISABLE = 0; - const PRIORITY_LOW = 0; - const PRIORITY_MEDIUM = 1; - const PRIORITY_HIGH = 2; +// const PRIORITY_LOW = 0; +// const PRIORITY_MEDIUM = 1; +// const PRIORITY_HIGH = 2; +// +// /** +// * @return string[] +// */ +// public static function priorityList() :array +// { +// return [ +// self::PRIORITY_LOW => 'Низкий', +// self::PRIORITY_MEDIUM => 'Средний', +// self::PRIORITY_HIGH => 'Высокий', +// ]; +// } - /** - * @return string[] - */ - public static function priorityList() :array - { - return [ - self::PRIORITY_LOW => 'Низкий', - self::PRIORITY_MEDIUM => 'Средний', - self::PRIORITY_HIGH => 'Высокий', - ]; - } - - /** - * @param $priority - * @return string - * @throws \Exception - */ - public static function getPriority($priority): string - { - return ArrayHelper::getValue(self::priorityList(), $priority); - } +// /** +// * @param $priority +// * @return string +// * @throws \Exception +// */ +// public static function getPriority($priority): string +// { +// return ArrayHelper::getValue(self::priorityList(), $priority); +// } /** * {@inheritdoc} @@ -92,7 +92,7 @@ class ProjectTask extends ActiveRecord [['project_id', 'status', 'title', 'description',], 'required'], [['project_id', 'status', 'column_id', 'user_id', 'executor_id', 'priority'], 'integer'], [['created_at', 'updated_at', 'dead_line'], 'safe'], - ['status', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], +// ['status', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], ['title', 'unique', 'targetAttribute' => ['title', 'project_id'], 'message' => 'Такая задача уже создана'], [['title'], 'string', 'max' => 255], [['description'], 'string', 'max' => 1500], diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index 5ca190a..89cdde9 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -72,7 +72,7 @@ class TaskController extends ApiController * @OA\Property( * property="priority", * type="integer", - * description="Приоритет задачи. (0 -low, 1 - medium, 2 - high)", + * description="Приоритет задачи.", * ), * @OA\Property( * property="column_id", From c20d7fbe777db87c2c4648d898967debbb6fc183 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 17:58:58 +0300 Subject: [PATCH 07/37] Add execution_priority to task --- .../modules/task/models/ProjectTaskSearch.php | 1 + backend/modules/task/views/task/_form.php | 12 ++-- backend/modules/task/views/task/index.php | 16 +++--- backend/modules/task/views/task/view.php | 12 ++-- common/models/ProjectTask.php | 57 ++++++++++--------- ..._priority_column_to_project_task_table.php | 25 ++++++++ .../api/controllers/TaskController.php | 5 ++ 7 files changed, 81 insertions(+), 47 deletions(-) create mode 100644 console/migrations/m231013_144526_add_execution_priority_column_to_project_task_table.php diff --git a/backend/modules/task/models/ProjectTaskSearch.php b/backend/modules/task/models/ProjectTaskSearch.php index 6b60bee..9c20656 100644 --- a/backend/modules/task/models/ProjectTaskSearch.php +++ b/backend/modules/task/models/ProjectTaskSearch.php @@ -62,6 +62,7 @@ class ProjectTaskSearch extends ProjectTask 'id' => $this->id, 'project_task.project_id' => $this->project_id, 'project_task.status' => $this->status, + 'project_task.execution_priority' => $this->execution_priority, 'project_task.created_at' => $this->created_at, 'project_task.updated_at' => $this->updated_at, ]); diff --git a/backend/modules/task/views/task/_form.php b/backend/modules/task/views/task/_form.php index 7ae2466..f8c8e20 100644 --- a/backend/modules/task/views/task/_form.php +++ b/backend/modules/task/views/task/_form.php @@ -60,12 +60,12 @@ use yii\widgets\ActiveForm; field($model, 'priority')->input('number') ?> -field($model, 'priority')->dropDownList( -// ProjectTask::priorityList(), -// [ -// 'prompt' => 'Выберите' -// ] -// ) ?> + field($model, 'execution_priority')->dropDownList( + ProjectTask::priorityList(), + [ + 'prompt' => 'Выберите' + ] + ) ?>
'btn btn-success']) ?> diff --git a/backend/modules/task/views/task/index.php b/backend/modules/task/views/task/index.php index b130a43..538f42c 100644 --- a/backend/modules/task/views/task/index.php +++ b/backend/modules/task/views/task/index.php @@ -58,14 +58,14 @@ $this->params['breadcrumbs'][] = $this->title; return StatusHelper::statusLabel($model->status); } ], -// [ -// 'attribute' => 'priority', -// 'format' => 'raw', -// 'filter' => ProjectTask::priorityList(), -// 'value' => function($model){ -// return ProjectTask::getPriority($model->status); -// } -// ], + [ + 'attribute' => 'execution_priority', + 'format' => 'raw', + 'filter' => ProjectTask::priorityList(), + 'value' => function($model){ + return ProjectTask::getPriority($model->status); + } + ], [ 'attribute' => 'created_at', 'format' => ['datetime', 'php:d.m.Y H:i'] diff --git a/backend/modules/task/views/task/view.php b/backend/modules/task/views/task/view.php index c13a900..0f458b1 100644 --- a/backend/modules/task/views/task/view.php +++ b/backend/modules/task/views/task/view.php @@ -61,12 +61,12 @@ YiiAsset::register($this); ], 'description', 'priority', -// [ -// 'attribute' => 'priority', -// 'value' => function($model){ -// return ProjectTask::getPriority($model->status); -// } -// ], + [ + 'attribute' => 'execution_priority', + 'value' => function($model){ + return ProjectTask::getPriority($model->status); + } + ], ], ]) ?> diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index 37f94e4..7321c9b 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -21,6 +21,7 @@ use yii\helpers\ArrayHelper; * @property int $user_id * @property int $executor_id * @property int $priority + * @property int $execution_priority * @property string $description * @property string $dead_line * @@ -37,31 +38,31 @@ class ProjectTask extends ActiveRecord const STATUS_ACTIVE = 1; const STATUS_DISABLE = 0; -// const PRIORITY_LOW = 0; -// const PRIORITY_MEDIUM = 1; -// const PRIORITY_HIGH = 2; -// -// /** -// * @return string[] -// */ -// public static function priorityList() :array -// { -// return [ -// self::PRIORITY_LOW => 'Низкий', -// self::PRIORITY_MEDIUM => 'Средний', -// self::PRIORITY_HIGH => 'Высокий', -// ]; -// } + const PRIORITY_LOW = 0; + const PRIORITY_MEDIUM = 1; + const PRIORITY_HIGH = 2; -// /** -// * @param $priority -// * @return string -// * @throws \Exception -// */ -// public static function getPriority($priority): string -// { -// return ArrayHelper::getValue(self::priorityList(), $priority); -// } + /** + * @return string[] + */ + public static function priorityList() :array + { + return [ + self::PRIORITY_LOW => 'Низкий', + self::PRIORITY_MEDIUM => 'Средний', + self::PRIORITY_HIGH => 'Высокий', + ]; + } + + /** + * @param $priority + * @return string + * @throws \Exception + */ + public static function getPriority($priority): string + { + return ArrayHelper::getValue(self::priorityList(), $priority); + } /** * {@inheritdoc} @@ -90,9 +91,9 @@ class ProjectTask extends ActiveRecord { return [ [['project_id', 'status', 'title', 'description',], 'required'], - [['project_id', 'status', 'column_id', 'user_id', 'executor_id', 'priority'], 'integer'], + [['project_id', 'status', 'column_id', 'user_id', 'executor_id', 'priority', 'execution_priority'], 'integer'], [['created_at', 'updated_at', 'dead_line'], 'safe'], -// ['status', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], + ['execution_priority', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], ['title', 'unique', 'targetAttribute' => ['title', 'project_id'], 'message' => 'Такая задача уже создана'], [['title'], 'string', 'max' => 255], [['description'], 'string', 'max' => 1500], @@ -121,6 +122,7 @@ class ProjectTask extends ActiveRecord 'executor_id' => 'Исполнитель', 'priority' => 'Приоритет', 'dead_line' => 'Срок выполнения задачи', + 'execution_priority' => 'Приоритет выполнения', ]; } @@ -165,7 +167,8 @@ class ProjectTask extends ActiveRecord return Comment::find()->where(['entity_id' => $this->id, 'entity_type' => 2, 'status' => Comment::STATUS_ACTIVE])->count(); }, 'taskUsers', - 'mark' + 'mark', + 'execution_priority' ]; } diff --git a/console/migrations/m231013_144526_add_execution_priority_column_to_project_task_table.php b/console/migrations/m231013_144526_add_execution_priority_column_to_project_task_table.php new file mode 100644 index 0000000..8dea2cd --- /dev/null +++ b/console/migrations/m231013_144526_add_execution_priority_column_to_project_task_table.php @@ -0,0 +1,25 @@ +addColumn('project_task', 'execution_priority', $this->integer(1)); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropColumn('project_task', 'execution_priority'); + } +} diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index 89cdde9..77d830c 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -75,6 +75,11 @@ class TaskController extends ApiController * description="Приоритет задачи.", * ), * @OA\Property( + * property="execution_priority", + * type="integer", + * description="Приоритет выполнения задачи (0 - low, 1 - medium, 2 - high)", + * ), + * @OA\Property( * property="column_id", * type="integer", * description="Колонка к которой относится задача", From 0e2bd930101a94ad4ab13c116ace931004c0b08c Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 13 Oct 2023 18:03:36 +0300 Subject: [PATCH 08/37] update doc --- frontend/modules/api/models/ProjectTask.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/modules/api/models/ProjectTask.php b/frontend/modules/api/models/ProjectTask.php index 78d6de0..6258115 100644 --- a/frontend/modules/api/models/ProjectTask.php +++ b/frontend/modules/api/models/ProjectTask.php @@ -71,6 +71,12 @@ namespace frontend\modules\api\models; * description="Приоритет задачи" * ), * @OA\Property( + * property="execution_priority", + * type="integer", + * example="2", + * description="Приоритет выполнения задачи (0 - low, 1 - medium, 2 - high)", + * ), + * @OA\Property( * property="status", * type="int", * example="1", From 233a8a6d10be1863b2e9e75cc7e687c3dfed7aef Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 18 Oct 2023 10:48:38 +0300 Subject: [PATCH 09/37] Config mail.ru smtp --- backend/config/params.php | 2 +- common/config/params.php | 8 ++++---- common/mail/signup-html.php | 10 ++++++++++ common/mail/signup-text.php | 10 ++++++++++ console/config/params.php | 2 +- frontend/config/main.php | 2 +- frontend/config/params.php | 2 +- frontend/models/ContactForm.php | 11 +---------- frontend/models/SignupForm.php | 26 ++++++++++++++++++++++++-- 9 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 common/mail/signup-html.php create mode 100644 common/mail/signup-text.php diff --git a/backend/config/params.php b/backend/config/params.php index 231bded..392918b 100755 --- a/backend/config/params.php +++ b/backend/config/params.php @@ -1,4 +1,4 @@ 'supp0rt.friday@yandex.ru', + 'adminEmail' => 'chancellery@itguild.info', ]; diff --git a/common/config/params.php b/common/config/params.php index 52a439b..c6ecfb3 100755 --- a/common/config/params.php +++ b/common/config/params.php @@ -1,9 +1,9 @@ 'supp0rt.friday@yandex.ru', - 'supportEmail' => 'supp0rt.friday@yandex.ru', - 'senderEmail' => 'supp0rt.friday@yandex.ru', - 'senderName' => 'Friday.support mailer', + 'adminEmail' => 'chancellery@itguild.info', + 'supportEmail' => 'chancellery@itguild.info', + 'senderEmail' => 'chancellery@itguild.info', + 'senderName' => 'Chancellery ITguild mailer', 'user.passwordResetTokenExpire' => 3600, 'user.passwordMinLength' => 8, ]; diff --git a/common/mail/signup-html.php b/common/mail/signup-html.php new file mode 100644 index 0000000..8bfe78e --- /dev/null +++ b/common/mail/signup-html.php @@ -0,0 +1,10 @@ + +Hello username ?>, + +You have successfully registered! + diff --git a/common/mail/signup-text.php b/common/mail/signup-text.php new file mode 100644 index 0000000..8bfe78e --- /dev/null +++ b/common/mail/signup-text.php @@ -0,0 +1,10 @@ + +Hello username ?>, + +You have successfully registered! + diff --git a/console/config/params.php b/console/config/params.php index 231bded..392918b 100755 --- a/console/config/params.php +++ b/console/config/params.php @@ -1,4 +1,4 @@ 'supp0rt.friday@yandex.ru', + 'adminEmail' => 'chancellery@itguild.info', ]; diff --git a/frontend/config/main.php b/frontend/config/main.php index 6bf6071..547afd9 100755 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -8,7 +8,7 @@ $params = array_merge( return [ 'id' => 'app-frontend', - 'name' => 'Guild', + 'name' => 'itGuild', 'basePath' => dirname(__DIR__), 'bootstrap' => ['log'], 'controllerNamespace' => 'frontend\controllers', diff --git a/frontend/config/params.php b/frontend/config/params.php index 231bded..392918b 100755 --- a/frontend/config/params.php +++ b/frontend/config/params.php @@ -1,4 +1,4 @@ 'supp0rt.friday@yandex.ru', + 'adminEmail' => 'chancellery@itguild.info', ]; diff --git a/frontend/models/ContactForm.php b/frontend/models/ContactForm.php index c96be3a..3b6d90b 100755 --- a/frontend/models/ContactForm.php +++ b/frontend/models/ContactForm.php @@ -45,24 +45,15 @@ class ContactForm extends Model /** * Sends an email to the specified email address using the information collected by this model. * - * @param string $email the target email address * @return bool whether the email was sent */ - public function sendEmail($email) + public function sendEmail() { return Yii::$app->mailer->compose() ->setTo($this->email) ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']]) -// ->setReplyTo([$this->email => $this->name]) ->setSubject($this->subject) ->setTextBody($this->body) ->send(); - -// return Yii::$app->mailer->compose() -// ->setTo($email) -// ->setFrom([$this->email => $this->name]) -// ->setSubject($this->subject) -// ->setTextBody($this->body) -// ->send(); } } diff --git a/frontend/models/SignupForm.php b/frontend/models/SignupForm.php index 5eec1cc..79c34a9 100755 --- a/frontend/models/SignupForm.php +++ b/frontend/models/SignupForm.php @@ -59,7 +59,29 @@ class SignupForm extends Model $auth = Yii::$app->authManager; $authorRole = $auth->getRole('user'); $auth->assign($authorRole, $user->id); - - return $user->save() ? $user : null; + + if ($user->save()) { + $this->sendEmail($user); + return $user; + } else { + return null; + } + } + + /** + * Sends an email with a link, for resetting the password. + * + * @return bool whether the email was send + */ + private function sendEmail(User $user) + { + return Yii::$app->mailer->compose( + ['html' => 'signup-html', 'text' => 'signup-text'], + ['user' => $this] + ) + ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->name . ' robot']) + ->setTo($user->email) + ->setSubject('Account registration at ' . Yii::$app->name) + ->send(); } } From 61085a13620188fa46479ce96a7ea3722e8f40e8 Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 18 Oct 2023 13:12:02 +0300 Subject: [PATCH 10/37] switching to email service --- common/models/email/Email.php | 23 +++++++++++++ common/models/email/RegistrationEmail.php | 20 +++++++++++ common/services/EmailService.php | 33 +++++++++++++++++++ frontend/models/SignupForm.php | 24 +------------- .../api/controllers/RegisterController.php | 12 ++++++- 5 files changed, 88 insertions(+), 24 deletions(-) create mode 100644 common/models/email/Email.php create mode 100644 common/models/email/RegistrationEmail.php create mode 100644 common/services/EmailService.php diff --git a/common/models/email/Email.php b/common/models/email/Email.php new file mode 100644 index 0000000..f6b0e0d --- /dev/null +++ b/common/models/email/Email.php @@ -0,0 +1,23 @@ +sendTo = $user->email; + $this->subject = 'Account registration at ' . Yii::$app->name; + $this->mailLayout = ['html' => 'signup-html', 'text' => 'signup-text']; + $this->params = ['user' => $user]; + } +} \ No newline at end of file diff --git a/common/services/EmailService.php b/common/services/EmailService.php new file mode 100644 index 0000000..f031fce --- /dev/null +++ b/common/services/EmailService.php @@ -0,0 +1,33 @@ +sendFrom = [Yii::$app->params['senderEmail'] => Yii::$app->name . ' robot']; + + } + + /** + * @param Email $email + * @return bool + */ + public function sendEmail(Email $email): bool + { + return Yii::$app->mailer->compose( + $email->mailLayout, + $email->params, + ) + ->setFrom($this->sendFrom) + ->setTo($email->sendTo) + ->setSubject($email->subject) + ->send(); + } +} \ No newline at end of file diff --git a/frontend/models/SignupForm.php b/frontend/models/SignupForm.php index 79c34a9..8fa8cd8 100755 --- a/frontend/models/SignupForm.php +++ b/frontend/models/SignupForm.php @@ -60,28 +60,6 @@ class SignupForm extends Model $authorRole = $auth->getRole('user'); $auth->assign($authorRole, $user->id); - if ($user->save()) { - $this->sendEmail($user); - return $user; - } else { - return null; - } - } - - /** - * Sends an email with a link, for resetting the password. - * - * @return bool whether the email was send - */ - private function sendEmail(User $user) - { - return Yii::$app->mailer->compose( - ['html' => 'signup-html', 'text' => 'signup-text'], - ['user' => $this] - ) - ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->name . ' robot']) - ->setTo($user->email) - ->setSubject('Account registration at ' . Yii::$app->name) - ->send(); + return $user->save() ? $user : null; } } diff --git a/frontend/modules/api/controllers/RegisterController.php b/frontend/modules/api/controllers/RegisterController.php index 4c759dd..5a9e697 100644 --- a/frontend/modules/api/controllers/RegisterController.php +++ b/frontend/modules/api/controllers/RegisterController.php @@ -2,8 +2,9 @@ namespace frontend\modules\api\controllers; -use common\classes\Debug; +use common\models\email\RegistrationEmail; use common\models\User; +use common\services\EmailService; use frontend\models\SignupForm; use Yii; @@ -16,6 +17,14 @@ class RegisterController extends ApiController return $newBehavior; } + private EmailService $emailService; + + public function __construct($id, $module, EmailService $emailService, $config = []) + { + $this->emailService = $emailService; + parent::__construct($id, $module, $config); + } + /** * * @OA\Post(path="/register/sign-up", @@ -60,6 +69,7 @@ class RegisterController extends ApiController if ($model->load(Yii::$app->getRequest()->getBodyParams(), '')) { /** @var User $user */ if ($user = $model->signup()) { + $this->emailService->sendEmail(new RegistrationEmail($user)); return [ 'id' => $user->id, ]; From b02d0b3ddf3c5c7bdf7fd3411378edbf431fe8d2 Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 18 Oct 2023 16:26:49 +0300 Subject: [PATCH 11/37] add reset pass method to api --- common/mail/passwordResetToken-html.php | 6 +- common/models/email/ResetPasswordEmail.php | 20 +++ .../api/controllers/RegisterController.php | 116 ++++++++++++++++++ 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 common/models/email/ResetPasswordEmail.php diff --git a/common/mail/passwordResetToken-html.php b/common/mail/passwordResetToken-html.php index f3daf49..ef8cf72 100755 --- a/common/mail/passwordResetToken-html.php +++ b/common/mail/passwordResetToken-html.php @@ -3,13 +3,11 @@ use yii\helpers\Html; /* @var $this yii\web\View */ /* @var $user common\models\User */ - -$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); ?>

Hello username) ?>,

-

Follow the link below to reset your password:

+

Your reset token:

-

+

password_reset_token ?>

diff --git a/common/models/email/ResetPasswordEmail.php b/common/models/email/ResetPasswordEmail.php new file mode 100644 index 0000000..89af2bb --- /dev/null +++ b/common/models/email/ResetPasswordEmail.php @@ -0,0 +1,20 @@ +sendTo = $user->email; + $this->subject = 'Password reset for ' . Yii::$app->name; + $this->mailLayout = ['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text']; //+ + $this->params = ['user' => $user];//+ + } +} \ No newline at end of file diff --git a/frontend/modules/api/controllers/RegisterController.php b/frontend/modules/api/controllers/RegisterController.php index 5a9e697..e887766 100644 --- a/frontend/modules/api/controllers/RegisterController.php +++ b/frontend/modules/api/controllers/RegisterController.php @@ -3,10 +3,16 @@ namespace frontend\modules\api\controllers; use common\models\email\RegistrationEmail; +use common\models\email\ResetPasswordEmail; use common\models\User; use common\services\EmailService; +use Exception; +use frontend\models\PasswordResetRequestForm; +use frontend\models\ResetPasswordForm; use frontend\models\SignupForm; use Yii; +use yii\base\InvalidParamException; +use yii\web\BadRequestHttpException; class RegisterController extends ApiController { @@ -78,4 +84,114 @@ class RegisterController extends ApiController return null; } + + /** + * + * @OA\Post(path="/register/request-password-reset", + * summary="Запросить сброс пароля", + * description="Метод метод высылает токен сброса пароля на почтовый адрес", + * tags={"Registration"}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * required={"email"}, + * @OA\Property( + * property="email", + * type="string", + * description="Электронная почта пользователя", + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает true в случае успеха", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * ) + * + * @return bool|string + */ + public function actionRequestPasswordReset() + { + $model = new PasswordResetRequestForm(); + + if ($model->load(Yii::$app->request->post(), '') & $model->validate()) { + + /* @var $user User */ + $user = User::findOne([ + 'status' => User::STATUS_ACTIVE, + 'email' => $model->email, + ]); + + if (!$user) { + return false; + } + + if (!User::isPasswordResetTokenValid($user->password_reset_token)) { + $user->generatePasswordResetToken(); + if (!$user->save()) { + return false; + } + } + + return $this->emailService->sendEmail(new ResetPasswordEmail($user)); + } + + return json_encode($model->getFirstErrors()); + } + + /** + * + * @OA\Post(path="/register/reset-password", + * summary="Cброс пароля", + * description="Метод сброса пароля", + * tags={"Registration"}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="multipart/form-data", + * @OA\Schema( + * required={"token", "password"}, + * @OA\Property( + * property="token", + * type="string", + * description="Токен сброса пароля", + * ), + * @OA\Property( + * property="password", + * type="string", + * description="Новый пароль пользователя", + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * ) + * + * @return array|string + * @throws BadRequestHttpException + */ + public function actionResetPassword() + { + try { + $model = new ResetPasswordForm(Yii::$app->request->post()['token']); + } catch (Exception $e) { + throw new BadRequestHttpException($e->getMessage()); + } + + if ($model->load(Yii::$app->request->post(), '') & $model->validate() & $model->resetPassword()) { + return 'Success! New password saved.'; + } else { + return $model->errors; + } + } } From 672d0833c5e5b25d04a2d811551fdbb9b466d8a1 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 20 Oct 2023 15:02:59 +0300 Subject: [PATCH 12/37] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3.=20=D0=92=20=D0=90=D0=9F=D0=98?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B=20=D0=B2=20=D0=B8=D0=B7?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F:=20username,=20email?= =?UTF-8?q?,=20password?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/config/main.php | 4 +- .../api/controllers/ProfileController.php | 41 +++- .../api/controllers/UserController.php | 210 +++++++++++++----- .../models/profile/ProfileChangeEmailForm.php | 32 +++ .../profile/ProfileChangePasswordForm.php | 30 +++ .../profile/ProfileChangePersonalDataForm.php | 31 +++ .../{ => profile}/ProfileSearchForm.php | 3 +- .../modules/api/models/{ => profile}/User.php | 5 +- .../modules/api}/services/ProfileService.php | 4 +- frontend/modules/api/services/UserService.php | 102 +++++++++ 10 files changed, 385 insertions(+), 77 deletions(-) create mode 100644 frontend/modules/api/models/profile/ProfileChangeEmailForm.php create mode 100644 frontend/modules/api/models/profile/ProfileChangePasswordForm.php create mode 100644 frontend/modules/api/models/profile/ProfileChangePersonalDataForm.php rename frontend/modules/api/models/{ => profile}/ProfileSearchForm.php (97%) mode change 100755 => 100644 rename frontend/modules/api/models/{ => profile}/User.php (78%) rename {common => frontend/modules/api}/services/ProfileService.php (97%) create mode 100644 frontend/modules/api/services/UserService.php diff --git a/frontend/config/main.php b/frontend/config/main.php index 547afd9..aebc7f2 100755 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -17,10 +17,10 @@ return [ 'api' => [ 'components' => [ 'user' => [ - 'identityClass' => 'frontend\modules\api\models\User', + 'identityClass' => 'frontend\modules\api\models\profile\User', 'enableAutoLogin' => true, 'enableSession' => false, - 'class' => 'frontend\modules\api\models\User', + 'class' => 'frontend\modules\api\models\profile\User', //'identityCookie' => ['name' => '_identity-api', 'httpOnly' => true], ], ], diff --git a/frontend/modules/api/controllers/ProfileController.php b/frontend/modules/api/controllers/ProfileController.php index 87ab147..b7545d5 100755 --- a/frontend/modules/api/controllers/ProfileController.php +++ b/frontend/modules/api/controllers/ProfileController.php @@ -2,12 +2,9 @@ namespace frontend\modules\api\controllers; -use common\models\User; use common\models\UserCard; -use common\services\ProfileService; +use frontend\modules\api\services\ProfileService; use yii\helpers\ArrayHelper; -use yii\web\BadRequestHttpException; -use yii\web\NotFoundHttpException; use yii\web\ServerErrorHttpException; class ProfileController extends ApiController @@ -30,33 +27,55 @@ class ProfileController extends ApiController ]); } + private ProfileService $profileService; + + public function __construct( + $id, + $module, + ProfileService $profileService, + $config = [] + ) + { + $this->profileService = $profileService; + parent::__construct($id, $module, $config); + } + /** - * @throws NotFoundHttpException + * @param null $id + * @return array|null */ public function actionIndex($id = null): ?array { - return ProfileService::getProfile($id, \Yii::$app->request->get()); + return $this->profileService->getProfile($id, \Yii::$app->request->get()); } /** - * @throws BadRequestHttpException + * @param $id + * @return array|null + * @throws ServerErrorHttpException */ public function actionProfileWithReportPermission($id): ?array { - return ProfileService::getProfileWithReportPermission($id); + return $this->profileService->getProfileWithReportPermission($id); } /** + * @param $user_id + * @return array * @throws ServerErrorHttpException */ public function actionGetMainData($user_id): array { - return ProfileService::getMainData($user_id); + return $this->profileService->getMainData($user_id); } + /** + * @param $card_id + * @return array + */ public function actionPortfolioProjects($card_id): array { - return ProfileService::getPortfolioProjects($card_id); + return $this->profileService->getPortfolioProjects($card_id); } /** @@ -83,7 +102,7 @@ class ProfileController extends ApiController */ public function actionPositionsList(): array { - return ProfileService::getPositionsList(); + return $this->profileService->getPositionsList(); } /** diff --git a/frontend/modules/api/controllers/UserController.php b/frontend/modules/api/controllers/UserController.php index 98b6bb8..6e2d628 100755 --- a/frontend/modules/api/controllers/UserController.php +++ b/frontend/modules/api/controllers/UserController.php @@ -3,16 +3,13 @@ namespace frontend\modules\api\controllers; -use common\behaviors\GsCors; -use common\classes\Debug; use common\models\User; -use frontend\modules\api\models\LoginForm; +use frontend\modules\api\models\profile\ProfileChangeEmailForm; +use frontend\modules\api\models\profile\ProfileChangePersonalDataForm; +use frontend\modules\api\services\UserService; use Yii; -use yii\filters\ContentNegotiator; -use yii\helpers\ArrayHelper; -use yii\rest\ActiveController; +use yii\base\InvalidConfigException; use yii\web\BadRequestHttpException; -use yii\web\Response; class UserController extends ApiController { @@ -26,28 +23,20 @@ class UserController extends ApiController } return $behaviors; -// return ArrayHelper::merge(parent::behaviors(), [ -// [ -// 'class' => ContentNegotiator::class, -// 'formats' => [ -// 'application/json' => Response::FORMAT_JSON, -// ], -// ], -// 'corsFilter' => [ -// 'class' => GsCors::class, -// 'cors' => [ -// 'Origin' => ['*'], -// //'Access-Control-Allow-Credentials' => true, -// 'Access-Control-Allow-Headers' => [ -// 'Access-Control-Allow-Origin', -// 'Content-Type', -// 'Access-Control-Allow-Headers', -// 'Authorization', -// 'X-Requested-With' -// ], -// ] -// ], -// ]); + } + + + private UserService $userService; + + public function __construct( + $id, + $module, + UserService $userService, + $config = [] + ) + { + $this->userService = $userService; + parent::__construct($id, $module, $config); } public function actions() @@ -59,28 +48,23 @@ class UserController extends ApiController unset($actions['delete']); } -// protected function verbs(){ -// return [ -// 'login' => ['POST'] -// ]; -// } - - public function actionLogin() + public function verbs(): array { - $model = new LoginForm(); - if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) { - /** @var User $user */ - $user = $model->getUser(); - return [ - 'access_token' => $model->login(), - 'access_token_expired_at' => $model->getUser()->getTokenExpiredAt(), - 'id' => $user->id, - 'status' => $user->userCard->status ?? null, - 'card_id' => $user->userCard->id ?? null, - ]; - } else { - throw new BadRequestHttpException(json_encode($model->errors)); - } + return [ + 'change-personalData' => ['put', 'patch'], + 'change-email' => ['put', 'patch'], + 'change-password' => ['put', 'patch'], + ]; + } + + /** + * @return array + * @throws BadRequestHttpException + * @throws InvalidConfigException + */ + public function actionLogin(): array + { + return $this->userService->login(Yii::$app->getRequest()->getBodyParams()); } /** @@ -101,16 +85,128 @@ class UserController extends ApiController * ), * ) * - * @return \frontend\modules\api\models\User + * @return \frontend\modules\api\models\profile\User * @throws BadRequestHttpException */ - public function actionMe(): \frontend\modules\api\models\User + public function actionMe(): \frontend\modules\api\models\profile\User { - $user = \frontend\modules\api\models\User::findOne(Yii::$app->user->id); - if (!$user){ - throw new BadRequestHttpException("User not found"); - } + return $this->userService->findCurrentUser(); + } - return $user; + /** + * + * @OA\Put(path="/user/change-email", + * summary="Изменить email адрес", + * description="Метод для изменения email адреса пользователя", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"User"}, + * + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"newEmail"}, + * @OA\Property( + * property="newEmail", + * type="string", + * description="Новый email адрес", + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * ), + * ) + * ) + * + * @return ProfileChangeEmailForm|string[] + */ + public function actionChangeEmail() + { + return $this->userService->changeEmail(Yii::$app->request->post()); + } + + /** + * + * @OA\Put(path="/user/change-password", + * summary="Изменить пароль", + * description="Метод для изменения пароля пользователя", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"User"}, + * + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"password", newPassword}, + * @OA\Property( + * property="password", + * type="string", + * description="Старый пароль", + * ), + * @OA\Property( + * property="newPassword", + * type="string", + * description="Новый пароль", + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * ), + * ) + * ) + * + * @return ProfileChangeEmailForm|string[] + */ + public function actionChangePassword() + { + return $this->userService->changePassword(Yii::$app->request->post()); + } + + /** + * + * @OA\Put(path="/user/change-personal-data", + * summary="Изменить логин", + * description="Метод для изменения логина пользователя", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"User"}, + * + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"newUsername"}, + * @OA\Property( + * property="newUsername", + * type="string", + * description="Новый логин", + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * ), + * ) + * ) + * + * @return ProfileChangePersonalDataForm|string[] + * @throws \Exception + */ + public function actionChangePersonalData() + { + return $this->userService->changeChangePersonalData(Yii::$app->request->post()); } } diff --git a/frontend/modules/api/models/profile/ProfileChangeEmailForm.php b/frontend/modules/api/models/profile/ProfileChangeEmailForm.php new file mode 100644 index 0000000..aabf8c1 --- /dev/null +++ b/frontend/modules/api/models/profile/ProfileChangeEmailForm.php @@ -0,0 +1,32 @@ + 'email', 'targetClass' => User::class], + ]; + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } + +} diff --git a/frontend/modules/api/models/profile/ProfileChangePasswordForm.php b/frontend/modules/api/models/profile/ProfileChangePasswordForm.php new file mode 100644 index 0000000..92f5f08 --- /dev/null +++ b/frontend/modules/api/models/profile/ProfileChangePasswordForm.php @@ -0,0 +1,30 @@ + 255], + [['newUsername'], 'required'], + ]; + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } +} \ No newline at end of file diff --git a/frontend/modules/api/models/ProfileSearchForm.php b/frontend/modules/api/models/profile/ProfileSearchForm.php old mode 100755 new mode 100644 similarity index 97% rename from frontend/modules/api/models/ProfileSearchForm.php rename to frontend/modules/api/models/profile/ProfileSearchForm.php index 4842f27..384edf2 --- a/frontend/modules/api/models/ProfileSearchForm.php +++ b/frontend/modules/api/models/profile/ProfileSearchForm.php @@ -1,11 +1,10 @@ load($params, ''); + + if ($model->load($params, '') && $model->login()) { + /** @var User $user */ + $user = $model->getUser(); + return [ + 'access_token' => $model->login(), + 'access_token_expired_at' => $model->getUser()->getTokenExpiredAt(), + 'id' => $user->id, + 'status' => $user->userCard->status ?? null, + 'card_id' => $user->userCard->id ?? null, + ]; + } else { + throw new BadRequestHttpException(json_encode($model->errors)); + } + } + + public function findCurrentUser(): User + { + $user = User::findOne(Yii::$app->user->id); + if (!$user){ + throw new BadRequestHttpException("User not found"); + } + + return $user; + } + /** + * @throws Exception + */ + public function changeChangePersonalData(array $params) + { + $form = new ProfileChangePersonalDataForm(); + $form->load($params); + + if (!$form->validate()){ + return $form; + } + + $user = User::findOne(['id' => Yii::$app->user->identity->getId()]);; + + $user->username = $form->newUsername; + if (!$user->save()) { + throw new Exception('User dont save'); + } + + return ['status' => 'success']; + } + + public function changeEmail(array $params) + { + $form = new ProfileChangeEmailForm(); + $form->load($params); + + if (!$form->validate()) { + return $form; + } + + $user = User::findOne(Yii::$app->user->identity->getId()); + $user->email = $form->newEmail; + $user->save(); + + return ['status' => 'success']; + } + + public function changePassword(array $params) + { + $form = new ProfileChangePasswordForm(); + $form->load($params); + + if (!$form->validate()){ + return $form; + } + + $user = User::findOne(Yii::$app->user->identity->getId()); + if ($user->validatePassword($form->password)) { + $user->password_hash = Yii::$app->security->generatePasswordHash($form->newPassword); + $user->save(); + + return ['status' => 'success']; + } + + return ['error' => 'Wrong password!']; + } +} \ No newline at end of file From 3c9204f61a0d644bb516b429719806739462be87 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 20 Oct 2023 15:07:01 +0300 Subject: [PATCH 13/37] fix Semantical Error --- frontend/modules/api/controllers/UserController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/modules/api/controllers/UserController.php b/frontend/modules/api/controllers/UserController.php index 6e2d628..81dd374 100755 --- a/frontend/modules/api/controllers/UserController.php +++ b/frontend/modules/api/controllers/UserController.php @@ -144,7 +144,7 @@ class UserController extends ApiController * @OA\MediaType( * mediaType="application/x-www-form-urlencoded", * @OA\Schema( - * required={"password", newPassword}, + * required={"password", "newPassword"}, * @OA\Property( * property="password", * type="string", From da08bcf1b2d21a38d7a1f657b7307149ae4e506f Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 24 Oct 2023 13:34:23 +0300 Subject: [PATCH 14/37] add Telegram bot tokens table and API methods --- common/config/params.php | 2 + common/models/UserTgBotToken.php | 75 ++++++++++ ...231023_122338_create_user_tg_bot_token.php | 33 +++++ .../api/controllers/UserController.php | 1 - .../api/controllers/UserTgBotController.php | 107 ++++++++++++++ .../modules/api/models/UserTgBotToken.php | 69 +++++++++ .../api/services/UserTgBotTokenService.php | 131 ++++++++++++++++++ 7 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 common/models/UserTgBotToken.php create mode 100644 console/migrations/m231023_122338_create_user_tg_bot_token.php create mode 100644 frontend/modules/api/controllers/UserTgBotController.php create mode 100644 frontend/modules/api/models/UserTgBotToken.php create mode 100644 frontend/modules/api/services/UserTgBotTokenService.php diff --git a/common/config/params.php b/common/config/params.php index c6ecfb3..00b7415 100755 --- a/common/config/params.php +++ b/common/config/params.php @@ -6,4 +6,6 @@ return [ 'senderName' => 'Chancellery ITguild mailer', 'user.passwordResetTokenExpire' => 3600, 'user.passwordMinLength' => 8, + 'tgBotTokenLength' => 6, + 'tgBotTokenValidityTime' => 180, ]; diff --git a/common/models/UserTgBotToken.php b/common/models/UserTgBotToken.php new file mode 100644 index 0000000..0c3f905 --- /dev/null +++ b/common/models/UserTgBotToken.php @@ -0,0 +1,75 @@ + 255], + [['token'], 'unique'], + [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'user_id' => 'User ID', + 'token' => 'Token', + 'created_at' => 'Created At', + 'updated_at' => 'Updated At', + 'expired_at' => 'Expired At', + ]; + } + + /** + * @return ActiveQuery + */ + public function getUser() + { + return $this->hasOne(User::className(), ['id' => 'user_id']); + } + + /** + * @param string $tokenValue + * @return bool + */ + public static function checkExistsByToken(string $tokenValue): bool + { + return self::find()->where(['token' => $tokenValue])->exists(); + } +} diff --git a/console/migrations/m231023_122338_create_user_tg_bot_token.php b/console/migrations/m231023_122338_create_user_tg_bot_token.php new file mode 100644 index 0000000..43f51de --- /dev/null +++ b/console/migrations/m231023_122338_create_user_tg_bot_token.php @@ -0,0 +1,33 @@ +createTable('{{%user_tg_bot_token}}', [ + 'id' => $this->primaryKey(), + 'user_id' => $this->integer(), + 'token' => $this->string()->notNull()->unique(), + 'created_at' => $this->dateTime(), + 'updated_at' => $this->dateTime(), + 'expired_at' => $this->dateTime(), + ]); + $this->addForeignKey('user_user_tg_bot_token', 'user_tg_bot_token', 'user_id', 'user', 'id'); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('user_tg_bot_token'); + } +} diff --git a/frontend/modules/api/controllers/UserController.php b/frontend/modules/api/controllers/UserController.php index 81dd374..60f27af 100755 --- a/frontend/modules/api/controllers/UserController.php +++ b/frontend/modules/api/controllers/UserController.php @@ -25,7 +25,6 @@ class UserController extends ApiController return $behaviors; } - private UserService $userService; public function __construct( diff --git a/frontend/modules/api/controllers/UserTgBotController.php b/frontend/modules/api/controllers/UserTgBotController.php new file mode 100644 index 0000000..4a9b4ed --- /dev/null +++ b/frontend/modules/api/controllers/UserTgBotController.php @@ -0,0 +1,107 @@ + [ + 'class' => \yii\filters\VerbFilter::class, + 'actions' => [ + 'get-token' => ['get'], + 'get-user-by-token' => ['get'], + ], + ] + ]); + } + + /** + * @var UserTgBotTokenService + */ + private UserTgBotTokenService $userTgBotTokenService; + + public function __construct( + $id, + $module, + UserTgBotTokenService $userTgBotTokenService, + $config = [] + ) + { + $this->userTgBotTokenService = $userTgBotTokenService; + parent::__construct($id, $module, $config); + } + + /** + * @OA\Get(path="/user-tg-bot/get-token", + * summary="Токен ТГ бота", + * description="Метод для возвращает токен для ТГ бота", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"UserTgBotToken"}, + * @OA\Response( + * response=200, + * description="Возвращает объект токен ТГ бота", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/UserTgBotTokenExample"), + * ), + * ), + * ) + * + * @return UserTgBotToken + * @throws Exception + */ + public function actionGetToken() + { + return $this->userTgBotTokenService->getToken(Yii::$app->user->id); + } + + /** + * + * @OA\Get(path="/user-tg-bot/get-user", + * summary="Получить данные пользователя", + * description="Метод для получения данныех пользователя по токену ТГ бота", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"UserTgBotToken"}, + * @OA\Parameter( + * name="token", + * in="query", + * example="HDAS7J", + * required=true, + * description="Токен ТГ бота", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает данные пользователя", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * ) + * + * @param string $token + * @return User|string[] + * @throws Exception + */ + public function actionGetUser(string $token) + { + return $this->userTgBotTokenService->getUserByToken($token); + } +} diff --git a/frontend/modules/api/models/UserTgBotToken.php b/frontend/modules/api/models/UserTgBotToken.php new file mode 100644 index 0000000..66d7082 --- /dev/null +++ b/frontend/modules/api/models/UserTgBotToken.php @@ -0,0 +1,69 @@ +hasOne(User::class, ['id' => 'user_id']); + } +} \ No newline at end of file diff --git a/frontend/modules/api/services/UserTgBotTokenService.php b/frontend/modules/api/services/UserTgBotTokenService.php new file mode 100644 index 0000000..8f32d80 --- /dev/null +++ b/frontend/modules/api/services/UserTgBotTokenService.php @@ -0,0 +1,131 @@ + $userId]); + if (!empty($model)) { + return $this->checkExpiredAtTime($model); + } + + return $this->createToken($userId); + } + + /** + * @param string $token + * @return User|string[] + * @throws Exception + */ + public function getUserByToken(string $token) + { + $model = UserTgBotToken::findOne(['token' => $token]); + if (!empty($model) ) { + + $currentTime = new DateTime(); + + if ($currentTime < new DateTime($model->expired_at)) { + return $model->user; + } else { + return ['error' => 'The token is expired!']; + } + + } + + return ['error' => 'Token not found!']; + } + + /** + * @return string + * @throws Exception + */ + private function generateToken(): string + { + $length = Yii::$app->params['tgBotTokenLength']; + $charactersLength = strlen($this::CHARACTERS); + do { + $value = ''; + for ($i = 0; $i < $length; $i++) { + $value .= $this::CHARACTERS[random_int(0, $charactersLength - 1)]; + } + } while ( UserTgBotToken::checkExistsByToken($value)); + + return $value; + } + + /** + * @param int $userId + * @return UserTgBotToken + * @throws Exception + */ + private function createToken(int $userId): UserTgBotToken + { + $model = new UserTgBotToken(); + $model->user_id = $userId; + $model->token = $this->generateToken(); + $model->expired_at = $this->generateExpiredAtTime(); + + if (!$model->save()) { + throw new \Exception('Токен не сохранен'); + } + + return $model; + } + + /** + * @return false|string + */ + private function generateExpiredAtTime() + { + return date("Y-m-d H:i:s",strtotime(date("Y-m-d H:i:s")) + Yii::$app->params['tgBotTokenValidityTime']); + } + + /** + * @param UserTgBotToken $model + * @return UserTgBotToken + * @throws Exception + */ + private function checkExpiredAtTime(UserTgBotToken $model) + { + $currentTime = new DateTime(); + + if ($currentTime > new DateTime($model->expired_at)) { + $this->updateExpiredAtTime($model); + } + + return $model; + } + + /** + * @param UserTgBotToken $model + * @return UserTgBotToken + * @throws Exception + */ + private function updateExpiredAtTime(UserTgBotToken $model) + { + $model->token = $this->generateToken(); + $model->expired_at = $this->generateExpiredAtTime(); + + if (!$model->save()) { + throw new \Exception('Токен не сохранен'); + } + + return $model; + } +} \ No newline at end of file From 5df755ff8b35d4ca2221ea3ba24ee5c5b32f078d Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 25 Oct 2023 14:37:29 +0300 Subject: [PATCH 15/37] add Telegram bot id dialogs table and API methods --- common/models/UserTgBotDialog.php | 78 +++++++++ common/models/UserTgBotToken.php | 14 ++ ...132757_create_user_tg_bot_dialog_table.php | 32 ++++ frontend/config/main.php | 10 ++ .../api/controllers/UserController.php | 4 +- .../api/controllers/UserTgBotController.php | 148 +++++++++++++++--- .../{ => forms}/ProfileChangeEmailForm.php | 5 +- .../{ => forms}/ProfileChangePasswordForm.php | 2 +- .../ProfileChangePersonalDataForm.php | 2 +- .../api/models/tg_bot/UserTgBotDialog.php | 33 ++++ .../models/{ => tg_bot}/UserTgBotToken.php | 2 +- .../models/tg_bot/forms/TgBotDialogForm.php | 30 ++++ .../models/tg_bot/forms/TgBotDialogIdForm.php | 28 ++++ .../models/tg_bot/forms/TgBotUserIdForm.php | 28 ++++ frontend/modules/api/services/UserService.php | 6 +- .../api/services/UserTgBotTokenService.php | 61 +++++++- 16 files changed, 450 insertions(+), 33 deletions(-) create mode 100644 common/models/UserTgBotDialog.php create mode 100644 console/migrations/m231024_132757_create_user_tg_bot_dialog_table.php rename frontend/modules/api/models/profile/{ => forms}/ProfileChangeEmailForm.php (84%) rename frontend/modules/api/models/profile/{ => forms}/ProfileChangePasswordForm.php (89%) rename frontend/modules/api/models/profile/{ => forms}/ProfileChangePersonalDataForm.php (89%) create mode 100644 frontend/modules/api/models/tg_bot/UserTgBotDialog.php rename frontend/modules/api/models/{ => tg_bot}/UserTgBotToken.php (96%) create mode 100644 frontend/modules/api/models/tg_bot/forms/TgBotDialogForm.php create mode 100644 frontend/modules/api/models/tg_bot/forms/TgBotDialogIdForm.php create mode 100644 frontend/modules/api/models/tg_bot/forms/TgBotUserIdForm.php diff --git a/common/models/UserTgBotDialog.php b/common/models/UserTgBotDialog.php new file mode 100644 index 0000000..0c32212 --- /dev/null +++ b/common/models/UserTgBotDialog.php @@ -0,0 +1,78 @@ + TimestampBehavior::class, + 'createdAtAttribute' => 'created_at', + 'updatedAtAttribute' => 'updated_at', + 'value' => new Expression('NOW()'), + ], + ]; + } + + + /** + * {@inheritdoc} + */ + public function rules() + { + return [ + [['user_id', 'dialog_id'], 'integer'], + [['dialog_id'], 'required'], + [['created_at', 'updated_at'], 'safe'], + [['dialog_id'], 'unique'], + [['user_id'], 'unique'], + [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'user_id' => 'User ID', + 'dialog_id' => 'Dialog ID', + 'created_at' => 'Created At', + 'updated_at' => 'Updated At', + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getUser() + { + return $this->hasOne(User::className(), ['id' => 'user_id']); + } +} diff --git a/common/models/UserTgBotToken.php b/common/models/UserTgBotToken.php index 0c3f905..7dbc899 100644 --- a/common/models/UserTgBotToken.php +++ b/common/models/UserTgBotToken.php @@ -2,7 +2,9 @@ namespace common\models; +use yii\behaviors\TimestampBehavior; use yii\db\ActiveQuery; +use yii\db\Expression; /** * This is the model class for table "user_tg_bot_token". @@ -26,6 +28,18 @@ class UserTgBotToken extends \yii\db\ActiveRecord return 'user_tg_bot_token'; } + public function behaviors() + { + return [ + [ + 'class' => TimestampBehavior::class, + 'createdAtAttribute' => 'created_at', + 'updatedAtAttribute' => 'updated_at', + 'value' => new Expression('NOW()'), + ], + ]; + } + /** * {@inheritdoc} */ diff --git a/console/migrations/m231024_132757_create_user_tg_bot_dialog_table.php b/console/migrations/m231024_132757_create_user_tg_bot_dialog_table.php new file mode 100644 index 0000000..b99ebc5 --- /dev/null +++ b/console/migrations/m231024_132757_create_user_tg_bot_dialog_table.php @@ -0,0 +1,32 @@ +createTable('{{%user_tg_bot_dialog}}', [ + 'id' => $this->primaryKey(), + 'user_id' => $this->integer(), + 'dialog_id' => $this->integer()->notNull()->unique(), + 'created_at' => $this->dateTime(), + 'updated_at' => $this->dateTime(), + ]); + $this->addForeignKey('user_user_tg_bot_dialog', 'user_tg_bot_dialog', 'user_id', 'user', 'id'); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('{{%user_tg_bot_dialog}}'); + } +} diff --git a/frontend/config/main.php b/frontend/config/main.php index aebc7f2..4285610 100755 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -86,6 +86,16 @@ return [ 'api/profile/' => 'api/profile/index', 'api/reports/' => 'api/reports/view', '' => 'card/user-card/index', + + + 'GET api/tg-bot/token' => 'api/user-tg-bot/get-token', + 'GET api/tg-bot/user' => 'api/user-tg-bot/get-user', + + 'POST api/tg-bot/dialog/create' => 'api/user-tg-bot/set-dialog', + 'GET api/tg-bot/dialog/user/id' => 'api/user-tg-bot/get-user-id-by-dialog-id', + 'GET api/tg-bot/dialog/dialog/id' => 'api/user-tg-bot/get-dialog-id-by-user-id', + + ['class' => 'yii\rest\UrlRule', 'controller' => 'skills'], ], ], diff --git a/frontend/modules/api/controllers/UserController.php b/frontend/modules/api/controllers/UserController.php index 60f27af..bf07ba8 100755 --- a/frontend/modules/api/controllers/UserController.php +++ b/frontend/modules/api/controllers/UserController.php @@ -4,8 +4,8 @@ namespace frontend\modules\api\controllers; use common\models\User; -use frontend\modules\api\models\profile\ProfileChangeEmailForm; -use frontend\modules\api\models\profile\ProfileChangePersonalDataForm; +use frontend\modules\api\models\profile\forms\ProfileChangeEmailForm; +use frontend\modules\api\models\profile\forms\ProfileChangePersonalDataForm; use frontend\modules\api\services\UserService; use Yii; use yii\base\InvalidConfigException; diff --git a/frontend/modules/api/controllers/UserTgBotController.php b/frontend/modules/api/controllers/UserTgBotController.php index 4a9b4ed..f17d19b 100644 --- a/frontend/modules/api/controllers/UserTgBotController.php +++ b/frontend/modules/api/controllers/UserTgBotController.php @@ -5,27 +5,13 @@ namespace frontend\modules\api\controllers; use Exception; use frontend\modules\api\models\profile\User; -use frontend\modules\api\models\UserTgBotToken; +use frontend\modules\api\models\tg_bot\forms\TgBotDialogForm; +use frontend\modules\api\models\tg_bot\UserTgBotToken; use frontend\modules\api\services\UserTgBotTokenService; use Yii; -use yii\helpers\ArrayHelper; class UserTgBotController extends ApiController { - public function behaviors(): array - { - return ArrayHelper::merge(parent::behaviors(), [ - - 'verbs' => [ - 'class' => \yii\filters\VerbFilter::class, - 'actions' => [ - 'get-token' => ['get'], - 'get-user-by-token' => ['get'], - ], - ] - ]); - } - /** * @var UserTgBotTokenService */ @@ -43,13 +29,13 @@ class UserTgBotController extends ApiController } /** - * @OA\Get(path="/user-tg-bot/get-token", + * @OA\Get(path="/tg-bot/token", * summary="Токен ТГ бота", * description="Метод для возвращает токен для ТГ бота", * security={ * {"bearerAuth": {}} * }, - * tags={"UserTgBotToken"}, + * tags={"TgBot"}, * @OA\Response( * response=200, * description="Возвращает объект токен ТГ бота", @@ -63,20 +49,20 @@ class UserTgBotController extends ApiController * @return UserTgBotToken * @throws Exception */ - public function actionGetToken() + public function actionGetToken(): UserTgBotToken { return $this->userTgBotTokenService->getToken(Yii::$app->user->id); } /** * - * @OA\Get(path="/user-tg-bot/get-user", + * @OA\Get(path="/tg-bot/user", * summary="Получить данные пользователя", * description="Метод для получения данныех пользователя по токену ТГ бота", * security={ * {"bearerAuth": {}} * }, - * tags={"UserTgBotToken"}, + * tags={"TgBot"}, * @OA\Parameter( * name="token", * in="query", @@ -104,4 +90,124 @@ class UserTgBotController extends ApiController { return $this->userTgBotTokenService->getUserByToken($token); } + + /** + * + * @OA\Post(path="/tg-bot/dialog/create", + * summary="Сохранить новый id диалога", + * description="Метод создает новую запись с id пользователя и id диалога", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TgBot"}, + * @OA\Parameter( + * name="userId", + * in="query", + * example="1", + * required=true, + * description="id пользователя", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="dialogId", + * in="query", + * example="2355", + * required=true, + * description="id диалога", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * ) + * + * @return TgBotDialogForm|string[] + * @throws Exception + */ + public function actionSetDialog() + { + return $this->userTgBotTokenService->createDialog(Yii::$app->request->post()); + } + + /** + * + * @OA\Get(path="/tg-bot/dialog/dialog/id", + * summary="Получить id диалога по id пользователя", + * description="Метод для получения id пользователя по id пользователя", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TgBot"}, + * @OA\Parameter( + * name="userId", + * in="query", + * example="1", + * required=true, + * description="id пользователя", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает dialog_id", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * ) + * + * @param string $userId + * @return array + * @throws Exception + */ + public function actionGetDialogIdByUserId(string $userId): array + { + return $this->userTgBotTokenService->getDialogIdByUserId($userId); + } + + /** + * + * @OA\Get(path="/tg-bot/dialog/user/id", + * summary="Получить id пользователя по id диалога", + * description="Метод для получения id пользователя по id диалога", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TgBot"}, + * @OA\Parameter( + * name="dialogId", + * in="query", + * example="225", + * required=true, + * description="id диалога", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает user_id", + * @OA\MediaType( + * mediaType="application/json", + * ), + * ), + * ) + * + * @param string $dialogId + * @return array + * @throws Exception + */ + public function actionGetUserIdByDialogId(string $dialogId): array + { + return $this->userTgBotTokenService->getUserIdByDialogId($dialogId); + } } diff --git a/frontend/modules/api/models/profile/ProfileChangeEmailForm.php b/frontend/modules/api/models/profile/forms/ProfileChangeEmailForm.php similarity index 84% rename from frontend/modules/api/models/profile/ProfileChangeEmailForm.php rename to frontend/modules/api/models/profile/forms/ProfileChangeEmailForm.php index aabf8c1..6be7a66 100644 --- a/frontend/modules/api/models/profile/ProfileChangeEmailForm.php +++ b/frontend/modules/api/models/profile/forms/ProfileChangeEmailForm.php @@ -1,11 +1,11 @@ hasOne(User::class, ['id' => 'user_id']); + } +} \ No newline at end of file diff --git a/frontend/modules/api/models/UserTgBotToken.php b/frontend/modules/api/models/tg_bot/UserTgBotToken.php similarity index 96% rename from frontend/modules/api/models/UserTgBotToken.php rename to frontend/modules/api/models/tg_bot/UserTgBotToken.php index 66d7082..14c0fed 100644 --- a/frontend/modules/api/models/UserTgBotToken.php +++ b/frontend/modules/api/models/tg_bot/UserTgBotToken.php @@ -1,6 +1,6 @@ load($params); + + if (!$form->validate()){ + return $form; + } + + $dialog = new UserTgBotDialog(); + $dialog->user_id = $form->userId; + $dialog->dialog_id = $form->dialogId; + + if (!$dialog->save()) { + throw new Exception('User dont save'); + } + + return ['status' => 'success']; + } + + /** + * @param string $userId + * @return array + * @throws Exception + */ + public function getDialogIdByUserId(string $userId) + { + $model = UserTgBotDialog::findOne(['user_id' => $userId]); + + if (!$model) { + throw new \Exception('dialog_id не найден!'); + } + + return ['dialog_id' => $model->dialog_id]; + } + + /** + * @param string $dialogId + * @return array + * @throws Exception + */ + public function getUserIdByDialogId(string $dialogId) + { + $model = UserTgBotDialog::findOne(['dialog_id' => $dialogId]); + + if (!$model) { + throw new \Exception('user_id не найден!'); + } + + return ['user_id' => $model->user_id]; + } } \ No newline at end of file From 93f51f39c6d712ea96284dc1a33c239b818c38ae Mon Sep 17 00:00:00 2001 From: iIronside Date: Thu, 26 Oct 2023 09:55:39 +0300 Subject: [PATCH 16/37] fix swagger doc --- frontend/modules/api/controllers/QuestionController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/modules/api/controllers/QuestionController.php b/frontend/modules/api/controllers/QuestionController.php index 9823ba3..4881fc0 100644 --- a/frontend/modules/api/controllers/QuestionController.php +++ b/frontend/modules/api/controllers/QuestionController.php @@ -37,12 +37,12 @@ class QuestionController extends ApiController * }, * tags={"Tests"}, * @OA\Parameter( - * name="user", + * name="uuid", * in="query", * required=true, * description="UUID анкеты назначеной пользователю", * @OA\Schema( - * type="integer", + * type="string", * ) * ), * @OA\Response( From 700f79eb5cf83b3c3b72ca2b8fff10e61c3a337d Mon Sep 17 00:00:00 2001 From: iIronside Date: Thu, 26 Oct 2023 11:37:04 +0300 Subject: [PATCH 17/37] add column description to questionnaire table --- .../views/questionnaire/_form.php | 2 ++ .../views/questionnaire/index.php | 1 + .../views/questionnaire/view.php | 1 + common/models/Questionnaire.php | 4 ++- common/models/UserQuestionnaire.php | 11 +------- ...umn_description_to_questionnaire_table.php | 25 +++++++++++++++++++ .../UserQuestionnaireController.php | 9 ++++--- .../modules/api/models/UserQuestionnaire.php | 24 ++++++++++++++++++ .../services/UserQuestionnaireService.php | 16 +++--------- 9 files changed, 66 insertions(+), 27 deletions(-) create mode 100644 console/migrations/m231026_071555_add_column_description_to_questionnaire_table.php rename {common => frontend/modules/api}/services/UserQuestionnaireService.php (83%) diff --git a/backend/modules/questionnaire/views/questionnaire/_form.php b/backend/modules/questionnaire/views/questionnaire/_form.php index 82750c5..aa632b8 100644 --- a/backend/modules/questionnaire/views/questionnaire/_form.php +++ b/backend/modules/questionnaire/views/questionnaire/_form.php @@ -46,6 +46,8 @@ use yii\widgets\ActiveForm; ] ]) ?> + field($model, 'description')->textInput(['maxlength' => true]) ?> +
'btn btn-success']) ?>
diff --git a/backend/modules/questionnaire/views/questionnaire/index.php b/backend/modules/questionnaire/views/questionnaire/index.php index fb1c9ad..c304741 100644 --- a/backend/modules/questionnaire/views/questionnaire/index.php +++ b/backend/modules/questionnaire/views/questionnaire/index.php @@ -49,6 +49,7 @@ $this->params['breadcrumbs'][] = $this->title; return TimeHelper::limitTime($model->time_limit); } ], + 'description', ['class' => 'yii\grid\ActionColumn'], ], ]); ?> diff --git a/backend/modules/questionnaire/views/questionnaire/view.php b/backend/modules/questionnaire/views/questionnaire/view.php index 719de22..cecd6c1 100644 --- a/backend/modules/questionnaire/views/questionnaire/view.php +++ b/backend/modules/questionnaire/views/questionnaire/view.php @@ -54,6 +54,7 @@ YiiAsset::register($this); 'format' => 'raw', 'value' => TimeHelper::limitTime($model->time_limit), ], + 'description', ], ]) ?> diff --git a/common/models/Questionnaire.php b/common/models/Questionnaire.php index 5085aa6..3cc8f58 100644 --- a/common/models/Questionnaire.php +++ b/common/models/Questionnaire.php @@ -15,6 +15,7 @@ use yii\helpers\ArrayHelper; * @property int $category_id * @property string $title * @property int $status + * @property string $description * @property string $created_at * @property string $updated_at * @property string $time_limit @@ -55,7 +56,7 @@ class Questionnaire extends ActiveRecord [['category_id', 'status'], 'integer'], [['created_at', 'updated_at', 'time_limit'], 'safe'], ['title', 'unique'], - [['title'], 'string', 'max' => 255], + [['title', 'description'], 'string', 'max' => 255], ['status', 'default', 'value' => true], [['category_id'], 'exist', 'skipOnError' => true, 'targetClass' => QuestionnaireCategory::className(), 'targetAttribute' => ['category_id' => 'id']], ]; @@ -70,6 +71,7 @@ class Questionnaire extends ActiveRecord 'id' => 'ID', 'category_id' => 'Категория', 'title' => 'Название анкеты', + 'description' => 'Описание', 'status' => 'Статус', 'created_at' => 'Created At', 'updated_at' => 'Updated At', diff --git a/common/models/UserQuestionnaire.php b/common/models/UserQuestionnaire.php index 5882d60..7927db6 100644 --- a/common/models/UserQuestionnaire.php +++ b/common/models/UserQuestionnaire.php @@ -179,18 +179,9 @@ class UserQuestionnaire extends ActiveRecord public static function findActiveUserQuestionnaires($user_id): array { - $models = self::find() + return self::find() ->where(['user_id' => $user_id]) ->andWhere(['not', ['user_questionnaire.status' => 0]]) ->all(); - - $modelsArr = array(); - foreach ($models as $model) { - $modelsArr[] = array_merge($model->toArray(), [ - 'questionnaire_title' => $model->getQuestionnaireTitle() - ]); - } - - return $modelsArr; } } diff --git a/console/migrations/m231026_071555_add_column_description_to_questionnaire_table.php b/console/migrations/m231026_071555_add_column_description_to_questionnaire_table.php new file mode 100644 index 0000000..b00377f --- /dev/null +++ b/console/migrations/m231026_071555_add_column_description_to_questionnaire_table.php @@ -0,0 +1,25 @@ +addColumn('questionnaire', 'description', $this->string(255)); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropColumn('questionnaire', 'description'); + } +} diff --git a/frontend/modules/api/controllers/UserQuestionnaireController.php b/frontend/modules/api/controllers/UserQuestionnaireController.php index 285b831..c769572 100644 --- a/frontend/modules/api/controllers/UserQuestionnaireController.php +++ b/frontend/modules/api/controllers/UserQuestionnaireController.php @@ -2,7 +2,8 @@ namespace frontend\modules\api\controllers; -use common\services\UserQuestionnaireService; +use frontend\modules\api\models\UserQuestionnaire; +use frontend\modules\api\services\UserQuestionnaireService; use yii\helpers\ArrayHelper; use yii\web\NotFoundHttpException; use yii\web\ServerErrorHttpException; @@ -99,7 +100,7 @@ class UserQuestionnaireController extends ApiController * @throws NotFoundHttpException * @throws ServerErrorHttpException */ - public function actionQuestionnaireCompleted($user_questionnaire_uuid) + public function actionQuestionnaireCompleted($user_questionnaire_uuid): UserQuestionnaire { $userQuestionnaireModel = UserQuestionnaireService::calculateScore($user_questionnaire_uuid); if ($userQuestionnaireModel->errors) { @@ -142,7 +143,7 @@ class UserQuestionnaireController extends ApiController * ) * @throws ServerErrorHttpException */ - public function actionGetPointsNumber($user_questionnaire_uuid) + public function actionGetPointsNumber($user_questionnaire_uuid): array { $questionPointsNumber = UserQuestionnaireService::getPointsNumber($user_questionnaire_uuid); if (empty($questionPointsNumber)) { @@ -185,7 +186,7 @@ class UserQuestionnaireController extends ApiController * ) * @throws ServerErrorHttpException */ - public function actionGetQuestionNumber($user_questionnaire_uuid) + public function actionGetQuestionNumber($user_questionnaire_uuid): array { $questionNumber = UserQuestionnaireService::getQuestionNumber($user_questionnaire_uuid); if (empty($questionNumber)) { diff --git a/frontend/modules/api/models/UserQuestionnaire.php b/frontend/modules/api/models/UserQuestionnaire.php index 7e8993a..5ea7c37 100644 --- a/frontend/modules/api/models/UserQuestionnaire.php +++ b/frontend/modules/api/models/UserQuestionnaire.php @@ -99,5 +99,29 @@ namespace frontend\modules\api\models; */ class UserQuestionnaire extends \common\models\UserQuestionnaire { + public function fields(): array + { + return [ + 'user_id', + 'uuid', + 'score', + 'status', + 'percent_correct_answers', + 'testing_date', + 'questionnaire_title' => function() { + return $this->questionnaire->title; + }, + 'description' => function() { + return $this->questionnaire->description; + }, + ]; + } + /** + * @return string[] + */ + public function extraFields(): array + { + return []; + } } \ No newline at end of file diff --git a/common/services/UserQuestionnaireService.php b/frontend/modules/api/services/UserQuestionnaireService.php similarity index 83% rename from common/services/UserQuestionnaireService.php rename to frontend/modules/api/services/UserQuestionnaireService.php index a21410b..9f1340f 100644 --- a/common/services/UserQuestionnaireService.php +++ b/frontend/modules/api/services/UserQuestionnaireService.php @@ -1,9 +1,10 @@ Date: Thu, 26 Oct 2023 17:38:56 +0300 Subject: [PATCH 18/37] fix corse --- frontend/config/main.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/frontend/config/main.php b/frontend/config/main.php index 4285610..3c6ec8f 100755 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -1,4 +1,7 @@ [ + 'class' => GsCors::class, + 'cors' => [ + 'Origin' => ['*'], + //'Access-Control-Allow-Credentials' => true, + 'Access-Control-Allow-Headers' => [ + 'Access-Control-Allow-Origin', + 'Access-Control-Allow-Methods', + 'Content-Type', + 'Access-Control-Allow-Headers', + 'Authorization', + 'X-Requested-With' + ], + 'Access-Control-Allow-Methods' => ['POST', 'GET', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + 'Access-Control-Request-Method' => ['POST', 'GET', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + ] + ], 'request' => [ 'csrfParam' => '_csrf-frontend', 'baseUrl' => '', @@ -95,6 +114,8 @@ return [ 'GET api/tg-bot/dialog/user/id' => 'api/user-tg-bot/get-user-id-by-dialog-id', 'GET api/tg-bot/dialog/dialog/id' => 'api/user-tg-bot/get-dialog-id-by-user-id', + 'GET api/tg-bot/auth' => 'api/user-tg-bot/auth', + ['class' => 'yii\rest\UrlRule', 'controller' => 'skills'], ], From 6f999216711412d74750f036fca455cfb667ed5b Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 31 Oct 2023 12:14:06 +0300 Subject: [PATCH 19/37] remove method Set Response --- common/services/UserResponseService.php | 13 ---- .../controllers/UserResponseController.php | 69 ------------------- 2 files changed, 82 deletions(-) diff --git a/common/services/UserResponseService.php b/common/services/UserResponseService.php index d6eb6ee..af68613 100644 --- a/common/services/UserResponseService.php +++ b/common/services/UserResponseService.php @@ -9,19 +9,6 @@ use yii\web\ServerErrorHttpException; class UserResponseService { - /** - * @throws BadRequestHttpException - * @throws ServerErrorHttpException - */ - public static function createUserResponse($userResponseParams): UserResponse - { - $userResponse = new UserResponse(); - $userResponse->load($userResponseParams, ''); - (new UserResponseService)->validateResponseModel($userResponse); - (new UserResponseService)->saveModel($userResponse); - return $userResponse; - } - /** * @throws BadRequestHttpException * @throws ServerErrorHttpException diff --git a/frontend/modules/api/controllers/UserResponseController.php b/frontend/modules/api/controllers/UserResponseController.php index 622b030..7646ed8 100644 --- a/frontend/modules/api/controllers/UserResponseController.php +++ b/frontend/modules/api/controllers/UserResponseController.php @@ -2,7 +2,6 @@ namespace frontend\modules\api\controllers; -use common\models\UserResponse; use common\services\UserResponseService; use Yii; use yii\base\InvalidConfigException; @@ -19,74 +18,6 @@ class UserResponseController extends ApiController ]; } - /** - * @OA\Post(path="/user-response/set-response", - * summary="Добавить ответ пользователя", - * description="Добавление ответа на вопрос от пользователя", - * security={ - * {"bearerAuth": {}} - * }, - * tags={"Tests"}, - * @OA\Parameter( - * name="user_id", - * in="query", - * required=true, - * description="ID пользователя", - * @OA\Schema( - * type="integer", - * ) - * ), - * @OA\Parameter( - * name="question_id", - * in="query", - * required=true, - * description="ID вопроса", - * @OA\Schema( - * type="integer", - * ) - * ), - * @OA\Parameter( - * name="response_body", - * in="query", - * required=true, - * description="Ответ пользователя", - * @OA\Schema( - * type="string", - * ) - * ), - * @OA\Parameter( - * name="user_questionnaire_uuid", - * in="query", - * required=true, - * description="UUID анкеты назначенной пользователю", - * @OA\Schema( - * type="string", - * ) - * ), - * - * - * @OA\Response( - * response=200, - * description="Возвращает ответ", - * @OA\MediaType( - * mediaType="application/json", - * @OA\Schema(ref="#/components/schemas/UserResponseExample"), - * ), - * ), - * ) - * - * @throws InvalidConfigException - * @throws ServerErrorHttpException|BadRequestHttpException - */ - public function actionSetResponse(): UserResponse - { - $userResponseModel = UserResponseService::createUserResponse(Yii::$app->getRequest()->getBodyParams()); - if ($userResponseModel->errors) { - throw new ServerErrorHttpException(json_encode($userResponseModel->errors)); - } - return $userResponseModel; - } - /** * @OA\Post(path="/user-response/set-responses", * summary="Добавить массив ответов пользователя", From 934d2ccd0caad5a125256c2879344ea3df06a5fd Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 31 Oct 2023 13:05:15 +0300 Subject: [PATCH 20/37] improved validation in ProjectColumn --- common/models/ProjectColumn.php | 2 +- .../modules/api/controllers/ProjectColumnController.php | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/common/models/ProjectColumn.php b/common/models/ProjectColumn.php index cc8795e..4b5a62c 100644 --- a/common/models/ProjectColumn.php +++ b/common/models/ProjectColumn.php @@ -65,7 +65,7 @@ class ProjectColumn extends \yii\db\ActiveRecord return [ [['title', 'project_id'], 'required'], [['project_id', 'status', 'priority'], 'integer'], - [['project_id', 'title'], 'unique', 'targetAttribute' => ['project_id', 'title']], + ['title', 'unique', 'targetAttribute' => ['title','project_id' => 'status']], [['created_at', 'updated_at'], 'safe'], [['title'], 'string', 'max' => 255], [['project_id'], 'exist', 'skipOnError' => true, 'targetClass' => Project::className(), 'targetAttribute' => ['project_id' => 'id']], diff --git a/frontend/modules/api/controllers/ProjectColumnController.php b/frontend/modules/api/controllers/ProjectColumnController.php index b7e0e8f..e690a02 100644 --- a/frontend/modules/api/controllers/ProjectColumnController.php +++ b/frontend/modules/api/controllers/ProjectColumnController.php @@ -14,7 +14,6 @@ class ProjectColumnController extends ApiController public function verbs(): array { return [ - 'get-column' => ['get'], 'get-column-list' => ['get'], 'create-column' => ['post'], 'set-priority' => ['post'], @@ -22,11 +21,6 @@ class ProjectColumnController extends ApiController ]; } - public function actionGetColumn() - { - - } - /** * * @OA\Get(path="/project-column/get-column-list", From da97a677971ed8d86ad820bcf72c9c6915168f9c Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 1 Nov 2023 14:59:52 +0300 Subject: [PATCH 21/37] change url rule for userTgBotController --- frontend/config/main.php | 28 ------------------- .../api/controllers/UserTgBotController.php | 14 +++++----- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/frontend/config/main.php b/frontend/config/main.php index 3c6ec8f..49c31ea 100755 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -50,23 +50,6 @@ return [ ], ], ], - 'corsFilter' => [ - 'class' => GsCors::class, - 'cors' => [ - 'Origin' => ['*'], - //'Access-Control-Allow-Credentials' => true, - 'Access-Control-Allow-Headers' => [ - 'Access-Control-Allow-Origin', - 'Access-Control-Allow-Methods', - 'Content-Type', - 'Access-Control-Allow-Headers', - 'Authorization', - 'X-Requested-With' - ], - 'Access-Control-Allow-Methods' => ['POST', 'GET', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - 'Access-Control-Request-Method' => ['POST', 'GET', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - ] - ], 'request' => [ 'csrfParam' => '_csrf-frontend', 'baseUrl' => '', @@ -106,17 +89,6 @@ return [ 'api/reports/' => 'api/reports/view', '' => 'card/user-card/index', - - 'GET api/tg-bot/token' => 'api/user-tg-bot/get-token', - 'GET api/tg-bot/user' => 'api/user-tg-bot/get-user', - - 'POST api/tg-bot/dialog/create' => 'api/user-tg-bot/set-dialog', - 'GET api/tg-bot/dialog/user/id' => 'api/user-tg-bot/get-user-id-by-dialog-id', - 'GET api/tg-bot/dialog/dialog/id' => 'api/user-tg-bot/get-dialog-id-by-user-id', - - 'GET api/tg-bot/auth' => 'api/user-tg-bot/auth', - - ['class' => 'yii\rest\UrlRule', 'controller' => 'skills'], ], ], diff --git a/frontend/modules/api/controllers/UserTgBotController.php b/frontend/modules/api/controllers/UserTgBotController.php index f17d19b..7ab2b31 100644 --- a/frontend/modules/api/controllers/UserTgBotController.php +++ b/frontend/modules/api/controllers/UserTgBotController.php @@ -29,7 +29,7 @@ class UserTgBotController extends ApiController } /** - * @OA\Get(path="/tg-bot/token", + * @OA\Get(path="/user-tg-bot/get-token", * summary="Токен ТГ бота", * description="Метод для возвращает токен для ТГ бота", * security={ @@ -56,7 +56,7 @@ class UserTgBotController extends ApiController /** * - * @OA\Get(path="/tg-bot/user", + * @OA\Get(path="/user-tg-bot/get-user", * summary="Получить данные пользователя", * description="Метод для получения данныех пользователя по токену ТГ бота", * security={ @@ -93,7 +93,7 @@ class UserTgBotController extends ApiController /** * - * @OA\Post(path="/tg-bot/dialog/create", + * @OA\Post(path="/user-tg-bot/set-dialog", * summary="Сохранить новый id диалога", * description="Метод создает новую запись с id пользователя и id диалога", * security={ @@ -139,7 +139,7 @@ class UserTgBotController extends ApiController /** * - * @OA\Get(path="/tg-bot/dialog/dialog/id", + * @OA\Get(path="/user-tg-bot/dialog/get-dialog-id", * summary="Получить id диалога по id пользователя", * description="Метод для получения id пользователя по id пользователя", * security={ @@ -169,14 +169,14 @@ class UserTgBotController extends ApiController * @return array * @throws Exception */ - public function actionGetDialogIdByUserId(string $userId): array + public function actionGetDialogId(string $userId): array { return $this->userTgBotTokenService->getDialogIdByUserId($userId); } /** * - * @OA\Get(path="/tg-bot/dialog/user/id", + * @OA\Get(path="/user-tg-bot/get-user-id", * summary="Получить id пользователя по id диалога", * description="Метод для получения id пользователя по id диалога", * security={ @@ -206,7 +206,7 @@ class UserTgBotController extends ApiController * @return array * @throws Exception */ - public function actionGetUserIdByDialogId(string $dialogId): array + public function actionGetUserId(string $dialogId): array { return $this->userTgBotTokenService->getUserIdByDialogId($dialogId); } From 8ca7bef498c4afac6ba4a054ad63e69546de542a Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 1 Nov 2023 15:08:21 +0300 Subject: [PATCH 22/37] change characters to token code --- frontend/modules/api/services/UserTgBotTokenService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/modules/api/services/UserTgBotTokenService.php b/frontend/modules/api/services/UserTgBotTokenService.php index 0a95a67..3c9cb21 100644 --- a/frontend/modules/api/services/UserTgBotTokenService.php +++ b/frontend/modules/api/services/UserTgBotTokenService.php @@ -13,7 +13,7 @@ use Yii; class UserTgBotTokenService { - const CHARACTERS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + const CHARACTERS = '0123456789'; /** * @param int $userId From 52f8cb312f2570a3b6b53250b9d02b62eee4f1c7 Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 1 Nov 2023 16:59:02 +0300 Subject: [PATCH 23/37] add tg bot auth method --- .../api/controllers/UserTgBotController.php | 55 +++++++ .../api/models/tg_bot/UserTgBotToken.php | 13 ++ .../tg_bot/forms/UserTgBotLoginForm.php | 56 +++++++ frontend/modules/api/services/UserService.php | 1 - .../api/services/UserTgBotTokenService.php | 140 ++++++++++-------- 5 files changed, 206 insertions(+), 59 deletions(-) create mode 100644 frontend/modules/api/models/tg_bot/forms/UserTgBotLoginForm.php diff --git a/frontend/modules/api/controllers/UserTgBotController.php b/frontend/modules/api/controllers/UserTgBotController.php index 7ab2b31..927ccd5 100644 --- a/frontend/modules/api/controllers/UserTgBotController.php +++ b/frontend/modules/api/controllers/UserTgBotController.php @@ -17,6 +17,16 @@ class UserTgBotController extends ApiController */ private UserTgBotTokenService $userTgBotTokenService; + public function behaviors() + { + $behaviors = parent::behaviors(); + if($this->action->id == "auth"){ + unset($behaviors['authenticator']); + } + + return $behaviors; + } + public function __construct( $id, $module, @@ -210,4 +220,49 @@ class UserTgBotController extends ApiController { return $this->userTgBotTokenService->getUserIdByDialogId($dialogId); } + + /** + * + * @OA\Post(path="/user-tg-bot/auth", + * summary="Аутентификация", + * description="Метод производит аутентификацию пользователя по токену ТГ бта", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TgBot"}, + * @OA\Parameter( + * name="token", + * in="query", + * example="1", + * required=true, + * description="токен пользователя", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * @OA\MediaType( + * mediaType="application/json", + * * @OA\Schema( + * schema="schemae_5cfb24156100e_category", + * @OA\Property(property="access_token",type="string",description="Category ID",example="HclquHysW2Y6LecQfM_ZZTjL4kBz-jOi"), + * @OA\Property(property="access_token_expired_at",type="dateTime",description="Expired at",example="2023-11-08"), + * @OA\Property(property="id",type="integer",description="ID",example=1), + * @OA\Property(property="status",type="integer",description="status",example=1), + * @OA\Property(property="card_id",type="integer",description="Card ID",example=1), + * ), + * ), + + * ), + * ) + * + * @return array + * @throws \yii\web\BadRequestHttpException + */ + public function actionAuth() + { + return $this->userTgBotTokenService->auth(Yii::$app->request->post()); + } } diff --git a/frontend/modules/api/models/tg_bot/UserTgBotToken.php b/frontend/modules/api/models/tg_bot/UserTgBotToken.php index 14c0fed..e4e273e 100644 --- a/frontend/modules/api/models/tg_bot/UserTgBotToken.php +++ b/frontend/modules/api/models/tg_bot/UserTgBotToken.php @@ -4,6 +4,7 @@ namespace frontend\modules\api\models\tg_bot; use frontend\modules\api\models\profile\User; +use Yii; use yii\db\ActiveQuery; /** @@ -43,6 +44,8 @@ use yii\db\ActiveQuery; */ class UserTgBotToken extends \common\models\UserTgBotToken { + const EXPIRE_TIME = 604800; // token expiration time, valid for 7 days + public function fields(): array { return [ @@ -59,6 +62,16 @@ class UserTgBotToken extends \common\models\UserTgBotToken return []; } + public function updateToken() + { + $access_token = $this->user->generateAccessToken(); + $this->user->access_token_expired_at = date('Y-m-d', time() + static::EXPIRE_TIME); + $this->user->save(false); + + Yii::$app->user->login($this->user, static::EXPIRE_TIME); + return $access_token; + } + /** * @return ActiveQuery */ diff --git a/frontend/modules/api/models/tg_bot/forms/UserTgBotLoginForm.php b/frontend/modules/api/models/tg_bot/forms/UserTgBotLoginForm.php new file mode 100644 index 0000000..0138cd4 --- /dev/null +++ b/frontend/modules/api/models/tg_bot/forms/UserTgBotLoginForm.php @@ -0,0 +1,56 @@ + $this->token]); + + if (!empty($model)) { + + $currentTime = new DateTime(); + + if ($currentTime > new DateTime($model->expired_at)) { + $this->addError('token', 'Токен не действителен!'); + } + } else { + $this->addError('token', 'Пользователь с соответствующим токеном не найден!'); + } + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } + + public function getUser() + { + return User::findOne($this->userId); + } +} diff --git a/frontend/modules/api/services/UserService.php b/frontend/modules/api/services/UserService.php index e8cd5df..1512210 100644 --- a/frontend/modules/api/services/UserService.php +++ b/frontend/modules/api/services/UserService.php @@ -16,7 +16,6 @@ class UserService public function login(array $params) { $model = new LoginForm(); - $model->load($params, ''); if ($model->load($params, '') && $model->login()) { /** @var User $user */ diff --git a/frontend/modules/api/services/UserTgBotTokenService.php b/frontend/modules/api/services/UserTgBotTokenService.php index 3c9cb21..e6ca60f 100644 --- a/frontend/modules/api/services/UserTgBotTokenService.php +++ b/frontend/modules/api/services/UserTgBotTokenService.php @@ -5,16 +5,97 @@ namespace frontend\modules\api\services; use DateTime; use Exception; -use frontend\modules\api\models\tg_bot\forms\TgBotDialogForm; use frontend\modules\api\models\profile\User; +use frontend\modules\api\models\tg_bot\forms\TgBotDialogForm; +use frontend\modules\api\models\tg_bot\forms\UserTgBotLoginForm; use frontend\modules\api\models\tg_bot\UserTgBotDialog; use frontend\modules\api\models\tg_bot\UserTgBotToken; use Yii; +use yii\web\BadRequestHttpException; class UserTgBotTokenService { const CHARACTERS = '0123456789'; + + public function auth(array $params) + { + /** @var UserTgBotToken $model */ + $model = new UserTgBotLoginForm; + + if ($model->load($params, '') && $model->validate()) { + + $userTgBotToken = UserTgBotToken::findOne(['token' => $model->token]); + $user = $userTgBotToken->user; + return [ + 'access_token' => $userTgBotToken->updateToken(), + 'access_token_expired_at' => $userTgBotToken->user->getTokenExpiredAt(), + 'id' => $user->id, + 'status' => $user->userCard->status ?? null, + 'card_id' => $user->userCard->id ?? null, + ]; + } else { + throw new BadRequestHttpException(json_encode($model->errors)); + } + } + + /** + * @param array $params + * @return TgBotDialogForm|string[] + * @throws Exception + */ + public function createDialog(array $params) + { + $form = new TgBotDialogForm(); + $form->load($params); + + if (!$form->validate()){ + return $form; + } + + $dialog = new UserTgBotDialog(); + $dialog->user_id = $form->userId; + $dialog->dialog_id = $form->dialogId; + + if (!$dialog->save()) { + throw new Exception('User dont save'); + } + + return ['status' => 'success']; + } + + /** + * @param string $userId + * @return array + * @throws Exception + */ + public function getDialogIdByUserId(string $userId) + { + $model = UserTgBotDialog::findOne(['user_id' => $userId]); + + if (!$model) { + throw new \Exception('dialog_id не найден!'); + } + + return ['dialog_id' => $model->dialog_id]; + } + + /** + * @param string $dialogId + * @return array + * @throws Exception + */ + public function getUserIdByDialogId(string $dialogId) + { + $model = UserTgBotDialog::findOne(['dialog_id' => $dialogId]); + + if (!$model) { + throw new \Exception('user_id не найден!'); + } + + return ['user_id' => $model->user_id]; + } + /** * @param int $userId * @return UserTgBotToken @@ -130,61 +211,4 @@ class UserTgBotTokenService return $model; } - - /** - * @param array $params - * @return TgBotDialogForm|string[] - * @throws Exception - */ - public function createDialog(array $params) - { - $form = new TgBotDialogForm(); - $form->load($params); - - if (!$form->validate()){ - return $form; - } - - $dialog = new UserTgBotDialog(); - $dialog->user_id = $form->userId; - $dialog->dialog_id = $form->dialogId; - - if (!$dialog->save()) { - throw new Exception('User dont save'); - } - - return ['status' => 'success']; - } - - /** - * @param string $userId - * @return array - * @throws Exception - */ - public function getDialogIdByUserId(string $userId) - { - $model = UserTgBotDialog::findOne(['user_id' => $userId]); - - if (!$model) { - throw new \Exception('dialog_id не найден!'); - } - - return ['dialog_id' => $model->dialog_id]; - } - - /** - * @param string $dialogId - * @return array - * @throws Exception - */ - public function getUserIdByDialogId(string $dialogId) - { - $model = UserTgBotDialog::findOne(['dialog_id' => $dialogId]); - - if (!$model) { - throw new \Exception('user_id не найден!'); - } - - return ['user_id' => $model->user_id]; - } } \ No newline at end of file From ae447892001f6460f43e4a820915656c8cbb0875 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 3 Nov 2023 12:01:29 +0300 Subject: [PATCH 24/37] add project_id and company_id to reports update doc --- .../modules/reports/views/reports/_form.php | 15 +- common/models/Reports.php | 12 +- ...13_add_column_project_id_reports_table.php | 34 ++ ...24_add_column_company_id_reports_table.php | 34 ++ .../api/controllers/ReportsController.php | 329 +++++++++++++++++- frontend/modules/api/models/ProjectTask.php | 45 +++ frontend/modules/api/models/Reports.php | 132 +++++++ 7 files changed, 589 insertions(+), 12 deletions(-) create mode 100644 console/migrations/m231102_113213_add_column_project_id_reports_table.php create mode 100644 console/migrations/m231102_113324_add_column_company_id_reports_table.php create mode 100644 frontend/modules/api/models/Reports.php diff --git a/backend/modules/reports/views/reports/_form.php b/backend/modules/reports/views/reports/_form.php index 3140590..4910426 100755 --- a/backend/modules/reports/views/reports/_form.php +++ b/backend/modules/reports/views/reports/_form.php @@ -1,9 +1,8 @@ registerCss('.list-cell__task{width:73%}') field($model, 'tomorrow')->textarea(['maxlength' => true]) ?> field($model, 'user_card_id')->dropDownList( - \yii\helpers\ArrayHelper::map(common\models\UserCard::find()->all(), 'id', 'fio'), + ArrayHelper::map(common\models\UserCard::find()->all(), 'id', 'fio'), + ['prompt' => '...'] + ) ?> + + field($model, 'project_id')->dropDownList( + ArrayHelper::map(common\models\Project::find()->all(), 'id', 'name'), + ['prompt' => '...'] + ) ?> + + field($model, 'company_id')->dropDownList( + ArrayHelper::map(common\models\Company::find()->all(), 'id', 'name'), ['prompt' => '...'] ) ?> diff --git a/common/models/Reports.php b/common/models/Reports.php index 150fb06..1fc3b47 100755 --- a/common/models/Reports.php +++ b/common/models/Reports.php @@ -14,6 +14,8 @@ use Yii; * @property string $difficulties * @property string $tomorrow * @property int $user_card_id + * @property int $project_id + * @property int $company_id * @property int $status * * @property UserCard $userCard @@ -36,11 +38,13 @@ class Reports extends \yii\db\ActiveRecord public function rules() { return [ - [['user_card_id', 'status'], 'integer'], + [['user_card_id', 'status', 'company_id', 'project_id'], 'integer'], [['_task'], 'checkIsArray'], [['user_card_id', 'created_at'], 'required'], [['today', 'difficulties', 'tomorrow', 'created_at'], 'string', 'max' => 255], - [['user_card_id'], 'exist', 'skipOnError' => true, 'targetClass' => UserCard::className(), 'targetAttribute' => ['user_card_id' => 'id']], + [['user_card_id'], 'exist', 'skipOnError' => true, 'targetClass' => UserCard::class, 'targetAttribute' => ['user_card_id' => 'id']], + [['project_id'], 'exist', 'skipOnEmpty' => true, 'targetClass' => Project::class, 'targetAttribute' => ['project_id' => 'id']], + [['company_id'], 'exist', 'skipOnEmpty' => true, 'targetClass' => Company::class, 'targetAttribute' => ['company_id' => 'id']], ]; } @@ -56,7 +60,9 @@ class Reports extends \yii\db\ActiveRecord 'difficulties' => 'Какие сложности возникли?', 'tomorrow' => 'Что планируется сделать завтра?', 'user_card_id' => 'Пользователь', - 'status' => 'Статус' + 'status' => 'Статус', + 'project_id' => 'ID проекта', + 'company_id' => 'ID компании' ]; } diff --git a/console/migrations/m231102_113213_add_column_project_id_reports_table.php b/console/migrations/m231102_113213_add_column_project_id_reports_table.php new file mode 100644 index 0000000..6bbc5c1 --- /dev/null +++ b/console/migrations/m231102_113213_add_column_project_id_reports_table.php @@ -0,0 +1,34 @@ +addColumn('reports', 'project_id', $this->integer()->defaultValue(null)); + + $this->addForeignKey( + 'reports_project_id', + 'reports', + 'project_id', + 'project', + 'id' + ); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropForeignKey('reports_project_id', 'reports'); + $this->dropColumn('reports', 'project_id'); + } +} diff --git a/console/migrations/m231102_113324_add_column_company_id_reports_table.php b/console/migrations/m231102_113324_add_column_company_id_reports_table.php new file mode 100644 index 0000000..af44403 --- /dev/null +++ b/console/migrations/m231102_113324_add_column_company_id_reports_table.php @@ -0,0 +1,34 @@ +addColumn('reports', 'company_id', $this->integer()->defaultValue(null)); + + $this->addForeignKey( + 'reports_company_id', + 'reports', + 'company_id', + 'project', + 'id' + ); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropForeignKey('reports_company_id', 'reports'); + $this->dropColumn('reports', 'company_id'); + } +} diff --git a/frontend/modules/api/controllers/ReportsController.php b/frontend/modules/api/controllers/ReportsController.php index d447d38..2baddf0 100755 --- a/frontend/modules/api/controllers/ReportsController.php +++ b/frontend/modules/api/controllers/ReportsController.php @@ -14,7 +14,57 @@ use yii\web\NotFoundHttpException; class ReportsController extends ApiController { - public function actionIndex($user_card_id) + /** + * @OA\Get(path="/user-response/index", + * summary="Поиск отчётов по промежутку дат", + * description="Осуществляет поиск отчётов пользователя по промежутку дат", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Reports"}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"user_card_id"}, + * @OA\Property( + * property="user_card_id", + * type="integer", + * description="Идентификатор карты(профиля) пользователя", + * nullable=false, + * ), + * @OA\Property( + * property="fromDate", + * type="DateTime", + * description="Дата начала поиска", + * nullable=false, + * ), + * @OA\Property( + * property="toDate", + * type="DateTime", + * description="Дата конца периода поиска", + * nullable=false, + * ), + * ), + * ), + * ), + * + * @OA\Response( + * response=200, + * description="Возвращает объект Запроса", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ReportsResponseExample"), + * ), + * ), + * + * + * ) + * + * @param $user_card_id + * @return array + */ + public function actionIndex($user_card_id): array { $reportsModel = new ReportSearchForm(); @@ -32,9 +82,51 @@ class ReportsController extends ApiController } /** + * @OA\Get(path="/user-response/find-by-date", + * summary="Поиск отчётов по дате", + * description="Осуществляет поиск отчётов пользователя по дате", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Reports"}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"user_card_id"}, + * @OA\Property( + * property="user_card_id", + * type="integer", + * description="Идентификатор карты(профиля) пользователя", + * nullable=false, + * ), + * @OA\Property( + * property="date", + * type="DateTime", + * description="Дата поиска", + * nullable=false, + * ), + * ), + * ), + * ), + * + * @OA\Response( + * response=200, + * description="Возвращает объект Запроса", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ReportsResponseExample"), + * ), + * ), + * + * + * ) + * + * @return array + * @throws BadRequestHttpException * @throws NotFoundHttpException */ - public function actionFindByDate()//: array + public function actionFindByDate(): array { $reportsModel = new ReportSearchForm(); @@ -57,7 +149,118 @@ class ReportsController extends ApiController return $reportsModel->findByDate(); } - public function actionCreate() + /** + * @OA\Post(path="/reports/create", + * summary="Создание отчёта", + * description="Метод для создания нового отчёта", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Reports"}, + * @OA\Parameter( + * name="difficulties", + * in="query", + * required=false, + * description="Описание сложностей возникших при выполнении задач", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="tomorrow", + * in="query", + * required=false, + * description="Описание планов на завтра", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="created_at", + * in="query", + * required=false, + * description="Дата создания", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="status", + * in="query", + * required=false, + * description="Статус", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="user_card_id", + * in="query", + * required=false, + * description="ID карты(профиля) пользователя", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="project_id", + * in="query", + * required=false, + * description="ID проекта", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="company_id", + * in="query", + * required=false, + * description="ID компании", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="tasks[]", + * in="query", + * required=false, + * description="Масив задач. ", + * @OA\Schema( + * type="array", + * @OA\Items( + * type="object", + * @OA\Property( + * property="task", + * description="Название задачи", + * type="string", + * ), + * @OA\Property( + * property="hours_spent", + * description="Затраченное количество часов", + * type="string", + * ), + * @OA\Property( + * property="minutes_spent", + * description="Затраченное количество минут", + * type="string", + * ) + * ) + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает объект Запроса", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ReportsResponseCreateExample"), + * ), + * ), + * ) + * + * @return array + * @throws BadRequestHttpException + */ + public function actionCreate(): array { $params = Yii::$app->request->post(); if (!isset($params['tasks'])){ @@ -93,6 +296,41 @@ class ReportsController extends ApiController return array_merge($reportsModel->toArray()); } + + /** + * @OA\Get(path="/user-response/delete", + * summary="Удаление отчёта", + * description="Осуществляет удаление отчёта", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Reports"}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"id"}, + * @OA\Property( + * property="id", + * type="integer", + * description="Идентификатор отчётая", + * nullable=false, + * ), + * ), + * ), + * ), + * + * @OA\Response( + * response=200, + * description="Возвращает true в случае успеха", + * ), + * ) + * + * @return bool + * @throws NotFoundHttpException + * @throws \Throwable + * @throws \yii\db\StaleObjectException + */ public function actionDelete() { $id = Yii::$app->request->get('id'); @@ -110,6 +348,82 @@ class ReportsController extends ApiController return true; } + /** + * @OA\Get(path="/reports/update", + * summary="Обновление отчёта", + * description="Метод для Обновления отчёта", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Reports"}, + * @OA\Parameter( + * name="id", + * in="query", + * required=true, + * description="ID отчёта", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="created_at", + * in="query", + * required=false, + * description="Дата создания (yyyy-mm-dd)", + * @OA\Schema( + * type="DateTime", + * ) + * ), + * @OA\Parameter( + * name="today", + * in="query", + * required=false, + * description="Сделанное сегодня", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="difficulties", + * in="query", + * required=false, + * description="Описание сложностей возникших при выполнении задач", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="tomorrow", + * in="query", + * required=false, + * description="Описание планов на завтра", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="status", + * in="query", + * required=false, + * description="Статус", + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает объект Запроса", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ReportsResponseCreateExample"), + * ), + * ), + * ) + * + * @return array + * @throws BadRequestHttpException + * @throws NotFoundHttpException + */ public function actionUpdate(): array { $params = Yii::$app->request->get(); @@ -135,9 +449,13 @@ class ReportsController extends ApiController } /** - * @throws NotFoundHttpException + * @param $fromDate + * @param $toDate + * @param $user_card_id + * @return array|array[]|object|object[]|string[] + * @throws BadRequestHttpException */ - public function actionReportsByDate($fromDate, $toDate, $user_card_id) + public function actionCheckReportsByDate($fromDate, $toDate, $user_card_id) { if (!$this->checkDate($fromDate) || !$this->checkDate($toDate)) { throw new BadRequestHttpException('Wrong date format'); @@ -170,5 +488,4 @@ class ReportsController extends ApiController } return true; } - } diff --git a/frontend/modules/api/models/ProjectTask.php b/frontend/modules/api/models/ProjectTask.php index 6258115..3146448 100644 --- a/frontend/modules/api/models/ProjectTask.php +++ b/frontend/modules/api/models/ProjectTask.php @@ -153,6 +153,51 @@ namespace frontend\modules\api\models; * type="string", * example="/profileava/m8.png" * ), + *) + * + * @OA\Schema( + * schema="ProjectTaskReportsExample", + * type="array", + * @OA\Items( + * @OA\Property( + * property="id", + * type="integer", + * example=1 + * ), + * @OA\Property( + * property="report_id", + * type="integer", + * example=12 + * ), + * @OA\Property( + * property="task", + * type="string", + * example="Задача" + * ), + * @OA\Property( + * property="hours_spent", + * type="integer", + * example=2 + * ), + * @OA\Property( + * property="created_at", + * type="integer", + * example=1671148800 + * ), + * @OA\Property( + * property="status", + * type="integer", + * example=1 + * ), + * @OA\Property( + * property="minutes_spent", + * type="integer", + * example=0 + * ), + * ) + * + * + *) * */ diff --git a/frontend/modules/api/models/Reports.php b/frontend/modules/api/models/Reports.php new file mode 100644 index 0000000..5628fda --- /dev/null +++ b/frontend/modules/api/models/Reports.php @@ -0,0 +1,132 @@ + Date: Sat, 4 Nov 2023 18:30:01 +0300 Subject: [PATCH 25/37] fix method name in ReportsController --- frontend/modules/api/controllers/ReportsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/modules/api/controllers/ReportsController.php b/frontend/modules/api/controllers/ReportsController.php index 2baddf0..3167efd 100755 --- a/frontend/modules/api/controllers/ReportsController.php +++ b/frontend/modules/api/controllers/ReportsController.php @@ -455,7 +455,7 @@ class ReportsController extends ApiController * @return array|array[]|object|object[]|string[] * @throws BadRequestHttpException */ - public function actionCheckReportsByDate($fromDate, $toDate, $user_card_id) + public function actionReportsByDate($fromDate, $toDate, $user_card_id) { if (!$this->checkDate($fromDate) || !$this->checkDate($toDate)) { throw new BadRequestHttpException('Wrong date format'); From 44cfe842d5bcef04370c0f28ddee23d316b43fb2 Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 7 Nov 2023 15:42:15 +0300 Subject: [PATCH 26/37] add resume request --- common/models/UserCard.php | 11 ++-- .../api/controllers/ResumeController.php | 45 ++++++++++++++++ frontend/modules/api/models/resume/Resume.php | 54 +++++++++++++++++++ 3 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 frontend/modules/api/controllers/ResumeController.php create mode 100644 frontend/modules/api/models/resume/Resume.php diff --git a/common/models/UserCard.php b/common/models/UserCard.php index b3ff1b3..f6db69f 100755 --- a/common/models/UserCard.php +++ b/common/models/UserCard.php @@ -2,14 +2,11 @@ namespace common\models; -use common\classes\Debug; use Exception; -use phpDocumentor\Reflection\Types\This; use Yii; use yii\behaviors\TimestampBehavior; use yii\db\ActiveQuery; use yii\db\Expression; -use yii\filters\AccessControl; use yii\helpers\ArrayHelper; /** @@ -45,6 +42,7 @@ use yii\helpers\ArrayHelper; * * @property FieldsValue[] $fieldsValues * @property ProjectUser[] $projectUsers + * @property CardSkill[] $skillValues * @property ResumeTemplate $resumeTemplate * @property Position $position * @property Status $status0 @@ -246,6 +244,13 @@ class UserCard extends \yii\db\ActiveRecord return $this->hasMany(CardSkill::class, ['card_id' => 'id'])->with('skill'); } + public function getSkillsName() + { + return $this->getSkillValues() + ->leftJoin('skill', 'card_skill.skill_id = skill.id') + ->select('skill.name')->column(); + } + public static function getNameSkills() { return ArrayHelper::map(Skill::find()->all(), 'id', 'name'); diff --git a/frontend/modules/api/controllers/ResumeController.php b/frontend/modules/api/controllers/ResumeController.php new file mode 100644 index 0000000..483ee1e --- /dev/null +++ b/frontend/modules/api/controllers/ResumeController.php @@ -0,0 +1,45 @@ +user->identity->id); + } +} diff --git a/frontend/modules/api/models/resume/Resume.php b/frontend/modules/api/models/resume/Resume.php new file mode 100644 index 0000000..4af8a90 --- /dev/null +++ b/frontend/modules/api/models/resume/Resume.php @@ -0,0 +1,54 @@ + function() { + return $this->userCard->getSkillsName(); + + }, + 'resume' => function () { + return $this->userCard->vc_text; + }, + ]; + } +} \ No newline at end of file From 3d0ee63eae81bd560e891edc897c114df4bce74c Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 7 Nov 2023 15:46:19 +0300 Subject: [PATCH 27/37] update doc --- frontend/modules/api/controllers/ResumeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/modules/api/controllers/ResumeController.php b/frontend/modules/api/controllers/ResumeController.php index 483ee1e..23ede9e 100644 --- a/frontend/modules/api/controllers/ResumeController.php +++ b/frontend/modules/api/controllers/ResumeController.php @@ -18,7 +18,7 @@ class ResumeController extends ApiController * tags={"Resume"}, * @OA\Parameter( * name="userId", - * description="Метод для получение резюме", + * description="Метод для получение резюме. userId - обязателен, в случае его отсудствия будет возвращено резюме текущего пользователя", * in="query", * required=false, * @OA\Schema( From 4c7e6f3d2849a3d1da2770b6ef597fcdf9d5f557 Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 7 Nov 2023 16:29:52 +0300 Subject: [PATCH 28/37] fix user response --- .../controllers/UserResponseController.php | 2 +- frontend/modules/api/models/UserResponse.php | 10 +++++++++- .../api}/services/UserResponseService.php | 19 +++++++++++++------ 3 files changed, 23 insertions(+), 8 deletions(-) rename {common => frontend/modules/api}/services/UserResponseService.php (73%) diff --git a/frontend/modules/api/controllers/UserResponseController.php b/frontend/modules/api/controllers/UserResponseController.php index 7646ed8..d9ffec5 100644 --- a/frontend/modules/api/controllers/UserResponseController.php +++ b/frontend/modules/api/controllers/UserResponseController.php @@ -2,7 +2,7 @@ namespace frontend\modules\api\controllers; -use common\services\UserResponseService; +use frontend\modules\api\services\UserResponseService; use Yii; use yii\base\InvalidConfigException; use yii\web\BadRequestHttpException; diff --git a/frontend/modules/api/models/UserResponse.php b/frontend/modules/api/models/UserResponse.php index 7212f2b..7eb5f1d 100644 --- a/frontend/modules/api/models/UserResponse.php +++ b/frontend/modules/api/models/UserResponse.php @@ -98,5 +98,13 @@ namespace frontend\modules\api\models; */ class UserResponse extends \common\models\UserResponse { - + public function fields(): array + { + return [ + 'user_id', + 'question_id', + 'response_body', + 'user_questionnaire_uuid', + ]; + } } \ No newline at end of file diff --git a/common/services/UserResponseService.php b/frontend/modules/api/services/UserResponseService.php similarity index 73% rename from common/services/UserResponseService.php rename to frontend/modules/api/services/UserResponseService.php index af68613..7b41bdb 100644 --- a/common/services/UserResponseService.php +++ b/frontend/modules/api/services/UserResponseService.php @@ -1,8 +1,9 @@ load($userResponseParams, ''); - (new UserResponseService)->validateResponseModel($model); + $model->load($userResponse, ''); + + try { + self::validateResponseModel($model); + } catch (\Exception $ex) { + throw new BadRequestHttpException(json_encode('One of the parameters is empty!')); + } + array_push($userResponseModels, $model); } @@ -34,7 +41,7 @@ class UserResponseService /** * @throws BadRequestHttpException */ - protected function validateResponseModel($model) + protected static function validateResponseModel($model) { if (!$model->validate()) { throw new BadRequestHttpException(json_encode($model->errors)); From bc4cb3b54c02f4863e0f27a443244b7e93d8d2a0 Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 8 Nov 2023 12:06:22 +0300 Subject: [PATCH 29/37] update resume method --- common/models/User.php | 2 +- .../api/models/UserCardPortfolioProjects.php | 74 +++++++++++++++++++ frontend/modules/api/models/resume/Resume.php | 39 +++++++++- frontend/modules/card/models/UserCard.php | 10 +++ 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 frontend/modules/api/models/UserCardPortfolioProjects.php diff --git a/common/models/User.php b/common/models/User.php index 43fe046..96c96b8 100755 --- a/common/models/User.php +++ b/common/models/User.php @@ -231,6 +231,6 @@ class User extends ActiveRecord implements IdentityInterface, UserRbacInterface public function getProjectUser() { - return $this->hasMany(ProjectUser::className(), ['user_id' => 'id']); + return $this->hasMany(ProjectUser::class, ['user_id' => 'id']); } } diff --git a/frontend/modules/api/models/UserCardPortfolioProjects.php b/frontend/modules/api/models/UserCardPortfolioProjects.php new file mode 100644 index 0000000..9c4ac8d --- /dev/null +++ b/frontend/modules/api/models/UserCardPortfolioProjects.php @@ -0,0 +1,74 @@ + function () { + return $this->mainStack->name; + }, + 'additional_stack', + 'link', + ]; + } + + /** + * @return string[] + */ + public function extraFields(): array + { + return []; + } + + public function getMainStack() + { + return $this->hasOne(Skill::class, ['id' => 'main_stack']); + } +} diff --git a/frontend/modules/api/models/resume/Resume.php b/frontend/modules/api/models/resume/Resume.php index 4af8a90..89eb669 100644 --- a/frontend/modules/api/models/resume/Resume.php +++ b/frontend/modules/api/models/resume/Resume.php @@ -2,12 +2,26 @@ namespace frontend\modules\api\models\resume; -use yii\helpers\ArrayHelper; +use frontend\modules\card\models\UserCard; /** * * @OA\Schema( * schema="Resume", + * type="array", + * @OA\Items( + * @OA\Property( + * property="fio", + * type="string", + * example="ФИО", + * description="ФИО" + * ), + * @OA\Property( + * property="position", + * type="string", + * example="position", + * description="Специализация" + * ), * @OA\Property( * property="stack", * type="array", @@ -23,6 +37,11 @@ use yii\helpers\ArrayHelper; * example="Резюме", * description="Тело резюме в HTML разметке" * ), + * @OA\Property( + * property="projects", + * ref="#/components/schemas/UserCardPortfolioProjectsExample", + * ), + * ), *) * * @OA\Schema( @@ -32,23 +51,37 @@ use yii\helpers\ArrayHelper; * ref="#/components/schemas/Resume", * ), *) + * @property UserCard $userCard */ class Resume extends \common\models\User { - /** * @return string[] */ public function fields(): array { return [ - 'stack' => function() { + 'fio' => function () { + return $this->userCard->fio; + }, + 'position' => function () { + return $this->userCard->position->name; + }, + 'stack' => function () { return $this->userCard->getSkillsName(); }, 'resume' => function () { return $this->userCard->vc_text; }, + 'projects' => function () { + return $this->userCard->userCardPortfolioProjects; + }, ]; } + + public function getUserCard() + { + return $this->hasOne(UserCard::class, ['id_user' => 'id']); + } } \ No newline at end of file diff --git a/frontend/modules/card/models/UserCard.php b/frontend/modules/card/models/UserCard.php index 923904e..fd7390e 100755 --- a/frontend/modules/card/models/UserCard.php +++ b/frontend/modules/card/models/UserCard.php @@ -3,9 +3,14 @@ namespace frontend\modules\card\models; use common\models\CardSkill; +use frontend\modules\api\models\UserCardPortfolioProjects; use Yii; +use yii\db\ActiveQuery; use yii\helpers\ArrayHelper; +/** + * @property UserCardPortfolioProjects[] $userCardPortfolioProjects + */ class UserCard extends \common\models\UserCard { public $fields; @@ -48,4 +53,9 @@ class UserCard extends \common\models\UserCard { return self::findOne(['id_user' => $userId]); } + + public function getUserCardPortfolioProjects(): ActiveQuery + { + return $this->hasMany(UserCardPortfolioProjects::class, ['card_id' => 'id']); + } } From 05271d5f177162f734ce04eec11ba3702f91441a Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 10 Nov 2023 15:55:01 +0300 Subject: [PATCH 30/37] add import tasks to xlsx --- .../task/controllers/TaskController.php | 31 +++++ .../modules/task/views/task/_form-import.php | 79 ++++++++++++ backend/modules/task/views/task/index.php | 1 + backend/modules/task/views/task/view.php | 2 +- common/models/MarkEntity.php | 2 + common/models/ProjectTask.php | 93 +++++++++++++- common/models/ProjectTaskUser.php | 2 +- common/models/forms/TasksImportForm.php | 44 +++++++ common/services/ImportProjectTaskService.php | 78 ++++++++++++ common/services/TaskService.php | 57 --------- composer.json | 4 +- .../controllers/ProjectColumnController.php | 25 ++-- .../api/controllers/ProjectController.php | 15 +-- .../api/controllers/TaskController.php | 117 ++++++++++++++---- .../api/models/{ => project}/Project.php | 3 +- .../models/{ => project}/ProjectColumn.php | 2 +- .../api/models/{ => project}/ProjectTask.php | 2 +- .../models/{ => project}/ProjectTaskUser.php | 2 +- .../api/models/{ => project}/ProjectUser.php | 2 +- frontend/modules/api/services/TaskService.php | 97 +++++++++++++++ 20 files changed, 542 insertions(+), 116 deletions(-) create mode 100644 backend/modules/task/views/task/_form-import.php create mode 100644 common/models/forms/TasksImportForm.php create mode 100644 common/services/ImportProjectTaskService.php delete mode 100644 common/services/TaskService.php rename frontend/modules/api/models/{ => project}/Project.php (97%) rename frontend/modules/api/models/{ => project}/ProjectColumn.php (98%) rename frontend/modules/api/models/{ => project}/ProjectTask.php (99%) rename frontend/modules/api/models/{ => project}/ProjectTaskUser.php (90%) rename frontend/modules/api/models/{ => project}/ProjectUser.php (97%) create mode 100644 frontend/modules/api/services/TaskService.php diff --git a/backend/modules/task/controllers/TaskController.php b/backend/modules/task/controllers/TaskController.php index 735e2db..e062740 100644 --- a/backend/modules/task/controllers/TaskController.php +++ b/backend/modules/task/controllers/TaskController.php @@ -2,6 +2,8 @@ namespace backend\modules\task\controllers; +use common\models\forms\TasksImportForm; +use common\services\ImportProjectTaskService; use yii\data\ActiveDataProvider; use Yii; use backend\modules\task\models\ProjectTask; @@ -137,4 +139,33 @@ class TaskController extends Controller throw new NotFoundHttpException('The requested page does not exist.'); } + + public function actionImport() + { + $model = new TasksImportForm(); + + if ($model->load(Yii::$app->request->post()) && $model->validate()) { + $importTaskService = new ImportProjectTaskService(); + + $query = ProjectTask::genQueryToImport( + (int)$model->companyId, + (int)$model->userId, + (int)$model->projectId, + (int)$model->fromDate, + (int)$model->toDate + ); + $tasks = $query->all(); + + if (!$tasks) { + Yii::$app->session->setFlash('danger', 'Задачи не найдены!'); + return Yii::$app->getResponse()->redirect(['/task/task/import']); + } else { + return $importTaskService->importTasks($tasks); + } + } + + return $this->render('_form-import', [ + 'model' => $model, + ]); + } } diff --git a/backend/modules/task/views/task/_form-import.php b/backend/modules/task/views/task/_form-import.php new file mode 100644 index 0000000..46a9d19 --- /dev/null +++ b/backend/modules/task/views/task/_form-import.php @@ -0,0 +1,79 @@ +title = 'Импорт задач'; +$this->params['breadcrumbs'][] = ['label' => 'Tasks', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +?> + +
+ + + + field($model, 'companyId')->dropDownList(\common\models\Company::find() + ->select(['name', 'id'])->indexBy('id')->column(), + [ + 'prompt' => 'Выберите' + ] + ); + + ?> + + field($model, 'userId')->widget( + Select2::class, + [ + 'data' => \common\models\UserCard::getListUserWithUserId(), + 'options' => ['placeholder' => '...', 'class' => 'form-control'], + 'pluginOptions' => [ + 'allowClear' => true + ], + ] + ); ?> + + field($model, 'projectId')->widget( + Select2::class, + [ + 'data' => Project::find()->select(['name', 'id'])->indexBy('id')->column(), + 'options' => ['placeholder' => '...', 'class' => 'form-control'], + 'pluginOptions' => [ + 'allowClear' => true + ], + ] + ); ?> + + field($model, 'fromDate')->widget(DatePicker::class,[ + 'type' => DatePicker::TYPE_COMPONENT_APPEND, + 'pluginOptions' => [ + 'autoclose' => true, + 'format' => 'yyyy-mm-dd' + ] + ]); + ?> + + + field($model, 'toDate')->widget(DatePicker::class,[ + 'type' => DatePicker::TYPE_COMPONENT_APPEND, + 'pluginOptions' => [ + 'autoclose' => true, + 'format' => 'yyyy-mm-dd' + ] + ]); + ?> + +
+ 'btn btn-success']) ?> +
+ + + +
diff --git a/backend/modules/task/views/task/index.php b/backend/modules/task/views/task/index.php index 538f42c..98af0e5 100644 --- a/backend/modules/task/views/task/index.php +++ b/backend/modules/task/views/task/index.php @@ -22,6 +22,7 @@ $this->params['breadcrumbs'][] = $this->title;

'btn btn-success']) ?> + 'btn btn-primary']) ?>

'project_user_id', - 'value' => 'user.email' + 'value' => 'user.username' ], [ diff --git a/common/models/MarkEntity.php b/common/models/MarkEntity.php index 8b54a0a..b4a2265 100644 --- a/common/models/MarkEntity.php +++ b/common/models/MarkEntity.php @@ -11,6 +11,8 @@ use Yii; * @property int $mark_id * @property int $entity_type * @property int $entity_id + * + * @property Mark $mark */ class MarkEntity extends \yii\db\ActiveRecord { diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index 7321c9b..e93838e 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -29,6 +29,9 @@ use yii\helpers\ArrayHelper; * @property User $user * @property UserCard $card * @property UserCard $cardIdCreator + * @property Company $company + * @property ProjectColumn $column + * @property User $executor * @property Mark[] $mark * @property MarkEntity[] $markEntity * @property ProjectTaskUser[] $taskUsers @@ -42,6 +45,8 @@ class ProjectTask extends ActiveRecord const PRIORITY_MEDIUM = 1; const PRIORITY_HIGH = 2; + const DAY_IN_UNIX_TIME = 86340; // 23:59:59 + /** * @return string[] */ @@ -147,7 +152,7 @@ class ProjectTask extends ActiveRecord 'user_id', 'user' => function () { return [ - "fio" => $this->user->userCard->fio ?? $this->user->id, + "fio" => $this->user->userCard->fio ?? ($this->user->id ?? ''), "avatar" => $this->user->userCard->photo ?? '', ]; }, @@ -164,7 +169,7 @@ class ProjectTask extends ActiveRecord return null; }, 'comment_count' => function () { - return Comment::find()->where(['entity_id' => $this->id, 'entity_type' => 2, 'status' => Comment::STATUS_ACTIVE])->count(); + return $this->getCommentCount(); }, 'taskUsers', 'mark', @@ -172,6 +177,14 @@ class ProjectTask extends ActiveRecord ]; } + /** + * @return bool|int|string|null + */ + public function getCommentCount() + { + return Comment::find()->where(['entity_id' => $this->id, 'entity_type' => 2, 'status' => Comment::STATUS_ACTIVE])->count(); + } + /** * @return string[] */ @@ -215,6 +228,15 @@ class ProjectTask extends ActiveRecord return $this->hasOne(Project::className(), ['id' => 'project_id']); } + /** + * @return ActiveQuery + */ + public function getCompany(): ActiveQuery + { + return $this->hasOne(Company::class,['id' => 'company_id']) + ->via('project'); + } + /** * @return ActiveQuery */ @@ -275,4 +297,71 @@ class ProjectTask extends ActiveRecord return ArrayHelper::map( self::find()->joinWith(['user', 'project'])->where(['project_id' => $task_id])->all(), 'id', 'user.username'); } + + /** + * @return string + */ + public function getTaskUsersToStr(): string + { + $participants = ''; + foreach ($this->taskUsers as $taskUser) { + $participants .= $taskUser->user->userCard->fio ?? $taskUser->user->username; + $participants .= ', '; + } + return $participants; + } + + /** + * @return string + */ + public function getMarkTitleToStr(): string + { + $tags = ''; + foreach ($this->markEntity as $markEntity) { + $tags .= $markEntity->mark->title . ', '; + } + return $tags; + } + + /** + * @param int|null $companyId + * @param int|null $userId + * @param int|null $projectId + * @param int|null $fromDate + * @param int|null $toDate + * @return ActiveQuery + */ + public static function genQueryToImport( + int $companyId = null, + int $userId = null, + int $projectId = null, + int $fromDate = null, + int $toDate = null + ): ActiveQuery + { + $query = ProjectTask::find(); + + if ($companyId) { + $query->joinWith('company') + ->andWhere(['company.id' => $companyId]); + } + + if ($userId) { + $query->andWhere(['project_task.user_id' => $userId]); + } + + if ($projectId) { + $query->andWhere(['project_task.project_id' => $projectId]); + } + + if ($fromDate) { + $query->andFilterWhere(['>=', 'project_task.created_at', date("Y-m-d H:i:s", $fromDate)]); + } + + if ($toDate) { + $query->andFilterWhere(['<=', 'project_task.created_at', date("Y-m-d H:i:s", ($toDate + self::DAY_IN_UNIX_TIME))]); + } + + return $query; + } } diff --git a/common/models/ProjectTaskUser.php b/common/models/ProjectTaskUser.php index fb22cec..89fddf0 100644 --- a/common/models/ProjectTaskUser.php +++ b/common/models/ProjectTaskUser.php @@ -11,8 +11,8 @@ use yii\db\ActiveQuery; * @property int $task_id * @property int $user_id * - * @property ProjectUser $projectUser * @property ProjectTask $task + * @property User $user */ class ProjectTaskUser extends \yii\db\ActiveRecord { diff --git a/common/models/forms/TasksImportForm.php b/common/models/forms/TasksImportForm.php new file mode 100644 index 0000000..21ee733 --- /dev/null +++ b/common/models/forms/TasksImportForm.php @@ -0,0 +1,44 @@ + 'php:Y-m-d', 'timestampAttribute' => 'fromDate'], + ['toDate', 'date', 'format' => 'php:Y-m-d', 'timestampAttribute' => 'toDate'], + ]; + } + + public function attributeLabels() + { + return [ + 'companyId' => 'ID компании', + 'userId' => 'ID пользователя', + 'projectId' => 'ID проекта', + 'fromDate' => 'Дата начала поиска', + 'toDate' => 'Дата конца поиска', + ]; + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } +} diff --git a/common/services/ImportProjectTaskService.php b/common/services/ImportProjectTaskService.php new file mode 100644 index 0000000..4cb8699 --- /dev/null +++ b/common/services/ImportProjectTaskService.php @@ -0,0 +1,78 @@ +getActiveSheet(); + + $sheet + ->setCellValue('A1', 'ID') + ->setCellValue('B1', 'ID проекта') + ->setCellValue('C1', 'Название проекта') + ->setCellValue('D1', 'Задача') + ->setCellValue('E1', 'Дата создания') + ->setCellValue('F1', 'Дата обновления') + ->setCellValue('G1', 'Дедлайн') + ->setCellValue('H1', 'Описание') + ->setCellValue('I1', 'Статус') + ->setCellValue('J1', 'Колонка') + ->setCellValue('G1', 'ID создателя задачи') + ->setCellValue('K1', 'Создатель задачи') + ->setCellValue('L1', 'ID исполнителя') + ->setCellValue('M1', 'Приоритет') + ->setCellValue('N1', 'Исполнитель') + ->setCellValue('O1', 'Количество коментариев') + ->setCellValue('P1', 'Участники') + ->setCellValue('Q1', 'Теги') + ->setCellValue('R1', 'Приоритет выполнения'); + + $i = 2; + /** @var ProjectTask $task */ + foreach ($tasks as $task) { + $sheet + ->setCellValue('A' . $i, $task->id) + ->setCellValue('B' . $i, $task->project_id) + ->setCellValue('C' . $i, $task->project->name) + ->setCellValue('D' . $i, $task->title) + ->setCellValue('E' . $i, $task->created_at) + ->setCellValue('F' . $i, $task->updated_at) + ->setCellValue('G' . $i, $task->dead_line) + ->setCellValue('H' . $i, $task->description) + ->setCellValue('I' . $i, ArrayHelper::getValue(ProjectTask::getStatus(), $task->status)) + ->setCellValue('J' . $i, $task->column->title) + ->setCellValue('G' . $i, $task->user_id) + ->setCellValue('K' . $i, $task->user->userCard->fio ?? ($task->user->username ?? 'kkk')) + ->setCellValue('L' . $i, $task->executor_id) + ->setCellValue('M' . $i, $task->priority) + ->setCellValue('N' . $i, $task->executor->userCard->fio ?? ($task->executor->username ?? 'ggg')) + ->setCellValue('O' . $i, $task->getCommentCount()) + ->setCellValue('P' . $i, $task->getTaskUsersToStr()) + ->setCellValue('Q' . $i, $task->getMarkTitleToStr()) + ->setCellValue('R' . $i, $task->execution_priority); + $i++; + } + + $writer = new Xlsx($spreadsheet); + header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); + header('Content-Disposition: attachment; filename="'. urlencode('tasks.xlsx').'"'); + $writer->save('php://output'); + exit(); + } catch (\Exception $ex) { + return $ex->getMessage(); + } + } +} \ No newline at end of file diff --git a/common/services/TaskService.php b/common/services/TaskService.php deleted file mode 100644 index 835cd82..0000000 --- a/common/services/TaskService.php +++ /dev/null @@ -1,57 +0,0 @@ -load($taskParams, ''); - $task->save(); - return $task; - } - - public static function getTask($task_id): ?ProjectTask - { - return ProjectTask::findOne($task_id); - } - - public static function getTaskList($task_id): array - { - return ProjectTask::find()->asArray()->all(); - } - - public static function getTaskListByProject($project_id): array - { - return ProjectTask::find()->where(['project_id' => $project_id])->orderBy('priority DESC')->all(); - } - - public static function getTaskListByUser($user_id): array - { - $taskIdList = ProjectTaskUser::find()->where(['user_id' => $user_id])->select('task_id')->column(); - return ProjectTask::find()->where([ 'IN', 'id', $taskIdList])->orWhere(['user_id' => $user_id])->orderBy('priority DESC')->all(); - } - - public static function updateTask($task_params): ?ProjectTask - { - $modelTask = ProjectTask::findOne($task_params['task_id']); - - if (isset($task_params['executor_id']) && $task_params['executor_id'] == 0){ - $task_params['executor_id'] = null; - } - - $modelTask->load($task_params, ''); - $modelTask->save(); - - return $modelTask; - } - - public static function taskExists($task_id): bool - { - return ProjectTask::find()->where(['id' => $task_id])->exists(); - } -} \ No newline at end of file diff --git a/composer.json b/composer.json index dc0f9ed..f23ceb5 100755 --- a/composer.json +++ b/composer.json @@ -37,7 +37,9 @@ "kartik-v/yii2-mpdf": "dev-master", "mihaildev/yii2-ckeditor": "*", "developeruz/yii2-db-rbac": "*", - "zircote/swagger-php": "^4.7" + "zircote/swagger-php": "^4.7", + "phpoffice/phpspreadsheet": "^1.29", + "kartik-v/yii2-widget-datepicker": "dev-master" }, "require-dev": { "yiisoft/yii2-debug": "~2.0.0", diff --git a/frontend/modules/api/controllers/ProjectColumnController.php b/frontend/modules/api/controllers/ProjectColumnController.php index e690a02..f463c96 100644 --- a/frontend/modules/api/controllers/ProjectColumnController.php +++ b/frontend/modules/api/controllers/ProjectColumnController.php @@ -2,25 +2,14 @@ namespace frontend\modules\api\controllers; -use common\classes\Debug; use common\models\ProjectColumn; -use frontend\modules\api\models\Project; +use frontend\modules\api\models\project\Project; +use yii\db\ActiveRecord; use yii\web\BadRequestHttpException; use yii\web\NotFoundHttpException; class ProjectColumnController extends ApiController { - - public function verbs(): array - { - return [ - 'get-column-list' => ['get'], - 'create-column' => ['post'], - 'set-priority' => ['post'], - 'update-column' => ['put', 'patch'], - ]; - } - /** * * @OA\Get(path="/project-column/get-column-list", @@ -49,17 +38,17 @@ class ProjectColumnController extends ApiController * ) * * @param $project_id - * @return array|\yii\db\ActiveRecord[] + * @return array|ActiveRecord[] * @throws BadRequestHttpException */ - public function actionGetColumnList($project_id) + public function actionGetColumnList($project_id): array { $project = Project::findOne($project_id); if (!$project) { throw new BadRequestHttpException(json_encode(['Проект не найден'])); } - $columns = \frontend\modules\api\models\ProjectColumn::find()->where(['project_id' => $project_id, 'status' => ProjectColumn::STATUS_ACTIVE])->all(); + $columns = \frontend\modules\api\models\project\ProjectColumn::find()->where(['project_id' => $project_id, 'status' => ProjectColumn::STATUS_ACTIVE])->all(); return $columns; @@ -177,7 +166,7 @@ class ProjectColumnController extends ApiController * ), * ) * - * @return array|ProjectColumn|\yii\db\ActiveRecord|null + * @return array|ProjectColumn|ActiveRecord|null * @throws BadRequestHttpException * @throws \yii\base\InvalidConfigException */ @@ -241,7 +230,7 @@ class ProjectColumnController extends ApiController * ), * ) * - * @return array|Project|\yii\db\ActiveRecord + * @return array|Project|ActiveRecord * @throws BadRequestHttpException */ public function actionSetPriority() diff --git a/frontend/modules/api/controllers/ProjectController.php b/frontend/modules/api/controllers/ProjectController.php index 25ab4c3..68692f6 100644 --- a/frontend/modules/api/controllers/ProjectController.php +++ b/frontend/modules/api/controllers/ProjectController.php @@ -2,15 +2,16 @@ namespace frontend\modules\api\controllers; -use common\classes\Debug; use common\models\ProjectTaskCategory; use common\models\Status; use common\models\UseStatus; use frontend\modules\api\models\Manager; -use frontend\modules\api\models\Project; -use frontend\modules\api\models\ProjectUser; +use frontend\modules\api\models\project\Project; +use frontend\modules\api\models\project\ProjectUser; use Yii; +use yii\base\InvalidConfigException; use yii\data\ActiveDataProvider; +use yii\db\ActiveRecord; use yii\helpers\ArrayHelper; use yii\web\BadRequestHttpException; use yii\web\NotFoundHttpException; @@ -84,7 +85,7 @@ class ProjectController extends ApiController * ) * * @param $project_id - * @return array|Project|\yii\db\ActiveRecord|null + * @return array|ActiveRecord|null */ public function actionGetProject($project_id) { @@ -323,7 +324,7 @@ class ProjectController extends ApiController * ) * * @throws \Throwable - * @throws \yii\base\InvalidConfigException + * @throws InvalidConfigException * @throws \yii\db\StaleObjectException * @throws NotFoundHttpException */ @@ -366,7 +367,7 @@ class ProjectController extends ApiController * ), * ) * - * @return array|\yii\db\ActiveRecord + * @return array|ActiveRecord * @throws BadRequestHttpException */ public function actionMyEmployee() @@ -486,7 +487,7 @@ class ProjectController extends ApiController * ) * * @return Project - * @throws \yii\base\InvalidConfigException + * @throws InvalidConfigException|NotFoundHttpException */ public function actionDelUser(): Project { diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index 77d830c..47f7908 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -2,31 +2,32 @@ namespace frontend\modules\api\controllers; -use common\classes\Debug; use common\models\ProjectTask; use common\models\ProjectTaskUser; use common\models\User; -use common\services\TaskService; -use frontend\modules\api\models\ProjectColumn; +use frontend\modules\api\models\project\ProjectColumn; +use frontend\modules\api\services\TaskService; use Yii; use yii\base\InvalidConfigException; +use yii\data\ActiveDataProvider; use yii\web\BadRequestHttpException; use yii\web\NotFoundHttpException; use yii\web\ServerErrorHttpException; class TaskController extends ApiController { - public function verbs(): array + private TaskService $taskService; + + /** + * @param $id + * @param $module + * @param TaskService $taskService + * @param array $config + */ + public function __construct($id, $module, TaskService $taskService, $config = []) { - return [ - 'get-task' => ['get'], - 'get-task-list' => ['get'], - 'get-user-tasks' => ['get'], - 'create-task' => ['post'], - 'update-task' => ['put', 'patch'], - 'add-user-to-task' => ['post'], - 'del-user' => ['delete'], - ]; + $this->taskService = $taskService; + parent::__construct($id, $module, $config); } /** @@ -117,7 +118,7 @@ class TaskController extends ApiController $request['user_id'] = Yii::$app->user->id; } - $taskModel = TaskService::createTask($request); + $taskModel = $this->taskService->createTask($request); if ($taskModel->errors) { throw new ServerErrorHttpException(json_encode($taskModel->errors)); } @@ -170,9 +171,9 @@ class TaskController extends ApiController if (empty($project_id) or !is_numeric($project_id)) { throw new NotFoundHttpException('Incorrect project ID'); } - $tasks = TaskService::getTaskListByProject($project_id); + $tasks = $this->taskService->getTaskListByProject($project_id); } else { - $tasks = TaskService::getTaskList($project_id); + $tasks = $this->taskService->getTaskList($project_id); } if (empty($tasks)) { @@ -228,9 +229,9 @@ class TaskController extends ApiController if (empty($user_id) or !is_numeric($user_id)) { throw new NotFoundHttpException('Incorrect project ID'); } - $tasks = TaskService::getTaskListByUser($user_id); + $tasks = $this->taskService->getTaskListByUser($user_id); } else { - $tasks = TaskService::getTaskList($user_id); + $tasks = $this->taskService->getTaskList($user_id); } if (empty($tasks)) { @@ -283,7 +284,7 @@ class TaskController extends ApiController throw new NotFoundHttpException('Incorrect task ID'); } - $task = TaskService::getTask($task_id); + $task = $this->taskService->getTask($task_id); if (empty($task)) { throw new NotFoundHttpException('The task does not exist'); } @@ -372,11 +373,11 @@ class TaskController extends ApiController public function actionUpdateTask(): ?ProjectTask { $params = array_diff(\Yii::$app->request->getBodyParams(), [null, '']); - if (empty ($params['task_id']) or !TaskService::taskExists($params['task_id'])) { + if (empty ($params['task_id']) or !$this->taskService->taskExists($params['task_id'])) { throw new NotFoundHttpException('The task does not exist'); } - $modelTask = TaskService::updateTask($params); + $modelTask = $this->taskService->updateTask($params); if (!empty($modelTask->hasErrors())) { throw new ServerErrorHttpException(json_encode($modelTask->errors)); } @@ -435,7 +436,7 @@ class TaskController extends ApiController throw new NotFoundHttpException('User not found'); } - if (empty ($request['task_id']) or !TaskService::taskExists($request['task_id'])) { + if (empty ($request['task_id']) or !$this->taskService->taskExists($request['task_id'])) { throw new NotFoundHttpException('The task does not exist'); } @@ -506,13 +507,13 @@ class TaskController extends ApiController throw new NotFoundHttpException('User not found'); } - if (empty ($request['task_id']) or !TaskService::taskExists($request['task_id'])) { + if (empty ($request['task_id']) or !$this->taskService->taskExists($request['task_id'])) { throw new NotFoundHttpException('The task does not exist'); } ProjectTaskUser::deleteAll(['task_id' => $request['task_id'], 'user_id' => $request['user_id']]); - return TaskService::getTask($request['task_id']); + return $this->taskService->getTask($request['task_id']); } /** @@ -582,4 +583,72 @@ class TaskController extends ApiController return $column; } + + /** + * + * @OA\Get(path="/task/import", + * summary="Экспорт задач", + * description="Метод экспорта задач в xlsx", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TaskManager"}, + * @OA\Parameter( + * name="companyId", + * in="query", + * description="ID компании", + * required=true, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="userId", + * in="query", + * description="ID исполнителя задачи", + * required=true, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="projectId", + * in="query", + * description="ID проекта", + * required=true, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="fromDate", + * in="query", + * example="2023-11-09", + * description="Поиск задач с указанной даты", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Parameter( + * name="toDate", + * in="query", + * example="2023-11-09", + * description="Поиск задач до указанной даты", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает задачи в xlsx файле", + * ), + * ) + * + * @return string|void + * @throws BadRequestHttpException + */ + public function actionImport() + { + return $this->taskService->importTasks(Yii::$app->request->get()); + } } \ No newline at end of file diff --git a/frontend/modules/api/models/Project.php b/frontend/modules/api/models/project/Project.php similarity index 97% rename from frontend/modules/api/models/Project.php rename to frontend/modules/api/models/project/Project.php index 34ce13f..9e85130 100644 --- a/frontend/modules/api/models/Project.php +++ b/frontend/modules/api/models/project/Project.php @@ -1,7 +1,8 @@ importProjectTaskService = $importProjectTaskService; + } + + public function createTask($taskParams) + { + $task = new ProjectTask(); + $task->load($taskParams, ''); + $task->save(); + return $task; + } + + public function getTask($task_id): ?ProjectTask + { + return ProjectTask::findOne($task_id); + } + + public function getTaskList($task_id): array + { + return ProjectTask::find()->asArray()->all(); + } + + public function getTaskListByProject($project_id): array + { + return ProjectTask::find()->where(['project_id' => $project_id])->orderBy('priority DESC')->all(); + } + + public function getTaskListByUser($user_id): array + { + $taskIdList = ProjectTaskUser::find()->where(['user_id' => $user_id])->select('task_id')->column(); + return ProjectTask::find()->where([ 'IN', 'id', $taskIdList])->orWhere(['user_id' => $user_id])->orderBy('priority DESC')->all(); + } + + public function updateTask($task_params): ?ProjectTask + { + $modelTask = ProjectTask::findOne($task_params['task_id']); + + if (isset($task_params['executor_id']) && $task_params['executor_id'] == 0){ + $task_params['executor_id'] = null; + } + + $modelTask->load($task_params, ''); + $modelTask->save(); + + return $modelTask; + } + + public function taskExists($task_id): bool + { + return ProjectTask::find()->where(['id' => $task_id])->exists(); + } + + /** + * @throws BadRequestHttpException + */ + public function importTasks(array $params) + { + $form = new TasksImportForm(); + $form->load($params); + + if (!$form->validate()){ + $errors = $form->errors; + throw new BadRequestHttpException(array_shift($errors)[0]); + } + + $query = ProjectTask::genQueryToImport( + $form->companyId, + $form->userId, + $form->projectId, + $form->fromDate, + $form->toDate + ); + + $tasks = $query->all(); + + if (!$tasks) { + return null; + } + + return $this->importProjectTaskService->importTasks($tasks); + } +} \ No newline at end of file From b258eb7edf03d363481c46753e74cbc107afc96c Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 10 Nov 2023 15:57:39 +0300 Subject: [PATCH 31/37] fix api description --- frontend/modules/api/controllers/TaskController.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index 47f7908..0854ea2 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -588,7 +588,7 @@ class TaskController extends ApiController * * @OA\Get(path="/task/import", * summary="Экспорт задач", - * description="Метод экспорта задач в xlsx", + * description="Метод импорта задач в xlsx", * security={ * {"bearerAuth": {}} * }, @@ -597,7 +597,6 @@ class TaskController extends ApiController * name="companyId", * in="query", * description="ID компании", - * required=true, * @OA\Schema( * type="integer", * ) @@ -606,7 +605,6 @@ class TaskController extends ApiController * name="userId", * in="query", * description="ID исполнителя задачи", - * required=true, * @OA\Schema( * type="integer", * ) @@ -615,7 +613,6 @@ class TaskController extends ApiController * name="projectId", * in="query", * description="ID проекта", - * required=true, * @OA\Schema( * type="integer", * ) From 886ba1e65622921d48c729cd54a55e6d97a99ec1 Mon Sep 17 00:00:00 2001 From: iIronside Date: Thu, 16 Nov 2023 11:55:49 +0300 Subject: [PATCH 32/37] update api for test --- .../helpers/UserQuestionnaireStatusHelper.php | 9 ++ common/models/Answer.php | 5 - common/models/Question.php | 1 - common/models/UserQuestionnaire.php | 5 +- ...mn_start_testing_to_user_questionnaire.php | 25 +++++ .../api/controllers/AnswerController.php | 85 ---------------- .../api/controllers/QuestionController.php | 71 ++++++-------- .../UserQuestionnaireController.php | 96 +------------------ .../controllers/UserResponseController.php | 39 ++++++-- .../api/models/{ => questionnaire}/Answer.php | 29 +++++- .../models/{ => questionnaire}/Question.php | 48 +++++++++- .../{ => questionnaire}/UserQuestionnaire.php | 65 ++++++++++++- .../forms/QuestionnaireUuidForm.php | 32 +++++++ .../api/services/UserQuestionnaireService.php | 62 +++++------- .../api/services/UserResponseService.php | 8 +- 15 files changed, 290 insertions(+), 290 deletions(-) create mode 100644 console/migrations/m231114_072752_add_column_start_testing_to_user_questionnaire.php delete mode 100644 frontend/modules/api/controllers/AnswerController.php rename frontend/modules/api/models/{ => questionnaire}/Answer.php (62%) rename frontend/modules/api/models/{ => questionnaire}/Question.php (65%) rename frontend/modules/api/models/{ => questionnaire}/UserQuestionnaire.php (57%) create mode 100644 frontend/modules/api/models/questionnaire/forms/QuestionnaireUuidForm.php diff --git a/common/helpers/UserQuestionnaireStatusHelper.php b/common/helpers/UserQuestionnaireStatusHelper.php index af45b75..3512ef3 100644 --- a/common/helpers/UserQuestionnaireStatusHelper.php +++ b/common/helpers/UserQuestionnaireStatusHelper.php @@ -23,6 +23,15 @@ class UserQuestionnaireStatusHelper ]; } + public static function listCompleteStatuses(): array + { + return [ + self::STATUS_COMPLETED, + self::STATUS_ON_INSPECTION + ]; + + } + /** * @throws Exception */ diff --git a/common/models/Answer.php b/common/models/Answer.php index d018ad5..25f3a85 100644 --- a/common/models/Answer.php +++ b/common/models/Answer.php @@ -89,11 +89,6 @@ class Answer extends \yii\db\ActiveRecord ->viaTable('question', ['id' => 'question_id']); } -// public function getUserQuestionnaire() -// { -// return $this->hasOne(\backend\modules\questionnaire\models\UserQuestionnaire::className(), ['id']) -// } - static function numCorrectAnswers($question_id) { return Answer::find() diff --git a/common/models/Question.php b/common/models/Question.php index 568db21..4048a50 100644 --- a/common/models/Question.php +++ b/common/models/Question.php @@ -133,7 +133,6 @@ class Question extends \yii\db\ActiveRecord { return self::find()->where(['questionnaire_id' => $questionnaire_id]) ->andWhere(['status' => '1']) - ->AsArray() ->all(); } } diff --git a/common/models/UserQuestionnaire.php b/common/models/UserQuestionnaire.php index 7927db6..7a9af0d 100644 --- a/common/models/UserQuestionnaire.php +++ b/common/models/UserQuestionnaire.php @@ -26,6 +26,8 @@ use \backend\modules\questionnaire\models\Answer; * @property int $score * @property int $status * @property double $percent_correct_answers + * @property string $testing_date + * @property string $start_testing * * @property Questionnaire $questionnaire * @property User $user @@ -63,7 +65,7 @@ class UserQuestionnaire extends ActiveRecord [['questionnaire_id', 'user_id', 'status'], 'required'], [['questionnaire_id', 'user_id', 'score', 'status'], 'integer'], [['percent_correct_answers'], 'number'], - [['created_at', 'updated_at', 'testing_date'], 'safe'], + [['created_at', 'updated_at', 'testing_date', 'start_testing'], 'safe'], [['uuid'], 'string', 'max' => 36], [['uuid'], 'unique'], [['questionnaire_id'], 'exist', 'skipOnError' => true, 'targetClass' => Questionnaire::className(), 'targetAttribute' => ['questionnaire_id' => 'id']], @@ -98,6 +100,7 @@ class UserQuestionnaire extends ActiveRecord 'created_at' => 'Дата создания', 'updated_at' => 'Дата обновления', 'testing_date' => 'Дата тестирования', + 'start_testing' => 'Дата начала тестирования', 'percent_correct_answers' => 'Процент правильных ответов', ]; } diff --git a/console/migrations/m231114_072752_add_column_start_testing_to_user_questionnaire.php b/console/migrations/m231114_072752_add_column_start_testing_to_user_questionnaire.php new file mode 100644 index 0000000..27fb155 --- /dev/null +++ b/console/migrations/m231114_072752_add_column_start_testing_to_user_questionnaire.php @@ -0,0 +1,25 @@ +addColumn('user_questionnaire', 'start_testing', $this->dateTime()); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropColumn('user_questionnaire', 'start_testing'); + } +} diff --git a/frontend/modules/api/controllers/AnswerController.php b/frontend/modules/api/controllers/AnswerController.php deleted file mode 100644 index 57673a2..0000000 --- a/frontend/modules/api/controllers/AnswerController.php +++ /dev/null @@ -1,85 +0,0 @@ - ['get'], - ]; - } - - /** - * @OA\Get(path="/answer/get-answers", - * summary="Список ответов на вопрос", - * description="Получение списка ответов", - * security={ - * {"bearerAuth": {}} - * }, - * tags={"Tests"}, - * @OA\Parameter( - * name="question_id", - * in="query", - * required=true, - * description="id вопроса", - * @OA\Schema( - * type="integer", - * ) - * ), - * @OA\Response( - * response=200, - * description="Возвращает масив вопросов", - * @OA\MediaType( - * mediaType="application/json", - * @OA\Schema(ref="#/components/schemas/AnswerExampleArr"), - * ), - * - * ), - * ) - * - * @throws NotFoundHttpException - */ - public function actionGetAnswers(): array - { - $question_id = Yii::$app->request->get('question_id'); - if(empty($question_id) or !is_numeric($question_id)) - { - throw new NotFoundHttpException('Incorrect question ID'); - } - - $answers = Answer::activeAnswers($question_id); - if(empty($answers)) { - throw new NotFoundHttpException('Answers not found or question inactive'); - } - - array_walk( $answers, function(&$arr){ - unset( - $arr['created_at'], - $arr['updated_at'], - $arr['answer_flag'], - $arr['status'] - ); - }); - - return $answers; - } -} diff --git a/frontend/modules/api/controllers/QuestionController.php b/frontend/modules/api/controllers/QuestionController.php index 4881fc0..e8f562e 100644 --- a/frontend/modules/api/controllers/QuestionController.php +++ b/frontend/modules/api/controllers/QuestionController.php @@ -2,36 +2,35 @@ namespace frontend\modules\api\controllers; -use common\helpers\UUIDHelper; -use common\models\Question; -use common\models\UserQuestionnaire; +use Exception; +use frontend\modules\api\models\questionnaire\forms\QuestionnaireUuidForm; +use frontend\modules\api\models\questionnaire\Question; +use frontend\modules\api\models\questionnaire\UserQuestionnaire; +use frontend\modules\api\services\UserQuestionnaireService; use Yii; -use yii\filters\auth\HttpBearerAuth; -use yii\rest\Controller; -use yii\web\NotFoundHttpException; +use yii\web\BadRequestHttpException; class QuestionController extends ApiController { - public function behaviors() - { - $behaviors = parent::behaviors(); - $behaviors['authenticator']['authMethods'] = [ - HttpBearerAuth::className(), - ]; - return $behaviors; - } + private UserQuestionnaireService $userQuestionnaireService; - public function verbs() + public function __construct( + $id, + $module, + UserQuestionnaireService $userQuestionnaireService, + $config = [] + ) { - return [ - 'get-questions' => ['get'], - ]; + $this->userQuestionnaireService = $userQuestionnaireService; + parent::__construct($id, $module, $config); } /** * @OA\Get(path="/question/get-questions", * summary="Список вопросов", - * description="Получение списка вопросов", + * description="Получение списка вопросов и возможные варианты ответа. Сохраняет временную метку начала тестирования, + от которой будет отсчитываться временной интервал на выполнение теста. При наличии лимита времени на выполнение теста. + При превышении лимита времени на выполнение будет возвращена ошибка: Time's up!", * security={ * {"bearerAuth": {}} * }, @@ -53,40 +52,28 @@ class QuestionController extends ApiController * @OA\Schema(ref="#/components/schemas/QuestionExampleArr"), * ), * - * * ), * ) * - * @throws NotFoundHttpException - * @throws \Exception + * @throws BadRequestHttpException + * @throws Exception */ public function actionGetQuestions(): array { - $uuid = Yii::$app->request->get('uuid'); + $form = new QuestionnaireUuidForm(); - if(empty($uuid) or !UUIDHelper::is_valid($uuid)) - { - throw new NotFoundHttpException('Incorrect questionnaire UUID'); + if ($form->load(Yii::$app->request->get()) && !$form->validate()) { + $errors = $form->errors; + throw new BadRequestHttpException(array_shift($errors)[0]); } - $questionnaire_id = UserQuestionnaire::getQuestionnaireId($uuid); + $userQuestionnaire = UserQuestionnaire::findOne(['uuid' => $form->uuid]); - $questions = Question::activeQuestions($questionnaire_id); - if(empty($questions)) { - throw new NotFoundHttpException('Questions not found'); + if (!$this->userQuestionnaireService->checkTimeLimit($userQuestionnaire)) { + UserQuestionnaireService::calculateScore($userQuestionnaire->uuid); + throw new BadRequestHttpException("Time's up!"); } - array_walk( $questions, function(&$arr){ - unset( - $arr['score'], - $arr['created_at'], - $arr['updated_at'], - $arr['status'], - $arr['questionnaire_id'] - ); - }); - - return $questions; + return Question::activeQuestions($userQuestionnaire->questionnaire_id); } - } diff --git a/frontend/modules/api/controllers/UserQuestionnaireController.php b/frontend/modules/api/controllers/UserQuestionnaireController.php index c769572..50e3c6c 100644 --- a/frontend/modules/api/controllers/UserQuestionnaireController.php +++ b/frontend/modules/api/controllers/UserQuestionnaireController.php @@ -2,8 +2,9 @@ namespace frontend\modules\api\controllers; -use frontend\modules\api\models\UserQuestionnaire; +use frontend\modules\api\models\questionnaire\UserQuestionnaire; use frontend\modules\api\services\UserQuestionnaireService; +use yii\base\InvalidConfigException; use yii\helpers\ArrayHelper; use yii\web\NotFoundHttpException; use yii\web\ServerErrorHttpException; @@ -14,14 +15,11 @@ class UserQuestionnaireController extends ApiController public function behaviors(): array { return ArrayHelper::merge(parent::behaviors(), [ - 'verbs' => [ 'class' => \yii\filters\VerbFilter::class, 'actions' => [ 'questionnaires-list' => ['get'], 'questionnaire-completed' => ['get'], - 'get-points-number' => ['get'], - 'get-question-number' => ['get'], ], ] ]); @@ -73,7 +71,7 @@ class UserQuestionnaireController extends ApiController /** * @OA\Get(path="/user-questionnaire/questionnaire-completed", * summary="Проверка теста", - * description="Выполнения проверки теста", + * description="Выполнение проверки теста", * security={ * {"bearerAuth": {}} * }, @@ -98,7 +96,7 @@ class UserQuestionnaireController extends ApiController * ) * * @throws NotFoundHttpException - * @throws ServerErrorHttpException + * @throws ServerErrorHttpException|InvalidConfigException */ public function actionQuestionnaireCompleted($user_questionnaire_uuid): UserQuestionnaire { @@ -108,90 +106,4 @@ class UserQuestionnaireController extends ApiController } return $userQuestionnaireModel; } - - /** - * @OA\Get(path="/user-questionnaire/get-points-number", - * summary="Количество балов в тесте", - * description="Возвращает максимальное количество балов за тест", - * security={ - * {"bearerAuth": {}} - * }, - * tags={"Tests"}, - * @OA\Parameter( - * name="user_questionnaire_uuid", - * in="query", - * required=true, - * @OA\Schema( - * type="string", - * ) - * ), - * @OA\Response( - * response=200, - * description="Возвращает максимально возможное количество балов за тест", - * @OA\MediaType( - * mediaType="application/json", - * @OA\Schema( - * @OA\Property( - * property="sum_point", - * type="integer", - * example="61", - * ), - * ), - * ), - * - * ), - * ) - * @throws ServerErrorHttpException - */ - public function actionGetPointsNumber($user_questionnaire_uuid): array - { - $questionPointsNumber = UserQuestionnaireService::getPointsNumber($user_questionnaire_uuid); - if (empty($questionPointsNumber)) { - throw new ServerErrorHttpException('Question points not found!'); - } - return $questionPointsNumber; - } - - /** - * @OA\Get(path="/user-questionnaire/get-question-number", - * summary="Число вопросов в тесте", - * description="Возвращает число вопросов в тесте", - * security={ - * {"bearerAuth": {}} - * }, - * tags={"Tests"}, - * @OA\Parameter( - * name="user_questionnaire_uuid", - * in="query", - * required=true, - * @OA\Schema( - * type="string", - * ) - * ), - * @OA\Response( - * response=200, - * description="Возвращает число вопросов в тесте", - * @OA\MediaType( - * mediaType="application/json", - * @OA\Schema( - * @OA\Property( - * property="question_number", - * type="integer", - * example="61", - * ), - * ), - * ), - * - * ), - * ) - * @throws ServerErrorHttpException - */ - public function actionGetQuestionNumber($user_questionnaire_uuid): array - { - $questionNumber = UserQuestionnaireService::getQuestionNumber($user_questionnaire_uuid); - if (empty($questionNumber)) { - throw new ServerErrorHttpException('Question number not found!'); - } - return $questionNumber; - } } diff --git a/frontend/modules/api/controllers/UserResponseController.php b/frontend/modules/api/controllers/UserResponseController.php index d9ffec5..251c378 100644 --- a/frontend/modules/api/controllers/UserResponseController.php +++ b/frontend/modules/api/controllers/UserResponseController.php @@ -2,6 +2,8 @@ namespace frontend\modules\api\controllers; +use frontend\modules\api\models\questionnaire\UserQuestionnaire; +use frontend\modules\api\services\UserQuestionnaireService; use frontend\modules\api\services\UserResponseService; use Yii; use yii\base\InvalidConfigException; @@ -10,18 +12,24 @@ use yii\web\ServerErrorHttpException; class UserResponseController extends ApiController { - public function verbs(): array + private UserQuestionnaireService $userQuestionnaireService; + + public function __construct( + $id, + $module, + UserQuestionnaireService $userQuestionnaireService, + $config = [] + ) { - return [ - 'set-response' => ['post'], - 'set-responses' => ['post'], - ]; + $this->userQuestionnaireService = $userQuestionnaireService; + parent::__construct($id, $module, $config); } /** * @OA\Post(path="/user-response/set-responses", * summary="Добавить массив ответов пользователя", - * description="Добавление массива ответов на вопросы от пользователя", + * description="Добавление массива ответов на вопросы от пользователя. При наличии лимита времени на выполнение теста, + будет проведена проверка. При превышении лимита времени на выполнение будет возвращена ошибка: Time's up!", * security={ * {"bearerAuth": {}} * }, @@ -58,7 +66,7 @@ class UserResponseController extends ApiController * * @OA\Response( * response=200, - * description="Возвращает объект Запроса", + * description="Возвращает масив ответов", * @OA\MediaType( * mediaType="application/json", * @OA\Schema(ref="#/components/schemas/UserResponseExampleArr"), @@ -66,12 +74,25 @@ class UserResponseController extends ApiController * ), * ) * + * @return array + * @throws BadRequestHttpException * @throws InvalidConfigException - * @throws ServerErrorHttpException|BadRequestHttpException + * @throws ServerErrorHttpException + * @throws \yii\web\NotFoundHttpException */ public function actionSetResponses(): array { - $userResponseModels = UserResponseService::createUserResponses(Yii::$app->getRequest()->getBodyParams()); + $uuid = Yii::$app->request->post('user_questionnaire_uuid'); + $userResponses = Yii::$app->request->post('userResponses'); + + $userQuestionnaire = UserQuestionnaire::findOne(['uuid' => $uuid]); + + if (!$this->userQuestionnaireService->checkTimeLimit($userQuestionnaire)) { + UserQuestionnaireService::calculateScore($userQuestionnaire->uuid); + throw new BadRequestHttpException("Time's up!"); + } + + $userResponseModels = UserResponseService::createUserResponses($userResponses, $uuid); foreach ($userResponseModels as $model) { if ($model->errors) { throw new ServerErrorHttpException(json_encode($model->errors)); diff --git a/frontend/modules/api/models/Answer.php b/frontend/modules/api/models/questionnaire/Answer.php similarity index 62% rename from frontend/modules/api/models/Answer.php rename to frontend/modules/api/models/questionnaire/Answer.php index a22fbc7..abc87a1 100644 --- a/frontend/modules/api/models/Answer.php +++ b/frontend/modules/api/models/questionnaire/Answer.php @@ -1,6 +1,6 @@ function () { + return $this->answers; + } + ]; + } + /** + * @return string[] + */ + public function extraFields(): array + { + return []; + } + + /** + * @return ActiveQuery + */ + public function getAnswers(): ActiveQuery + { + return $this->hasMany(Answer::class, ['question_id' => 'id']); + } } \ No newline at end of file diff --git a/frontend/modules/api/models/UserQuestionnaire.php b/frontend/modules/api/models/questionnaire/UserQuestionnaire.php similarity index 57% rename from frontend/modules/api/models/UserQuestionnaire.php rename to frontend/modules/api/models/questionnaire/UserQuestionnaire.php index 5ea7c37..21ed16b 100644 --- a/frontend/modules/api/models/UserQuestionnaire.php +++ b/frontend/modules/api/models/questionnaire/UserQuestionnaire.php @@ -1,6 +1,6 @@ function() { + 'questionnaire_title' => function () { return $this->questionnaire->title; }, - 'description' => function() { + 'description' => function () { return $this->questionnaire->description; }, + 'points_number' => function () { + return Question::find() + ->where(['questionnaire_id' => $this->questionnaire_id]) + ->andWhere(['status' => 1]) + ->sum('score'); + }, + 'number_questions' => function () { + return Question::find() + ->where(['questionnaire_id' => $this->questionnaire_id]) + ->andWhere(['status' => 1]) + ->count(); + }, + 'time_limit' => function () { + return $this->questionnaire->time_limit; + } ]; } diff --git a/frontend/modules/api/models/questionnaire/forms/QuestionnaireUuidForm.php b/frontend/modules/api/models/questionnaire/forms/QuestionnaireUuidForm.php new file mode 100644 index 0000000..ce1ab4f --- /dev/null +++ b/frontend/modules/api/models/questionnaire/forms/QuestionnaireUuidForm.php @@ -0,0 +1,32 @@ + false, 'targetClass' => UserQuestionnaire::class, 'targetAttribute' => ['uuid' => 'uuid']], + ]; + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } +} diff --git a/frontend/modules/api/services/UserQuestionnaireService.php b/frontend/modules/api/services/UserQuestionnaireService.php index 9f1340f..3062cc8 100644 --- a/frontend/modules/api/services/UserQuestionnaireService.php +++ b/frontend/modules/api/services/UserQuestionnaireService.php @@ -2,12 +2,12 @@ namespace frontend\modules\api\services; -use common\models\Question; +use common\helpers\UserQuestionnaireStatusHelper; use common\services\ScoreCalculatorService; -use frontend\modules\api\models\UserQuestionnaire; +use frontend\modules\api\models\questionnaire\UserQuestionnaire; use yii\base\InvalidConfigException; +use yii\web\BadRequestHttpException; use yii\web\NotFoundHttpException; -use yii\web\ServerErrorHttpException; class UserQuestionnaireService { @@ -26,45 +26,33 @@ class UserQuestionnaireService if (empty($userQuestionnaireModel)) { throw new NotFoundHttpException('The questionnaire with this uuid does not exist'); } - ScoreCalculatorService::rateResponses($userQuestionnaireModel); - if (ScoreCalculatorService::checkAnswerFlagsForNull($userQuestionnaireModel)) { - ScoreCalculatorService::calculateScore($userQuestionnaireModel); - } else { - $userQuestionnaireModel->status = 3; - $userQuestionnaireModel->save(); - } + + if (!in_array($userQuestionnaireModel->status, UserQuestionnaireStatusHelper::listCompleteStatuses() )) { + ScoreCalculatorService::rateResponses($userQuestionnaireModel); + if (ScoreCalculatorService::checkAnswerFlagsForNull($userQuestionnaireModel)) { + ScoreCalculatorService::calculateScore($userQuestionnaireModel); + } else { + $userQuestionnaireModel->status = 3; + $userQuestionnaireModel->save(); + } + } return $userQuestionnaireModel; } - /** - * @throws ServerErrorHttpException - */ - public static function getQuestionNumber($user_questionnaire_uuid): array + public function checkTimeLimit(UserQuestionnaire $userQuestionnaire): bool { - $userQuestionnaireModel = UserQuestionnaire::findOne(['uuid' => $user_questionnaire_uuid]); - if (empty($userQuestionnaireModel)) { - throw new ServerErrorHttpException('Not found UserQuestionnaire'); - } - $count = Question::find() - ->where(['questionnaire_id' => $userQuestionnaireModel->questionnaire_id]) - ->andWhere(['status' => 1]) - ->count(); - return array('question_number' => $count); - } + if (!$userQuestionnaire->start_testing) { + $userQuestionnaire->start_testing = date('Y:m:d H:i:s'); + $userQuestionnaire->save(); + } elseif ($userQuestionnaire->questionnaire->time_limit) { + $limitTime = strtotime($userQuestionnaire->questionnaire->time_limit) - strtotime("00:00:00"); + $currentTime = strtotime(date('Y-m-d H:i:s')); + $startTesting = strtotime($userQuestionnaire->start_testing); - /** - * @throws ServerErrorHttpException - */ - public static function getPointsNumber($user_questionnaire_uuid) - { - $userQuestionnaireModel = UserQuestionnaire::findOne(['uuid' => $user_questionnaire_uuid]); - if (empty($userQuestionnaireModel)) { - throw new ServerErrorHttpException('Not found UserQuestionnaire'); + if ($currentTime - $startTesting > $limitTime) { + return false; + } } - $pointSum = Question::find() - ->where(['questionnaire_id' => $userQuestionnaireModel->questionnaire_id]) - ->andWhere(['status' => 1]) - ->sum('score'); - return array('sum_point' => $pointSum); + return true; } } \ No newline at end of file diff --git a/frontend/modules/api/services/UserResponseService.php b/frontend/modules/api/services/UserResponseService.php index 7b41bdb..1d3abfe 100644 --- a/frontend/modules/api/services/UserResponseService.php +++ b/frontend/modules/api/services/UserResponseService.php @@ -14,17 +14,19 @@ class UserResponseService * @throws BadRequestHttpException * @throws ServerErrorHttpException */ - public static function createUserResponses($userResponsesParams): array + public static function createUserResponses($userResponsesParams, $uuid): array { $userResponseModels = array(); foreach ($userResponsesParams as $userResponse) { + $model = new UserResponse(); $model->load($userResponse, ''); + $model->user_questionnaire_uuid = $uuid; try { self::validateResponseModel($model); } catch (\Exception $ex) { - throw new BadRequestHttpException(json_encode('One of the parameters is empty!')); + throw new BadRequestHttpException($ex->getMessage()); } @@ -48,7 +50,7 @@ class UserResponseService } if (empty($model->user_id) or empty($model->question_id) or empty($model->user_questionnaire_uuid)) { - throw new BadRequestHttpException(json_encode('One of the parameters is empty!')); + throw new BadRequestHttpException(json_encode('One of t222he parameters is empty!')); } } From 91607cc99b2e9e55891bee0e9d08fb2b0ad3f956 Mon Sep 17 00:00:00 2001 From: iIronside Date: Mon, 20 Nov 2023 11:39:44 +0300 Subject: [PATCH 33/37] add api/task/get-archive-task method --- .../modules/task/models/ProjectTaskSearch.php | 2 - common/models/ProjectTask.php | 4 +- .../api/controllers/TaskController.php | 76 ++++++++++++++----- .../api/models/project/ProjectTask.php | 2 +- frontend/modules/api/services/TaskService.php | 12 ++- 5 files changed, 73 insertions(+), 23 deletions(-) diff --git a/backend/modules/task/models/ProjectTaskSearch.php b/backend/modules/task/models/ProjectTaskSearch.php index 9c20656..5a05cba 100644 --- a/backend/modules/task/models/ProjectTaskSearch.php +++ b/backend/modules/task/models/ProjectTaskSearch.php @@ -2,10 +2,8 @@ namespace backend\modules\task\models; -use backend\modules\project\models\ProjectUser; use yii\base\Model; use yii\data\ActiveDataProvider; -use backend\modules\task\models\ProjectTask; /** * TaskSearch represents the model behind the search form of `backend\modules\task\models\Task`. diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index e93838e..a3a541f 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -38,8 +38,9 @@ use yii\helpers\ArrayHelper; */ class ProjectTask extends ActiveRecord { - const STATUS_ACTIVE = 1; const STATUS_DISABLE = 0; + const STATUS_ACTIVE = 1; + const STATUS_ARCHIVE = 2; const PRIORITY_LOW = 0; const PRIORITY_MEDIUM = 1; @@ -101,6 +102,7 @@ class ProjectTask extends ActiveRecord ['execution_priority', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], ['title', 'unique', 'targetAttribute' => ['title', 'project_id'], 'message' => 'Такая задача уже создана'], [['title'], 'string', 'max' => 255], + ['status', 'in', 'range' => [self::STATUS_DISABLE, self::STATUS_ACTIVE, self::STATUS_ARCHIVE]], [['description'], 'string', 'max' => 1500], [['project_id'], 'exist', 'skipOnError' => true, 'targetClass' => Project::className(), 'targetAttribute' => ['project_id' => 'id']], [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']], diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index 0854ea2..b559216 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -16,6 +16,9 @@ use yii\web\ServerErrorHttpException; class TaskController extends ApiController { + /** + * @var TaskService + */ private TaskService $taskService; /** @@ -166,15 +169,7 @@ class TaskController extends ApiController */ public function actionGetTaskList($project_id): array { - $tasks = array(); - if ($project_id) { - if (empty($project_id) or !is_numeric($project_id)) { - throw new NotFoundHttpException('Incorrect project ID'); - } - $tasks = $this->taskService->getTaskListByProject($project_id); - } else { - $tasks = $this->taskService->getTaskList($project_id); - } + $tasks = $this->taskService->getTaskListByProject($project_id); if (empty($tasks)) { throw new NotFoundHttpException('The project does not exist or there are no tasks for it'); @@ -182,6 +177,59 @@ class TaskController extends ApiController return $tasks; } + /** + * + * @OA\Get(path="/task/get-archive-task", + * summary="Получить список архивных задач по проекту", + * description="Метод для получения архивных задач по проекту", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TaskManager"}, + * @OA\Parameter( + * name="project_id", + * in="query", + * required=true, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="user_id", + * in="query", + * required=false, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( + * name="expand", + * in="query", + * example="column,timers,mark", + * description="В этом параметре по необходимости передаются поля, которые нужно добавить в ответ сервера, сейчас доступно только поля column, timers и mark", + * @OA\Schema( + * type="string", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает массив объектов Задач", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ProjectTaskExample"), + * ), + * ), + * ) + * + * @param $project_id + * @param null $user_id + * @return array + */ + public function actionGetArchiveTask($project_id, $user_id = null): array + { + return $this->taskService->getArchiveTask($project_id, $user_id); + } + /** * * @OA\Get(path="/task/get-user-tasks", @@ -224,15 +272,7 @@ class TaskController extends ApiController */ public function actionGetUserTasks($user_id): array { - $tasks = array(); - if ($user_id) { - if (empty($user_id) or !is_numeric($user_id)) { - throw new NotFoundHttpException('Incorrect project ID'); - } - $tasks = $this->taskService->getTaskListByUser($user_id); - } else { - $tasks = $this->taskService->getTaskList($user_id); - } + $tasks = $this->taskService->getTaskListByUser($user_id); if (empty($tasks)) { throw new NotFoundHttpException('The project does not exist or there are no tasks for it'); diff --git a/frontend/modules/api/models/project/ProjectTask.php b/frontend/modules/api/models/project/ProjectTask.php index 436e343..9435166 100644 --- a/frontend/modules/api/models/project/ProjectTask.php +++ b/frontend/modules/api/models/project/ProjectTask.php @@ -80,7 +80,7 @@ namespace frontend\modules\api\models\project; * property="status", * type="int", * example="1", - * description="Статус задачи" + * description="Статус задачи(0 - disable, 1 - active, 2 - archive)" * ), * @OA\Property( * property="comment_count", diff --git a/frontend/modules/api/services/TaskService.php b/frontend/modules/api/services/TaskService.php index 66629ec..809a42e 100644 --- a/frontend/modules/api/services/TaskService.php +++ b/frontend/modules/api/services/TaskService.php @@ -30,7 +30,7 @@ class TaskService return ProjectTask::findOne($task_id); } - public function getTaskList($task_id): array + public function getTaskList($status = null): array { return ProjectTask::find()->asArray()->all(); } @@ -40,6 +40,16 @@ class TaskService return ProjectTask::find()->where(['project_id' => $project_id])->orderBy('priority DESC')->all(); } + public function getArchiveTask($project_id, $user_id): array + { + $query = ProjectTask::find()->where(['project_id' => $project_id])->andWhere(['status' => ProjectTask::STATUS_ARCHIVE]); + + if ($user_id) { + $query->andWhere(['user_id' => $user_id]); + } + return $query->orderBy('priority DESC')->all(); + } + public function getTaskListByUser($user_id): array { $taskIdList = ProjectTaskUser::find()->where(['user_id' => $user_id])->select('task_id')->column(); From 75329a88359e665f1ccc038555e2df94e86d81e8 Mon Sep 17 00:00:00 2001 From: iIronside Date: Tue, 21 Nov 2023 11:23:38 +0300 Subject: [PATCH 34/37] add project statistic to api --- .../controllers/ProjectRoleController.php | 127 ++++++++++++++++ .../modules/project/models/ProjectRole.php | 7 + .../project/models/ProjectRoleSearch.php | 68 +++++++++ .../project/models/ProjectUserSearch.php | 4 +- .../project/views/project-role/_form.php | 23 +++ .../project/views/project-role/_search.php | 29 ++++ .../project/views/project-role/create.php | 18 +++ .../project/views/project-role/index.php | 30 ++++ .../project/views/project-role/update.php | 21 +++ .../project/views/project-role/view.php | 36 +++++ .../views/project-user/_form_update.php | 20 ++- .../project/views/project-user/index.php | 18 +++ .../project/views/project-user/view.php | 8 + backend/views/layouts/left.php | 3 +- common/models/Project.php | 28 ++-- common/models/ProjectRole.php | 53 +++++++ common/models/ProjectTask.php | 3 +- common/models/ProjectUser.php | 47 ++++-- ...31120_121208_create_project_role_table.php | 28 ++++ ..._project_role_id_to_project_user_table.php | 38 +++++ ...dd_status_column_to_project_user_table.php | 27 ++++ .../api/controllers/ProjectController.php | 37 +++++ .../models/project/ProjectParticipants.php | 142 ++++++++++++++++++ .../api/models/project/ProjectStatistic.php | 139 +++++++++++++++++ 24 files changed, 929 insertions(+), 25 deletions(-) create mode 100644 backend/modules/project/controllers/ProjectRoleController.php create mode 100644 backend/modules/project/models/ProjectRole.php create mode 100644 backend/modules/project/models/ProjectRoleSearch.php create mode 100644 backend/modules/project/views/project-role/_form.php create mode 100644 backend/modules/project/views/project-role/_search.php create mode 100644 backend/modules/project/views/project-role/create.php create mode 100644 backend/modules/project/views/project-role/index.php create mode 100644 backend/modules/project/views/project-role/update.php create mode 100644 backend/modules/project/views/project-role/view.php create mode 100644 common/models/ProjectRole.php create mode 100644 console/migrations/m231120_121208_create_project_role_table.php create mode 100644 console/migrations/m231120_122131_add_project_role_id_to_project_user_table.php create mode 100644 console/migrations/m231120_134302_add_status_column_to_project_user_table.php create mode 100644 frontend/modules/api/models/project/ProjectParticipants.php create mode 100644 frontend/modules/api/models/project/ProjectStatistic.php diff --git a/backend/modules/project/controllers/ProjectRoleController.php b/backend/modules/project/controllers/ProjectRoleController.php new file mode 100644 index 0000000..e620245 --- /dev/null +++ b/backend/modules/project/controllers/ProjectRoleController.php @@ -0,0 +1,127 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ]; + } + + /** + * Lists all ProjectRole models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new ProjectRoleSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single ProjectRole model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new ProjectRole model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new ProjectRole(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing ProjectRole model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing ProjectRole model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the ProjectRole model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return ProjectRole the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = ProjectRole::findOne($id)) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested page does not exist.'); + } +} diff --git a/backend/modules/project/models/ProjectRole.php b/backend/modules/project/models/ProjectRole.php new file mode 100644 index 0000000..4666069 --- /dev/null +++ b/backend/modules/project/models/ProjectRole.php @@ -0,0 +1,7 @@ + $query, + ]); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere([ + 'id' => $this->id, + ]); + + $query->andFilterWhere(['like', 'title', $this->title]); + + return $dataProvider; + } +} diff --git a/backend/modules/project/models/ProjectUserSearch.php b/backend/modules/project/models/ProjectUserSearch.php index 631067b..a201f84 100644 --- a/backend/modules/project/models/ProjectUserSearch.php +++ b/backend/modules/project/models/ProjectUserSearch.php @@ -4,7 +4,6 @@ namespace backend\modules\project\models; use yii\base\Model; use yii\data\ActiveDataProvider; -use backend\modules\project\models\ProjectUser; /** * ProjectUserSearch represents the model behind the search form of `backend\modules\project\models\ProjectUser`. @@ -17,7 +16,7 @@ class ProjectUserSearch extends ProjectUser public function rules() { return [ - [['id', 'project_id', 'user_id', 'card_id'], 'integer'], + [['id', 'project_id', 'user_id', 'card_id', 'project_role_id'], 'integer'], ]; } @@ -61,6 +60,7 @@ class ProjectUserSearch extends ProjectUser 'project_id' => $this->project_id, 'user_id' => $this->user_id, 'card_id' => $this->card_id, + 'project_role_id' => $this->project_role_id, ]); return $dataProvider; diff --git a/backend/modules/project/views/project-role/_form.php b/backend/modules/project/views/project-role/_form.php new file mode 100644 index 0000000..7e3ee8d --- /dev/null +++ b/backend/modules/project/views/project-role/_form.php @@ -0,0 +1,23 @@ + + +
+ + + + field($model, 'title')->textInput(['maxlength' => true]) ?> + +
+ 'btn btn-success']) ?> +
+ + + +
diff --git a/backend/modules/project/views/project-role/_search.php b/backend/modules/project/views/project-role/_search.php new file mode 100644 index 0000000..a269286 --- /dev/null +++ b/backend/modules/project/views/project-role/_search.php @@ -0,0 +1,29 @@ + + + diff --git a/backend/modules/project/views/project-role/create.php b/backend/modules/project/views/project-role/create.php new file mode 100644 index 0000000..29a6fde --- /dev/null +++ b/backend/modules/project/views/project-role/create.php @@ -0,0 +1,18 @@ +title = 'Создать новую роль'; +$this->params['breadcrumbs'][] = ['label' => 'Роли сотрудников на проекте ', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/project/views/project-role/index.php b/backend/modules/project/views/project-role/index.php new file mode 100644 index 0000000..4f7b13a --- /dev/null +++ b/backend/modules/project/views/project-role/index.php @@ -0,0 +1,30 @@ +title = 'Роли сотрудников на проекте'; +$this->params['breadcrumbs'][] = $this->title; +?> +
+ +

+ 'btn btn-success']) ?> +

+ + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + ['class' => 'yii\grid\SerialColumn'], + + 'title', + + ['class' => 'yii\grid\ActionColumn'], + ], + ]); ?> +
diff --git a/backend/modules/project/views/project-role/update.php b/backend/modules/project/views/project-role/update.php new file mode 100644 index 0000000..9151e91 --- /dev/null +++ b/backend/modules/project/views/project-role/update.php @@ -0,0 +1,21 @@ +title = 'Update Project Role: ' . $model->title; +$this->params['breadcrumbs'][] = ['label' => 'Project Roles', 'url' => ['index']]; +$this->params['breadcrumbs'][] = ['label' => $model->title, 'url' => ['view', 'id' => $model->id]]; +$this->params['breadcrumbs'][] = 'Update'; +?> +
+ +

title) ?>

+ + render('_form', [ + 'model' => $model, + ]) ?> + +
diff --git a/backend/modules/project/views/project-role/view.php b/backend/modules/project/views/project-role/view.php new file mode 100644 index 0000000..6f8e1da --- /dev/null +++ b/backend/modules/project/views/project-role/view.php @@ -0,0 +1,36 @@ +title = $model->title; +$this->params['breadcrumbs'][] = ['label' => 'Роли сотрудников на проекте', 'url' => ['index']]; +$this->params['breadcrumbs'][] = $this->title; +\yii\web\YiiAsset::register($this); +?> +
+ +

+ $model->id], ['class' => 'btn btn-primary']) ?> + $model->id], ['class' => 'btn btn-primary']) ?> + $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Are you sure you want to delete this item?', + 'method' => 'post', + ], + ]) ?> +

+ + $model, + 'attributes' => [ + 'id', + 'title', + ], + ]) ?> + +
diff --git a/backend/modules/project/views/project-user/_form_update.php b/backend/modules/project/views/project-user/_form_update.php index 440d1d5..91f5977 100644 --- a/backend/modules/project/views/project-user/_form_update.php +++ b/backend/modules/project/views/project-user/_form_update.php @@ -2,7 +2,8 @@ use backend\modules\card\models\UserCard; use backend\modules\project\models\Project; -use common\models\User; +use backend\modules\project\models\ProjectRole; +use backend\modules\project\models\ProjectUser; use kartik\select2\Select2; use yii\helpers\Html; use yii\widgets\ActiveForm; @@ -37,6 +38,23 @@ use yii\widgets\ActiveForm; ] ) ?> + field($model, 'project_role_id')->widget(Select2::className(), + [ + 'data' => ProjectRole::find()->select(['title', 'id'])->indexBy('id')->column(), + 'options' => ['placeholder' => '...','class' => 'form-control'], + 'pluginOptions' => [ + 'allowClear' => true, + 'multiple' => false, + ], + ] + ) ?> + + field($model, 'status')->dropDownList(ProjectUser::statusList(), + [ + 'prompt' => 'Выберите' + ] + ) ?> +
'btn btn-success']) ?>
diff --git a/backend/modules/project/views/project-user/index.php b/backend/modules/project/views/project-user/index.php index e0f5dee..15f5ed4 100644 --- a/backend/modules/project/views/project-user/index.php +++ b/backend/modules/project/views/project-user/index.php @@ -2,6 +2,7 @@ use backend\modules\card\models\UserCard; use backend\modules\project\models\Project; +use backend\modules\project\models\ProjectRole; use common\models\User; use kartik\select2\Select2; use yii\helpers\Html; @@ -79,6 +80,23 @@ $this->params['breadcrumbs'][] = $this->title; ], ]) ], + [ + 'attribute' => 'project_role_id', + 'value' => 'projectRole.title', + 'filter' => Select2::widget([ + 'model' => $searchModel, + 'attribute' => 'project_role_id', + 'data' => ProjectRole::find()->select(['title', 'id'])->indexBy('id')->column(), + 'pluginOptions' => [ + 'allowClear' => true, + 'width' => '250px', + ], + 'options' => [ + 'class' => 'form-control', + 'placeholder' => 'Выберите значение' + ], + ]) + ], ['class' => 'yii\grid\ActionColumn'], ], diff --git a/backend/modules/project/views/project-user/view.php b/backend/modules/project/views/project-user/view.php index 8de549c..d843a66 100644 --- a/backend/modules/project/views/project-user/view.php +++ b/backend/modules/project/views/project-user/view.php @@ -43,6 +43,14 @@ YiiAsset::register($this); 'attribute' => 'card_id', 'value' => ArrayHelper::getValue($model, 'card.fio' ), ], + [ + 'attribute' => 'project_role_id', + 'value' => ArrayHelper::getValue($model, 'projectRole.title' ), + ], + [ + 'attribute' => 'status', + 'value' => ArrayHelper::getValue($model->statusList(),$model->status ), + ], ], ]) ?> diff --git a/backend/views/layouts/left.php b/backend/views/layouts/left.php index defc4e0..cf19730 100755 --- a/backend/views/layouts/left.php +++ b/backend/views/layouts/left.php @@ -18,7 +18,8 @@ $projectItems[] = ['label' => $status, 'icon' => 'user', 'url' => ['/project/project?ProjectSearch[status]=' . $key, 'active' => \Yii::$app->controller->id == 'project']]; } $projectItems[] = ['label' => 'Сотрудники на проектах', 'icon' => 'users', 'url' => ['/project/project-user'], 'active' => \Yii::$app->controller->id == 'project-user']; - $projectItems[] = ['label' => 'метки проектов', 'icon' => 'tags', 'url' => ['/project/project-mark'], 'active' => \Yii::$app->controller->id == 'project-mark']; + $projectItems[] = ['label' => 'Метки проектов', 'icon' => 'tags', 'url' => ['/project/project-mark'], 'active' => \Yii::$app->controller->id == 'project-mark']; + $projectItems[] = ['label' => 'Роли на проекте', 'icon' => 'user-o', 'url' => ['/project/project-role'], 'active' => \Yii::$app->controller->id == 'project-role']; ?> hasMany(ProjectTask::class, ['project_id' => 'id']); + } + + /** + * @return ActiveQuery */ public function getOwner() { @@ -123,7 +131,7 @@ class Project extends \yii\db\ActiveRecord } /** - * @return \yii\db\ActiveQuery + * @return ActiveQuery */ public function getHh() { @@ -131,7 +139,7 @@ class Project extends \yii\db\ActiveRecord } /** - * @return \yii\db\ActiveQuery + * @return ActiveQuery */ public function getProjectUsers() { @@ -139,7 +147,7 @@ class Project extends \yii\db\ActiveRecord } /** - * @return \yii\db\ActiveQuery + * @return ActiveQuery */ public function getMark() { @@ -148,7 +156,7 @@ class Project extends \yii\db\ActiveRecord } /** - * @return \yii\db\ActiveQuery + * @return ActiveQuery */ public function getMarkEntity() { diff --git a/common/models/ProjectRole.php b/common/models/ProjectRole.php new file mode 100644 index 0000000..7c32021 --- /dev/null +++ b/common/models/ProjectRole.php @@ -0,0 +1,53 @@ + 255], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'title' => 'Название роли', + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProjectUsers() + { + return $this->hasMany(ProjectUser::className(), ['project_role_id' => 'id']); + } +} diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index a3a541f..991a24c 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -41,6 +41,7 @@ class ProjectTask extends ActiveRecord const STATUS_DISABLE = 0; const STATUS_ACTIVE = 1; const STATUS_ARCHIVE = 2; + const STATUS_AT_WORK = 3; const PRIORITY_LOW = 0; const PRIORITY_MEDIUM = 1; @@ -102,7 +103,7 @@ class ProjectTask extends ActiveRecord ['execution_priority', 'in', 'range' => [self::PRIORITY_LOW, self::PRIORITY_MEDIUM, self::PRIORITY_HIGH]], ['title', 'unique', 'targetAttribute' => ['title', 'project_id'], 'message' => 'Такая задача уже создана'], [['title'], 'string', 'max' => 255], - ['status', 'in', 'range' => [self::STATUS_DISABLE, self::STATUS_ACTIVE, self::STATUS_ARCHIVE]], + ['status', 'in', 'range' => [self::STATUS_DISABLE, self::STATUS_ACTIVE, self::STATUS_ARCHIVE, self::STATUS_AT_WORK]], [['description'], 'string', 'max' => 1500], [['project_id'], 'exist', 'skipOnError' => true, 'targetClass' => Project::className(), 'targetAttribute' => ['project_id' => 'id']], [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']], diff --git a/common/models/ProjectUser.php b/common/models/ProjectUser.php index c753696..232c782 100644 --- a/common/models/ProjectUser.php +++ b/common/models/ProjectUser.php @@ -14,14 +14,28 @@ use yii\helpers\ArrayHelper; * @property int $card_id * @property int $project_id * @property int $user_id + * @property int $project_role_id + * @property int $status * * @property Project $project * @property UserCard $card * @property User $user + * @property ProjectRole $projectRole * @property ProjectTaskUser[] $taskUsers */ class ProjectUser extends \yii\db\ActiveRecord { + public const STATUS_INACTIVE = 0; + public const STATUS_ACTIVE = 1; + + public static function statusList() :array + { + return [ + self::STATUS_INACTIVE => 'Не активен', + self::STATUS_ACTIVE => 'Активен', + ]; + } + /** * {@inheritdoc} */ @@ -39,10 +53,13 @@ class ProjectUser extends \yii\db\ActiveRecord [['user_id', 'project_id', 'card_id'], 'required'], ['user_id', 'unique', 'targetAttribute' => ['user_id', 'project_id'], 'message'=>'Сотрудник уже назначен на этот проект'], ['card_id', 'unique', 'targetAttribute' => ['card_id', 'project_id'], 'message'=>'Сотрудник уже назначен на этот проект'], - [['card_id', 'project_id', 'user_id'], 'integer'], - [['project_id'], 'exist', 'skipOnError' => true, 'targetClass' => Project::className(), 'targetAttribute' => ['project_id' => 'id']], - [['card_id'], 'exist', 'skipOnError' => true, 'targetClass' => UserCard::className(), 'targetAttribute' => ['card_id' => 'id']], - [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::className(), 'targetAttribute' => ['user_id' => 'id']], + [['card_id', 'project_id', 'user_id', 'project_role_id', 'status'], 'integer'], + [['status'], 'default', 'value'=> self::STATUS_ACTIVE], + ['status', 'in', 'range' => [self::STATUS_INACTIVE, self::STATUS_ACTIVE]], + [['project_id'], 'exist', 'skipOnError' => true, 'targetClass' => Project::class, 'targetAttribute' => ['project_id' => 'id']], + [['card_id'], 'exist', 'skipOnError' => true, 'targetClass' => UserCard::class, 'targetAttribute' => ['card_id' => 'id']], + [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['user_id' => 'id']], + [['project_role_id'], 'exist', 'skipOnError' => true, 'targetClass' => ProjectRole::class, 'targetAttribute' => ['project_role_id' => 'id']], ]; } @@ -56,6 +73,8 @@ class ProjectUser extends \yii\db\ActiveRecord 'card_id' => 'Карточка', 'project_id' => 'Проект', 'user_id' => 'Сотрудник', + 'project_role_id' => 'Роль на проекте', + 'status' => 'Статус' ]; } @@ -64,7 +83,7 @@ class ProjectUser extends \yii\db\ActiveRecord */ public function getProject() { - return $this->hasOne(Project::className(), ['id' => 'project_id']); + return $this->hasOne(Project::class, ['id' => 'project_id']); } /** @@ -72,7 +91,7 @@ class ProjectUser extends \yii\db\ActiveRecord */ public function getCard() { - return $this->hasOne(UserCard::className(), ['id' => 'card_id']); + return $this->hasOne(UserCard::class, ['id' => 'card_id']); } /** @@ -80,7 +99,7 @@ class ProjectUser extends \yii\db\ActiveRecord */ public function getUser() { - return $this->hasOne(User::className(), ['id' => 'user_id']); + return $this->hasOne(User::class, ['id' => 'user_id']); } /** @@ -88,7 +107,7 @@ class ProjectUser extends \yii\db\ActiveRecord */ public function getTasks() { - return $this->hasMany(ProjectTask::className(), ['project_user_id' => 'id']); + return $this->hasMany(ProjectTask::class, ['project_user_id' => 'id']); } /** @@ -96,7 +115,15 @@ class ProjectUser extends \yii\db\ActiveRecord */ public function getTasksByProject() { - return $this->hasMany(ProjectTask::className(), ['project_id' => 'project_id']); + return $this->hasMany(ProjectTask::class, ['project_id' => 'project_id']); + } + + /** + * @return ActiveQuery + */ + public function getProjectRole(): ActiveQuery + { + return $this->hasOne(ProjectRole::class, ['id' => 'project_role_id']); } /** @@ -104,7 +131,7 @@ class ProjectUser extends \yii\db\ActiveRecord */ public function getTaskUsers() { - return $this->hasMany(ProjectTaskUser::className(), ['project_user_id' => 'id']); + return $this->hasMany(ProjectTaskUser::class, ['project_user_id' => 'id']); } public static function usersByProjectArr($project_id): array diff --git a/console/migrations/m231120_121208_create_project_role_table.php b/console/migrations/m231120_121208_create_project_role_table.php new file mode 100644 index 0000000..7d86a86 --- /dev/null +++ b/console/migrations/m231120_121208_create_project_role_table.php @@ -0,0 +1,28 @@ +createTable('{{%project_role}}', [ + 'id' => $this->primaryKey(), + 'title' => $this->string(), + ]); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('{{%project_role}}'); + } +} diff --git a/console/migrations/m231120_122131_add_project_role_id_to_project_user_table.php b/console/migrations/m231120_122131_add_project_role_id_to_project_user_table.php new file mode 100644 index 0000000..b17aa6c --- /dev/null +++ b/console/migrations/m231120_122131_add_project_role_id_to_project_user_table.php @@ -0,0 +1,38 @@ +addColumn(self::TABLE_NAME, self::TABLE_COLUMN, $this->integer()->defaultValue(null)); + + $this->addForeignKey( + 'project_role_project_user', + self::TABLE_NAME, + self::TABLE_COLUMN, + 'project_role', + 'id' + ); + + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropForeignKey('project_role_project_user', self::TABLE_NAME); + $this->dropColumn(self::TABLE_NAME, self::TABLE_COLUMN ); + } +} diff --git a/console/migrations/m231120_134302_add_status_column_to_project_user_table.php b/console/migrations/m231120_134302_add_status_column_to_project_user_table.php new file mode 100644 index 0000000..e86311b --- /dev/null +++ b/console/migrations/m231120_134302_add_status_column_to_project_user_table.php @@ -0,0 +1,27 @@ +addColumn(self::TABLE_NAME, self::TABLE_COLUMN, $this->integer()->defaultValue(null)); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropColumn(self::TABLE_NAME, self::TABLE_COLUMN); + } +} diff --git a/frontend/modules/api/controllers/ProjectController.php b/frontend/modules/api/controllers/ProjectController.php index 68692f6..a59da02 100644 --- a/frontend/modules/api/controllers/ProjectController.php +++ b/frontend/modules/api/controllers/ProjectController.php @@ -7,6 +7,7 @@ use common\models\Status; use common\models\UseStatus; use frontend\modules\api\models\Manager; use frontend\modules\api\models\project\Project; +use frontend\modules\api\models\project\ProjectStatistic; use frontend\modules\api\models\project\ProjectUser; use Yii; use yii\base\InvalidConfigException; @@ -501,4 +502,40 @@ class ProjectController extends ApiController return $project; } + + /** + * + * @OA\Get(path="/project/statistic", + * summary="Получить статистику проекта", + * description="Метод для получения статистики проета", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"TaskManager"}, + * @OA\Parameter( + * name="project_id", + * in="query", + * required=true, + * @OA\Schema( + * type="integer", + * default=null + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает объект статистики проекта", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/ProjectStatisticExample"), + * ), + * ), + * ) + * + * @param $project_id + * @return array|ActiveRecord|null + */ + public function actionStatistic($project_id): array|ActiveRecord|null + { + return ProjectStatistic::find()->where(['id' => $project_id])->one(); + } } \ No newline at end of file diff --git a/frontend/modules/api/models/project/ProjectParticipants.php b/frontend/modules/api/models/project/ProjectParticipants.php new file mode 100644 index 0000000..3fbe0ed --- /dev/null +++ b/frontend/modules/api/models/project/ProjectParticipants.php @@ -0,0 +1,142 @@ + function () { + return $this->user->userCard->photo ?? ''; + }, + 'username' => function () { + return $this->card->fio ?? null; + }, + 'email' => function () { + return $this->card->email ?? $this->user->email; + }, + 'role' => function () { + return $this->projectRole->title ?? null; + }, + 'status' => function () { + return $this->status ?? null; + }, + ]; + } +} \ No newline at end of file diff --git a/frontend/modules/api/models/project/ProjectStatistic.php b/frontend/modules/api/models/project/ProjectStatistic.php new file mode 100644 index 0000000..e91dd61 --- /dev/null +++ b/frontend/modules/api/models/project/ProjectStatistic.php @@ -0,0 +1,139 @@ + function () { + return $this->owner; + }, + 'open_tasks_count'=> function () { + return $this->getProjectTask()->where(['status' => ProjectTask::STATUS_ACTIVE])->count(); + }, + 'task_on_work_count'=> function () { + return $this->getProjectTask()->where(['status' => ProjectTask::STATUS_AT_WORK])->count(); + }, + 'closed_task_count'=> function () { + return $this->getProjectTask()->where(['status' => ProjectTask::STATUS_ARCHIVE])->count(); + }, + 'participants'=> function () { + return $this->participants; + }, + ]; + } + + /** + * @return string[] + */ + public function extraFields(): array + { + return []; + } + + /** + * @return ActiveQuery + */ + public function getOwner() + { + return $this->hasOne(ProjectParticipants::class, ['id' => 'owner_id']); + } + + /** + * @return ActiveQuery + */ + public function getProjectUsers() + { + return $this->hasMany(ProjectUser::class, ['project_id' => 'id']); + } + + public function getLinks(): array + { + return [ + Link::REL_SELF => Url::to(['index', 'project_id' => $this->id], true), + ]; + } + + public function getCompany(): ActiveQuery + { + return $this->hasOne(Company::class, ['id' => 'company_id']); + } + + public function getParticipants() + { + return $this->hasMany(ProjectParticipants::class, ['project_id' => 'id']); + } +} From 835147af37cfc5f490e49b901f47affdbf159e2d Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 22 Nov 2023 12:42:36 +0300 Subject: [PATCH 35/37] add resume update method --- .../api/controllers/ResumeController.php | 98 +++++++++++++++++++ .../models/resume/forms/ChangeResumeForm.php | 28 ++++++ .../models/resume/forms/SkillsResumeForm.php | 30 ++++++ .../modules/api/services/ResumeService.php | 64 ++++++++++++ frontend/modules/card/models/UserCard.php | 18 ++-- 5 files changed, 230 insertions(+), 8 deletions(-) create mode 100644 frontend/modules/api/models/resume/forms/ChangeResumeForm.php create mode 100644 frontend/modules/api/models/resume/forms/SkillsResumeForm.php create mode 100644 frontend/modules/api/services/ResumeService.php diff --git a/frontend/modules/api/controllers/ResumeController.php b/frontend/modules/api/controllers/ResumeController.php index 23ede9e..f9c9331 100644 --- a/frontend/modules/api/controllers/ResumeController.php +++ b/frontend/modules/api/controllers/ResumeController.php @@ -2,11 +2,26 @@ namespace frontend\modules\api\controllers; +use frontend\modules\api\models\resume\forms\ChangeResumeForm; use frontend\modules\api\models\resume\Resume; +use frontend\modules\api\services\ResumeService; use Yii; +use yii\web\BadRequestHttpException; class ResumeController extends ApiController { + public ResumeService $resumeService; + + public function __construct( + $id, + $module, + ResumeService $resumeService, + $config = [] + ) + { + $this->resumeService = $resumeService; + parent::__construct($id, $module, $config); + } /** * @OA\Get(path="/resume", @@ -42,4 +57,87 @@ class ResumeController extends ApiController { return Resume::findOne($userId ?? Yii::$app->user->identity->id); } + + /** + * + * @OA\Put(path="/resume/edit-skills", + * summary="Изменить скилы", + * description="Метод полностью удалит старые скилы и используя переданые id скилов запишет новые", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Resume"}, + * + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="json", + * @OA\Schema( + * required={"UserCard"}, + * @OA\Property( + * property="UserCard", + * type="object", + * @OA\Property( + * property="skill", + * type="array", + * @OA\Items( + * type="integer", + * example={1,2,3,4} + * ), + * ), + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * ), + * ) + * ) + * + * @return array|ChangeResumeForm + * @throws BadRequestHttpException + */ + public function actionEditSkills() + { + return $this->resumeService->editSkills(Yii::$app->request->post(), Yii::$app->user->identity->getId()); + } + + /** + * + * @OA\Put(path="/resume/edit-text", + * summary="Изменить резюме", + * description="Метод для изменения текста резюме", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Resume"}, + * + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/x-www-form-urlencoded", + * @OA\Schema( + * required={"resume"}, + * @OA\Property( + * property="resume", + * type="string", + * description="Текст резюме", + * ), + * ), + * ), + * ), + * @OA\Response( + * response=200, + * description="Возвращает сообщение об успехе", + * ), + * ) + * ) + * + * @return array|ChangeResumeForm + * @throws BadRequestHttpException + */ + public function actionEditText(): ChangeResumeForm|array + { + return $this->resumeService->editText(Yii::$app->request->post(), Yii::$app->user->identity->getId()); + } } diff --git a/frontend/modules/api/models/resume/forms/ChangeResumeForm.php b/frontend/modules/api/models/resume/forms/ChangeResumeForm.php new file mode 100644 index 0000000..06ef547 --- /dev/null +++ b/frontend/modules/api/models/resume/forms/ChangeResumeForm.php @@ -0,0 +1,28 @@ + ['integer']], + ['skill', 'each', 'rule' => ['in', 'range' => Skill::find()->select('id')->column()]] + ]; + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } +} diff --git a/frontend/modules/api/services/ResumeService.php b/frontend/modules/api/services/ResumeService.php new file mode 100644 index 0000000..a13b0e2 --- /dev/null +++ b/frontend/modules/api/services/ResumeService.php @@ -0,0 +1,64 @@ +load($params['UserCard']); + + if (!$model->validate()) { + return $model; + } + + $card = UserCard::findOne(['id_user' => $userId]); + + + if (!$card->save()) { + $errors = $card->getErrors(); + throw new BadRequestHttpException(array_shift($errors)[0]); + } + + return ['status' => 'success']; + + } + + /** + * @param array $params + * @param int $userId + * @return array|ChangeResumeForm + * @throws BadRequestHttpException + */ + public function editText(array $params, int $userId): array|ChangeResumeForm + { + $model = new ChangeResumeForm(); + $model->load($params); + + if (!$model->validate()) { + return $model; + } + + $card = UserCard::findOne(['id_user' => $userId]); + $card->vc_text = $model->resume; + + if (!$card->save()) { + $errors = $card->getErrors(); + throw new BadRequestHttpException(array_shift($errors)[0]); + } + + return ['status' => 'success']; + } +} \ No newline at end of file diff --git a/frontend/modules/card/models/UserCard.php b/frontend/modules/card/models/UserCard.php index fd7390e..fe605fa 100755 --- a/frontend/modules/card/models/UserCard.php +++ b/frontend/modules/card/models/UserCard.php @@ -32,17 +32,19 @@ class UserCard extends \common\models\UserCard public function afterSave($insert, $changedAttributes) { - $post = Yii::$app->request->post('UserCard'); + if (Yii::$app->request->post('UserCard')) { + $post = Yii::$app->request->post('UserCard'); - if ($post['skill']) { - CardSkill::deleteAll(['card_id' => $this->id]); + if ($post['skill']) { + CardSkill::deleteAll(['card_id' => $this->id]); - foreach ($post['skill'] as $item) { - $skill = new CardSkill(); - $skill->skill_id = $item; - $skill->card_id = $this->id; + foreach ($post['skill'] as $item) { + $skill = new CardSkill(); + $skill->skill_id = $item; + $skill->card_id = $this->id; - $skill->save(); + $skill->save(); + } } } From cd9f828f6047bd3d4686c7e043ff460b731a573c Mon Sep 17 00:00:00 2001 From: iIronside Date: Wed, 22 Nov 2023 16:39:51 +0300 Subject: [PATCH 36/37] update get-task-list method --- frontend/modules/api/controllers/TaskController.php | 13 +++++++++++-- frontend/modules/api/services/TaskService.php | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/frontend/modules/api/controllers/TaskController.php b/frontend/modules/api/controllers/TaskController.php index b559216..fe3df56 100644 --- a/frontend/modules/api/controllers/TaskController.php +++ b/frontend/modules/api/controllers/TaskController.php @@ -147,6 +147,15 @@ class TaskController extends ApiController * ) * ), * @OA\Parameter( + * name="user_id", + * description="При передаче этого параметера вернёт все задачи на проекте для пользователя с заданным id", + * in="query", + * required=false, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Parameter( * name="expand", * in="query", * example="column,timers,mark", @@ -167,9 +176,9 @@ class TaskController extends ApiController * * @throws NotFoundHttpException */ - public function actionGetTaskList($project_id): array + public function actionGetTaskList($project_id, $user_id = null): array { - $tasks = $this->taskService->getTaskListByProject($project_id); + $tasks = $this->taskService->getTaskListByProject($project_id, $user_id); if (empty($tasks)) { throw new NotFoundHttpException('The project does not exist or there are no tasks for it'); diff --git a/frontend/modules/api/services/TaskService.php b/frontend/modules/api/services/TaskService.php index 809a42e..67dcdbc 100644 --- a/frontend/modules/api/services/TaskService.php +++ b/frontend/modules/api/services/TaskService.php @@ -35,9 +35,14 @@ class TaskService return ProjectTask::find()->asArray()->all(); } - public function getTaskListByProject($project_id): array + public function getTaskListByProject($project_id, $user_id): array { - return ProjectTask::find()->where(['project_id' => $project_id])->orderBy('priority DESC')->all(); + $query = ProjectTask::find()->where(['project_id' => $project_id]); + + if ($user_id) { + $query->andWhere(['user_id' => $user_id]); + } + return $query->orderBy('priority DESC')->all(); } public function getArchiveTask($project_id, $user_id): array From 932ea915ded01006d8110a0950ec5dd91ae18f64 Mon Sep 17 00:00:00 2001 From: iIronside Date: Fri, 24 Nov 2023 16:07:12 +0300 Subject: [PATCH 37/37] add api/company/get-personal --- common/models/ProjectTask.php | 8 ++ common/models/TestTask.php | 2 - .../api/controllers/CompanyController.php | 57 ++++++++++ .../api/models/{ => company}/Company.php | 2 +- .../company/dto/CompanyPersonnelDto.php | 106 ++++++++++++++++++ .../api/models/company/form/CompanyIdForm.php | 30 +++++ .../mappers/CompanyPersonnelMapper.php | 41 +++++++ .../modules/api/models/project/Project.php | 3 +- .../api/models/project/ProjectStatistic.php | 2 +- .../modules/api/services/PersonnelService.php | 35 ++++++ frontend/modules/api/services/TaskService.php | 51 +++++++++ 11 files changed, 331 insertions(+), 6 deletions(-) create mode 100644 frontend/modules/api/controllers/CompanyController.php rename frontend/modules/api/models/{ => company}/Company.php (96%) create mode 100644 frontend/modules/api/models/company/dto/CompanyPersonnelDto.php create mode 100644 frontend/modules/api/models/company/form/CompanyIdForm.php create mode 100644 frontend/modules/api/models/company/mappers/CompanyPersonnelMapper.php create mode 100644 frontend/modules/api/services/PersonnelService.php diff --git a/common/models/ProjectTask.php b/common/models/ProjectTask.php index 991a24c..2d40c5f 100644 --- a/common/models/ProjectTask.php +++ b/common/models/ProjectTask.php @@ -61,6 +61,14 @@ class ProjectTask extends ActiveRecord ]; } + /** + * @return int[] + */ + public static function openTaskStatusList(): array + { + return [self::STATUS_ACTIVE, self::STATUS_AT_WORK]; + } + /** * @param $priority * @return string diff --git a/common/models/TestTask.php b/common/models/TestTask.php index d931c98..59f9d07 100644 --- a/common/models/TestTask.php +++ b/common/models/TestTask.php @@ -2,8 +2,6 @@ namespace common\models; -use Yii; - /** * This is the model class for table "test_task". * diff --git a/frontend/modules/api/controllers/CompanyController.php b/frontend/modules/api/controllers/CompanyController.php new file mode 100644 index 0000000..60c51f5 --- /dev/null +++ b/frontend/modules/api/controllers/CompanyController.php @@ -0,0 +1,57 @@ +personnelService = $personnelService; + parent::__construct($id, $module, $config); + } + + /** + * @OA\Get(path="/company/get-personal", + * summary="Персонал компании", + * description="Метод для получения персонала компании", + * security={ + * {"bearerAuth": {}} + * }, + * tags={"Company"}, + * @OA\Parameter( + * name="company_id", + * description="ID компании", + * in="query", + * required=true, + * @OA\Schema( + * type="integer", + * ) + * ), + * @OA\Response( + * response=200, + * description="Возвращает масив объектов сотрудников", + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema(ref="#/components/schemas/CompanyPersonnelDtoExampleArr"), + * ), + * ), + * ) + * + * @return CompanyIdForm|array + */ + public function actionGetPersonal(): CompanyIdForm|array + { + return $this->personnelService->getPersonnel(Yii::$app->request->get()); + } +} \ No newline at end of file diff --git a/frontend/modules/api/models/Company.php b/frontend/modules/api/models/company/Company.php similarity index 96% rename from frontend/modules/api/models/Company.php rename to frontend/modules/api/models/company/Company.php index a3ea7ff..bb6191f 100644 --- a/frontend/modules/api/models/Company.php +++ b/frontend/modules/api/models/company/Company.php @@ -1,6 +1,6 @@ false, 'targetClass' => Company::class, 'targetAttribute' => ['company_id' => 'id']], + ]; + } + + /** + * @return string + */ + public function formName(): string + { + return ''; + } +} diff --git a/frontend/modules/api/models/company/mappers/CompanyPersonnelMapper.php b/frontend/modules/api/models/company/mappers/CompanyPersonnelMapper.php new file mode 100644 index 0000000..3389074 --- /dev/null +++ b/frontend/modules/api/models/company/mappers/CompanyPersonnelMapper.php @@ -0,0 +1,41 @@ +userId = $projectUser->user_id; + $dto->fio = $projectUser->card->fio ?? null; + $dto->position = $projectUser->card->position->name ?? null; + $dto->level = $projectUser->card->level ?? null; + $dto->projectName = $projectUser->project->name; + $dto->openTaskCount = TaskService::getOpenTaskCount($projectUser->user_id, $projectUser->project_id); + $dto->hoursWorkedForCurrentMonth = TaskService::getHoursWorkedForCurrentMonth($projectUser->user_id, $projectUser->project_id); + + return $dto; + } + + /** + * @param array $projectUsers + * @return array + */ + public static function mapAll(array $projectUsers): array + { + return array_map(function (ProjectUser $projectUser) { + return self::map($projectUser); + }, array_values($projectUsers)); + } +} diff --git a/frontend/modules/api/models/project/Project.php b/frontend/modules/api/models/project/Project.php index 9e85130..f7c352d 100644 --- a/frontend/modules/api/models/project/Project.php +++ b/frontend/modules/api/models/project/Project.php @@ -2,11 +2,10 @@ namespace frontend\modules\api\models\project; -use frontend\modules\api\models\Company; +use frontend\modules\api\models\company\Company; use yii\db\ActiveQuery; use yii\helpers\Url; use yii\web\Link; -use yii\web\Linkable; /** * diff --git a/frontend/modules/api/models/project/ProjectStatistic.php b/frontend/modules/api/models/project/ProjectStatistic.php index e91dd61..4a5ed2e 100644 --- a/frontend/modules/api/models/project/ProjectStatistic.php +++ b/frontend/modules/api/models/project/ProjectStatistic.php @@ -2,7 +2,7 @@ namespace frontend\modules\api\models\project; -use frontend\modules\api\models\Company; +use frontend\modules\api\models\company\Company; use yii\db\ActiveQuery; use yii\helpers\Url; use yii\web\Link; diff --git a/frontend/modules/api/services/PersonnelService.php b/frontend/modules/api/services/PersonnelService.php new file mode 100644 index 0000000..c4297b5 --- /dev/null +++ b/frontend/modules/api/services/PersonnelService.php @@ -0,0 +1,35 @@ +load($params); + + if (!$form->validate()){ + return $form; + } + + $projects = Project::find()->where(['company_id' => $form->company_id])->all(); + + $personals = []; + + /** @var Project $project */ + foreach ($projects as $project) { + $personals += CompanyPersonnelMapper::mapAll($project->projectUsers); + } + + return $personals; + } +} \ No newline at end of file diff --git a/frontend/modules/api/services/TaskService.php b/frontend/modules/api/services/TaskService.php index 67dcdbc..28af3e5 100644 --- a/frontend/modules/api/services/TaskService.php +++ b/frontend/modules/api/services/TaskService.php @@ -2,10 +2,13 @@ namespace frontend\modules\api\services; +use common\models\Entity; use common\models\forms\TasksImportForm; use common\services\ImportProjectTaskService; +use DateTime; use frontend\modules\api\models\project\ProjectTask; use frontend\modules\api\models\project\ProjectTaskUser; +use frontend\modules\api\models\Timer; use yii\web\BadRequestHttpException; class TaskService @@ -17,6 +20,54 @@ class TaskService $this->importProjectTaskService = $importProjectTaskService; } + public static function getOpenTaskCount(int $user_id, int $project_id): bool|int|string|null + { + return ProjectTask::find() + ->where(['user_id' => $user_id]) + ->andWhere(['project_id' => $project_id]) + ->andWhere(['in', 'status', ProjectTask::openTaskStatusList()]) + ->count(); + } + + public static function getHoursWorkedForCurrentMonth(int $user_id, int $project_id) + { + $projectTaskIdArr = ProjectTask::find() + ->select('id') + ->where(['user_id' => $user_id]) + ->andWhere(['project_id' => $project_id]) + ->column(); + + $firstMonthDay = new DateTime('first day of this month'); + $firstMonthDay->setTime(00, 00, 01); + $firstMonthDay = $firstMonthDay->format('Y-m-d H:i:s'); + + + $lastMonthDay = new DateTime('last day of this month'); + $lastMonthDay->setTime(23, 59, 00); + $lastMonthDay = $lastMonthDay->format('Y-m-d H:i:s'); + + $timers = Timer::find() + ->where(['user_id' => $user_id]) + ->andWhere(['entity_type' => Entity::ENTITY_TYPE_TASK]) + ->andWhere(['in', 'entity_id', $projectTaskIdArr]) + ->andWhere(['between', 'created_at', $firstMonthDay, $lastMonthDay ]) + ->all(); + + $hours = 0; + /** @var Timer $timer */ + foreach ($timers as $timer) { + // Create two new DateTime-objects... + $date1 = new DateTime($timer->created_at); + $date2 = new DateTime($timer->stopped_at); + + $diff = $date2->diff($date1); + + $hours += $diff->h + ($diff->days*24); + + } + return $hours; + } + public function createTask($taskParams) { $task = new ProjectTask();