diff --git a/backend/config/main.php b/backend/config/main.php index f87721d..454bbf5 100644 --- a/backend/config/main.php +++ b/backend/config/main.php @@ -10,6 +10,7 @@ return [ 'id' => 'app-backend', 'basePath' => dirname(__DIR__), 'controllerNamespace' => 'backend\controllers', + 'language' => 'ru', 'bootstrap' => ['log'], 'modules' => [ @@ -25,6 +26,9 @@ return [ 'company' => [ 'class' => 'backend\modules\company\Company', ], + 'hh' => [ + 'class' => 'backend\modules\hh\Hh', + ], ], 'components' => [ 'request' => [ diff --git a/backend/modules/hh/Hh.php b/backend/modules/hh/Hh.php new file mode 100644 index 0000000..d9ef497 --- /dev/null +++ b/backend/modules/hh/Hh.php @@ -0,0 +1,24 @@ +render('index'); + } +} diff --git a/backend/modules/hh/controllers/HhController.php b/backend/modules/hh/controllers/HhController.php new file mode 100644 index 0000000..19faeec --- /dev/null +++ b/backend/modules/hh/controllers/HhController.php @@ -0,0 +1,154 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ]; + } + + /** + * Lists all Hh models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new HhSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single Hh 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 Hh model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Hh(); + + if ($model->load(Yii::$app->request->post())) { + $hhId = explode('/', $model->url); + $model->hh_id = $hhId[4]; + $model->dt_add = time(); + //$jobs = HHService::run()->company($model->hh_id)->getJobs(); + $company = HHService::run()->company($model->hh_id)->get(); + if (isset($company->name)) { + $model->title = $company->name; + $model->photo = $company->logo_urls->{240}; + } + $model->save(); + return $this->redirect(['view', 'id' => $model->id]); + } + + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Hh 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 Hh 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']); + } + + public function actionGetJobs($id) + { + $model = \common\models\Hh::findOne($id); + $jobs = HHService::run()->company($model->hh_id)->getJobs(); + $count = 0; + foreach ((array)$jobs as $job) { + if(HhJob::createFromHH($job)){ + $count++; + } + } + Yii::$app->session->setFlash('success', "Получено $count новых вакансий"); + return $this->redirect(['/hh/hh']); + } + + /** + * Finds the Hh model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Hh the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Hh::findOne($id)) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested page does not exist.'); + } +} diff --git a/backend/modules/hh/controllers/HhJobController.php b/backend/modules/hh/controllers/HhJobController.php new file mode 100644 index 0000000..be5cf6e --- /dev/null +++ b/backend/modules/hh/controllers/HhJobController.php @@ -0,0 +1,131 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => ['POST'], + ], + ], + ]; + } + + /** + * Lists all HhJob models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new HhJobSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + $hhCompanies = ArrayHelper::map(Hh::find()->all(), 'hh_id', 'title'); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + 'hhCompanies' => $hhCompanies, + ]); + } + + /** + * Displays a single HhJob 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 HhJob model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new HhJob(); + + 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 HhJob 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 HhJob 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 HhJob model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return HhJob the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = HhJob::findOne($id)) !== null) { + return $model; + } + + throw new NotFoundHttpException('The requested page does not exist.'); + } +} diff --git a/backend/modules/hh/models/Hh.php b/backend/modules/hh/models/Hh.php new file mode 100644 index 0000000..d4ec818 --- /dev/null +++ b/backend/modules/hh/models/Hh.php @@ -0,0 +1,15 @@ +joinWith('hhcompany'); + + // add conditions that should always apply here + + $dataProvider = new ActiveDataProvider([ + 'query' => $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, + 'employer_id' => $this->employer_id, + 'hh_id' => $this->hh_id, + 'salary_from' => $this->salary_from, + 'salary_to' => $this->salary_to, + 'dt_add' => $this->dt_add, + ]); + + $query->andFilterWhere(['like', 'title', $this->title]) + ->andFilterWhere(['like', 'url', $this->url]) + ->andFilterWhere(['like', 'salary_currency', $this->salary_currency]) + ->andFilterWhere(['like', 'address', $this->address]); + + return $dataProvider; + } +} diff --git a/backend/modules/hh/models/HhSearch.php b/backend/modules/hh/models/HhSearch.php new file mode 100644 index 0000000..cac4970 --- /dev/null +++ b/backend/modules/hh/models/HhSearch.php @@ -0,0 +1,73 @@ + $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, + 'hh_id' => $this->hh_id, + 'dt_add' => $this->dt_add, + ]); + + $query->andFilterWhere(['like', 'url', $this->url]) + ->andFilterWhere(['like', 'title', $this->title]) + ->andFilterWhere(['like', 'photo', $this->photo]); + + return $dataProvider; + } +} diff --git a/backend/modules/hh/views/default/index.php b/backend/modules/hh/views/default/index.php new file mode 100644 index 0000000..ba855d9 --- /dev/null +++ b/backend/modules/hh/views/default/index.php @@ -0,0 +1,12 @@ +
+ This is the view content for action "= $this->context->action->id ?>". + The action belongs to the controller "= get_class($this->context) ?>" + in the "= $this->context->module->id ?>" module. +
+
+ You may customize this page by editing the following file:
+ = __FILE__ ?>
+
+ = Html::a('Update', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?> + = Html::a('Delete', ['delete', 'id' => $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Are you sure you want to delete this item?', + 'method' => 'post', + ], + ]) ?> +
+ + = DetailView::widget([ + 'model' => $model, + 'attributes' => [ + 'id', + 'employer_id', + 'hh_id', + 'title', + 'url:url', + 'salary_from', + 'salary_to', + 'salary_currency', + 'address', + 'dt_add', + ], + ]) ?> + ++ = Html::a('Добавить', ['create'], ['class' => 'btn btn-success']) ?> +
+ + = GridView::widget([ + 'dataProvider' => $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + ['class' => 'yii\grid\SerialColumn'], + + //'id', + //'hh_id', + 'url:url', + 'title', + 'dt_add:date', + 'photo:image', + [ + 'class' => 'yii\grid\DataColumn', + 'format' => 'raw', + 'value' => function ($model) { + return Html::a('Получить вакансии', \yii\helpers\Url::to([ + '/hh/hh/get-jobs', + 'id' => $model->id + ]), [ + 'class' => 'btn btn-success' + ]); + }, + ], + + [ + 'class' => 'yii\grid\ActionColumn', + 'template' => '{view} {delete}' + ], + ], + ]); ?> ++ = Html::a('Список', ['index'], ['class' => 'btn btn-primary']) ?> + = Html::a('Редактировать', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?> + = Html::a('Удалить', ['delete', 'id' => $model->id], [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Are you sure you want to delete this item?', + 'method' => 'post', + ], + ]) ?> +
+ + = DetailView::widget([ + 'model' => $model, + 'attributes' => [ + 'title', + 'hh_id', + 'url:url', + 'dt_add:date', + 'photo:image', + ], + ]) ?> + ++ = Html::a('Список', ['index'], ['class' => 'btn btn-primary']) ?> = Html::a('Редактировать', ['update', 'id' => $model->id], ['class' => 'btn btn-primary']) ?> = Html::a('Удалить', ['delete', 'id' => $model->id], [ 'class' => 'btn btn-danger', diff --git a/backend/views/layouts/left.php b/backend/views/layouts/left.php index d1839c3..c3a5b5b 100644 --- a/backend/views/layouts/left.php +++ b/backend/views/layouts/left.php @@ -18,6 +18,13 @@ ['label' => 'Профили', 'icon' => 'users', 'url' => ['/card/user-card']], ['label' => 'Пректы', 'icon' => 'files-o', 'url' => ['/project/project']], ['label' => 'Компании', 'icon' => 'files-o', 'url' => ['/company/company']], + [ + 'label' => 'Hh.ru', 'icon' => 'user-circle', 'url' => '#', + 'items' => [ + ['label' => 'Компании', 'icon' => 'building', 'url' => ['/hh/hh']], + ['label' => 'Вакансии', 'icon' => 'user-md', 'url' => ['/hh/hh-job']], + ], + ] /*['label' => 'Gii', 'icon' => 'file-code-o', 'url' => ['/gii']], ['label' => 'Debug', 'icon' => 'dashboard', 'url' => ['/debug']], diff --git a/common/hhapi/.gitignore b/common/hhapi/.gitignore new file mode 100644 index 0000000..ab22a7c --- /dev/null +++ b/common/hhapi/.gitignore @@ -0,0 +1,3 @@ +.idea +init.php +/vendor/ diff --git a/common/hhapi/composer.json b/common/hhapi/composer.json new file mode 100644 index 0000000..4f6a69a --- /dev/null +++ b/common/hhapi/composer.json @@ -0,0 +1,15 @@ +{ + "name": "kavalar/hhapi", + "type": "library", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Kavalar", + "email": "apuc06@mail.ru" + } + ], + "minimum-stability": "dev", + "require": { + "php": ">=7.0.0" + } +} diff --git a/common/hhapi/core/lib/Company.php b/common/hhapi/core/lib/Company.php new file mode 100644 index 0000000..5d7d443 --- /dev/null +++ b/common/hhapi/core/lib/Company.php @@ -0,0 +1,53 @@ +id = $id; + $company = $this->baseRequest('employers/' . $id)->get(); + $this->company = $company; + } + } + + public function getName() + { + return $this->company->name; + } + + public function getDescription() + { + return $this->company->description; + } + + public function getJobs() + { + $j = $this->baseRequest('vacancies')->addParams(['employer_id' => $this->id])->get(); + if($j){ + foreach ($j->items as $item){ + $this->jobs[] = new Vacancy($item); + } + } + return $this->jobs; + } + +} \ No newline at end of file diff --git a/common/hhapi/core/lib/Vacancy.php b/common/hhapi/core/lib/Vacancy.php new file mode 100644 index 0000000..1a43707 --- /dev/null +++ b/common/hhapi/core/lib/Vacancy.php @@ -0,0 +1,44 @@ +baseRequest('vacancies/' . $data)->get(); + $this->item = $item; + } + else { + $this->item = $data; + } + } + + public function getName() + { + return ($this->item) ? $this->item->name : null; + } + + public static function search($params) + { + $v = new self(); + return $v->baseRequest('vacancies')->addParams($params)->get(); + } + + + +} \ No newline at end of file diff --git a/common/hhapi/core/request/Request.php b/common/hhapi/core/request/Request.php new file mode 100644 index 0000000..5f99155 --- /dev/null +++ b/common/hhapi/core/request/Request.php @@ -0,0 +1,84 @@ +getPostParams()); + } + $res = curl_exec($curl); + curl_close($curl); + $res = json_decode($res); +// if ($res->http_code == '404') { +// throw new Exception('User not found!', 404); +// } +// if ($res->http_code == '403') { +// throw new Exception('Bad token!', 403); +// } + return $res; + } + + public function baseRequest($path) + { + $this->url = 'https://api.hh.ru/' . $path; + return $this; + } + + public function addParams($params) + { + if ($params) { + $this->params = $params; + $i = 0; + foreach ((array)$params as $key => $param) { + $s = ($i === 0) ? '?' : '&'; + $this->url .= $s . $key . '=' . $param; + $i++; + } + } + return $this; + } + + private function getPostParams() + { + $params = ''; + if ($this->params) { + foreach ((array)$this->params as $key => $param) { + $params .= $key . '=' . $param . '&'; + } + $params = mb_substr($params, 0, -1); + } + return $params; + } + + public function get() + { + return $this->parseUrl($this->url); + } + + public function post() + { + return $this->parseUrl($this->url, 'post'); + } + + +} \ No newline at end of file diff --git a/common/hhapi/core/service/HHService.php b/common/hhapi/core/service/HHService.php new file mode 100644 index 0000000..a6e181f --- /dev/null +++ b/common/hhapi/core/service/HHService.php @@ -0,0 +1,44 @@ +'; +print_r(\core\service\HHService::run()->company('2495437')->getJobs()); +echo ''; \ No newline at end of file diff --git a/common/models/Hh.php b/common/models/Hh.php new file mode 100644 index 0000000..0aa904f --- /dev/null +++ b/common/models/Hh.php @@ -0,0 +1,53 @@ + 255], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'hh_id' => 'Hh ID', + 'url' => 'Url', + 'title' => 'Название', + 'dt_add' => 'Дата добавления', + 'photo' => 'Фото', + ]; + } +} diff --git a/common/models/HhJob.php b/common/models/HhJob.php new file mode 100644 index 0000000..82650c5 --- /dev/null +++ b/common/models/HhJob.php @@ -0,0 +1,88 @@ + 255], + [['salary_currency'], 'string', 'max' => 100], + ]; + } + + /** + * {@inheritdoc} + */ + public function attributeLabels() + { + return [ + 'id' => 'ID', + 'employer_id' => 'Работодатель', + 'hh_id' => 'Hh ID', + 'title' => 'Заголовок', + 'url' => 'Url', + 'salary_from' => 'З.П. от', + 'salary_to' => 'З.П. до', + 'salary_currency' => 'З.П. валюта', + 'address' => 'Адрес', + 'dt_add' => 'Дата', + ]; + } + + public function gethhcompany() + { + return $this->hasOne(Hh::class, ['hh_id' => 'employer_id']); + } + + public static function createFromHH($data) + { + if (!self::find()->where(['hh_id' => $data->item->id])->exists()) { + $j = new self(); + $j->dt_add = time(); + $j->title = $data->item->name; + $j->hh_id = $data->item->id; + $j->url = $data->item->alternate_url; + $j->employer_id = $data->item->employer->id; + if (!empty($data->item->address)) { + $j->address = $data->item->address->city . ', ' . $data->item->address->street . ', ' . $data->item->address->building; + } + if (!empty($data->item->salary)) { + $j->salary_from = $data->item->salary->from; + $j->salary_to = $data->item->salary->to; + $j->salary_currency = $data->item->salary->currency; + } + return $j->save(); + } + return false; + } +} diff --git a/common/models/Project.php b/common/models/Project.php index 68eeca1..fcbc3df 100644 --- a/common/models/Project.php +++ b/common/models/Project.php @@ -16,6 +16,7 @@ use yii\db\Expression; * @property string $updated_at * @property string $budget * @property int $company_id + * @property int $hh_id * * @property FieldsValue[] $fieldsValues * @property Company $company @@ -55,6 +56,7 @@ class Project extends \yii\db\ActiveRecord [['name'], 'string', 'max' => 255], [['budget'], 'string', 'max' => 100], [['company_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::class, 'targetAttribute' => ['company_id' => 'id']], + [['hh_id'], 'exist', 'skipOnError' => true, 'targetClass' => Hh::class, 'targetAttribute' => ['hh_id' => 'id']], ]; } @@ -71,6 +73,7 @@ class Project extends \yii\db\ActiveRecord 'updated_at' => 'Дата редактирования', 'budget' => 'Бюджет', 'company_id' => 'Компания', + 'hh_id' => 'Проект на hh.ru', ]; } @@ -90,6 +93,14 @@ class Project extends \yii\db\ActiveRecord return $this->hasOne(Company::class, ['id' => 'company_id']); } + /** + * @return \yii\db\ActiveQuery + */ + public function getHh() + { + return $this->hasOne(Hh::class, ['id' => 'hh_id']); + } + /** * @return \yii\db\ActiveQuery */ diff --git a/composer.lock b/composer.lock index 5c065fa..8824874 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a85750ca57985e0961df5763c3927a1a", + "content-hash": "690fb4ff58b0c920877e5f27a7dd6165", "packages": [ { "name": "almasaeed2010/adminlte", @@ -99,7 +99,7 @@ "version": "3.2.1", "source": { "type": "git", - "url": "https://github.com/jquery/jquery-dist.git", + "url": "git@github.com:jquery/jquery-dist.git", "reference": "77d2a51d0520d2ee44173afdf4e40a9201f5964e" }, "dist": { @@ -766,6 +766,36 @@ ], "time": "2018-10-03T07:09:03+00:00" }, + { + "name": "kavalar/hhapi", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/apuc/hhapi.git", + "reference": "8e56d339b377155f559e8eb8d893b14541f9a7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/apuc/hhapi/zipball/8e56d339b377155f559e8eb8d893b14541f9a7ed", + "reference": "8e56d339b377155f559e8eb8d893b14541f9a7ed", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kavalar", + "email": "apuc06@mail.ru" + } + ], + "time": "2018-11-19T08:22:43+00:00" + }, { "name": "mihaildev/yii2-elfinder", "version": "1.3.0", @@ -3910,7 +3940,8 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "kartik-v/yii2-widget-select2": 20 + "kartik-v/yii2-widget-select2": 20, + "kavalar/hhapi": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/console/migrations/m181121_103329_create_hh_table.php b/console/migrations/m181121_103329_create_hh_table.php new file mode 100644 index 0000000..4d414ab --- /dev/null +++ b/console/migrations/m181121_103329_create_hh_table.php @@ -0,0 +1,32 @@ +createTable('hh', [ + 'id' => $this->primaryKey(), + 'hh_id' => $this->integer(11), + 'url' => $this->string(255)->notNull(), + 'title' => $this->string(255), + 'dt_add' => $this->integer(11), + 'photo' => $this->string(255) + ]); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('hh'); + } +} diff --git a/console/migrations/m181121_112940_create_hh_job_table.php b/console/migrations/m181121_112940_create_hh_job_table.php new file mode 100644 index 0000000..962603e --- /dev/null +++ b/console/migrations/m181121_112940_create_hh_job_table.php @@ -0,0 +1,36 @@ +createTable('hh_job', [ + 'id' => $this->primaryKey(), + 'employer_id' => $this->integer(11), + 'hh_id' => $this->integer(11), + 'title' => $this->string(255), + 'url' => $this->string(255), + 'salary_from' => $this->integer(11), + 'salary_to' => $this->integer(11), + 'salary_currency' => $this->string(100), + 'address' => $this->string(255), + 'dt_add' => $this->integer(11) + ]); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropTable('hh_job'); + } +} diff --git a/console/migrations/m181121_135329_add_hh_id_column_to_project_table.php b/console/migrations/m181121_135329_add_hh_id_column_to_project_table.php new file mode 100644 index 0000000..27967e0 --- /dev/null +++ b/console/migrations/m181121_135329_add_hh_id_column_to_project_table.php @@ -0,0 +1,25 @@ +addColumn('project', 'hh_id', $this->integer(11)); + } + + /** + * {@inheritdoc} + */ + public function safeDown() + { + $this->dropColumn('project', 'hh_id'); + } +} diff --git a/frontend/config/main.php b/frontend/config/main.php index 3c07d6b..ea12607 100644 --- a/frontend/config/main.php +++ b/frontend/config/main.php @@ -36,14 +36,14 @@ return [ 'errorHandler' => [ 'errorAction' => 'site/error', ], - /* + 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ ], ], - */ + ], 'params' => $params, ];