Skip to content

Commit

Permalink
Merge pull request #23 from PRTIMES/feature/add-recommend
Browse files Browse the repository at this point in the history
レコメンド機能の作成(フェーズ3)
  • Loading branch information
kyoya0819 authored Aug 29, 2024
2 parents f268d68 + 9a3aeec commit 71ea8a0
Show file tree
Hide file tree
Showing 19 changed files with 442 additions and 19 deletions.
4 changes: 4 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

PRTIMES_API_TOKEN=""

OPENAI_API_TOKEN=""
37 changes: 37 additions & 0 deletions backend/_ide_helper_models.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
* @property string $keyword
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\PressRelease> $pressReleases
* @property-read int|null $press_releases_count
* @method static \Illuminate\Database\Eloquent\Builder|Keyword newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Keyword newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Keyword query()
Expand All @@ -37,15 +39,23 @@ class Keyword extends \Eloquent {}
* @property int $id
* @property string $company_id
* @property string $release_id
* @property string $title
* @property string $summary
* @property string $release_created_at
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Keyword> $keywords
* @property-read int|null $keywords_count
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease query()
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereCompanyId($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereReleaseCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereReleaseId($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereSummary($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereTitle($value)
* @method static \Illuminate\Database\Eloquent\Builder|PressRelease whereUpdatedAt($value)
*/
class PressRelease extends \Eloquent {}
Expand All @@ -66,6 +76,8 @@ class PressRelease extends \Eloquent {}
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection<int, \Illuminate\Notifications\DatabaseNotification> $notifications
* @property-read int|null $notifications_count
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\ViewHistory> $view_histories
* @property-read int|null $view_histories_count
* @method static \Database\Factories\UserFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
Expand All @@ -83,3 +95,28 @@ class PressRelease extends \Eloquent {}
class User extends \Eloquent {}
}

namespace App\Models{
/**
*
*
* @property int $id
* @property int $score
* @property int $user_id
* @property int $keyword_id
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\Models\Keyword $keyword
* @property-read \App\Models\User $user
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory query()
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereKeywordId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereScore($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ViewHistory whereUserId($value)
*/
class ViewHistory extends \Eloquent {}
}

59 changes: 59 additions & 0 deletions backend/app/Http/Controllers/Api/PressRelease/ViewController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\Http\Controllers\Api\PressRelease;

use App\Exceptions\HttpJsonResponseException;
use App\Http\Controllers\Controller;
use App\Models\Keyword;
use App\Models\User;
use App\UseCases\User\FindByIdAction as UserFindByIdAction;
use App\UseCases\PressRelease\FindByIdsAction;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ViewController extends Controller
{
/**
* @param Request $request
* @return void
* @throws Exception
*/
public function __invoke(Request $request)
{
/* @var User $user */
$user = UserFindByIdAction::run(Auth::id());
if ($user === null) // @todo まともにExceptionする
throw new Exception("test");

$company_id = $request->get("company_id");
$release_id = $request->get("release_id");

$press_release = FindByIdsAction::run(company_id: $company_id, release_id: $release_id);

if (!$press_release)
throw new HttpJsonResponseException(
400,
"notfound",
"PressRelease Not Found",
compact("company_id", "release_id")
);

/* @var Keyword $keyword */
foreach ($press_release->keywords() as $keyword) {


$user->view_histories()->create([

]);




}
$press_release->keywords();


$press_release->keywords();
}
}
123 changes: 123 additions & 0 deletions backend/app/Jobs/PressReleaseAnalyze.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

namespace App\Jobs;

use App\Libs\Bedrock;
use App\Libs\OpenAI;
use App\UseCases\Keyword\UpdateOrCreateAction;
use App\UseCases\PressRelease\CreateAction as PressReleaseCreateAction;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use GuzzleHttp\Client as GuzzleClient;

class PressReleaseAnalyze implements ShouldQueue
{
use Queueable;

const EXACT_MATCH_WEIGHT = 2;

/**
* @param int $company_id
* @param int $release_id
*/
public function __construct(
private readonly int $company_id,
private readonly int $release_id
)
{}

/**
* @return void
* @throws GuzzleException
* @throws Exception
*/
public function handle(): void
{
$headers = [
"Authorization" => "Bearer " . config("services.prtimes.token")
];

$guzzle = new GuzzleClient([
"base_uri" => "https://hackathon.stg-prtimes.net/"
]);

$openai = new OpenAI(config("services.openai.token"));

$bedrock = new Bedrock();

$response = $guzzle->get("/api/companies/112/releases/1078", ["headers" => $headers]);

if ($response->getStatusCode() !== 200)
throw new Exception("PR TIMES API Error");

/* @var array{
* body: string,
* company_id: int,
* company_name: string,
* created_at: string,
* lead_paragraph: string,
* like: int,
* main_category_id: int,
* main_category_name: string,
* main_image: string,
* main_image_fastly: string,
* release_id: int,
* release_type: string,
* sub_category_id: string,
* sub_category_name: string,
* subtitle: string,
* title: string,
* url: string
* } $data
*/
$data = json_decode($response->getBody()->getContents(), true);

$body = strip_tags($data["body"]);

$summary = $openai->answer(
question: $body,
background: "あなたは、プレスリリースの内容を元にして、若者に興味を持ってもらえる文章を生成するエージェントです。\n
以下の条件を満たしてください。\n
1. 生成した文章は100文字以内\n
2. 絵文字は用いない\n
3. 内容に基づく。感想を含めない。"
);

$press_release = PressReleaseCreateAction::run(
company_id: $this->company_id,
release_id: $this->release_id,
title: $data["title"],
summary: $summary,
release_created_at: $data["created_at"]
);

$keywords_string = $bedrock->answer(
question: "以下の文章から重要なキーワードを抜き出せ \n
指示 \n
名詞や固有名詞のみ出力すること \n
年月日は重要ではないので出力しないこと \n
以下のようなJSONの配列形式で出力すること \n
[\"キーワード\", \"キーワード\", ...] \n
JSONデータ以外一切出力しないこと \n\n" . $body
);
$keywords = json_decode($keywords_string) ?? [];

foreach ($keywords as $keyword) {

$keyword = UpdateOrCreateAction::run($keyword);

$press_release->keywords()
->syncWithPivotValues(
$keyword->id,
[
"weight" => self::EXACT_MATCH_WEIGHT
],
false
);
}

// @todo さらに形態素解析をかまして、低い重みでキーワードを登録する。
}
}
2 changes: 1 addition & 1 deletion backend/app/Libs/Bedrock.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Bedrock

public function __construct(
private readonly string $model_id = "anthropic.claude-3-haiku-20240307-v1:0",
private readonly string $anthropic_version = "anthropic.claude-3-haiku-20240307-v1:0",
private readonly string $anthropic_version = "bedrock-2023-05-31",
private readonly int $max_token = 1000,
array $client_configs = [
"region" => "us-east-1",
Expand Down
6 changes: 3 additions & 3 deletions backend/app/Models/Keyword.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ class Keyword extends Model
use HasFactory;

protected $fillable = [
"keyword",
"weight"
"keyword"
];

/**
* @return BelongsToMany
*/
public function pressReleases(): BelongsToMany
{
return $this->belongsToMany(PressRelease::class);
return $this->belongsToMany(PressRelease::class)
->withPivot("weight");
}
}
3 changes: 2 additions & 1 deletion backend/app/Models/PressRelease.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class PressRelease extends Model
*/
public function keywords(): BelongsToMany
{
return $this->belongsToMany(Keyword::class);
return $this->belongsToMany(Keyword::class)
->withPivot("weight");
}
}
10 changes: 9 additions & 1 deletion backend/app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

Expand All @@ -24,4 +24,12 @@ class User extends Authenticatable
'age',
'prefecture'
];

/**
* @return HasMany
*/
public function view_histories(): HasMany
{
return $this->hasMany(ViewHistory::class);
}
}
33 changes: 33 additions & 0 deletions backend/app/Models/ViewHistory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class ViewHistory extends Model
{
use HasFactory;

protected $fillable = [
"score",
"user_id", "keyword_id"
];

/**
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

/**
* @return BelongsTo
*/
public function keyword(): BelongsTo
{
return $this->belongsTo(Keyword::class);
}
}
11 changes: 7 additions & 4 deletions backend/app/UseCases/Keyword/CreateOrFirstAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@

class CreateOrFirstAction
{
/**
* @param string $keyword
* @return Keyword
*/
public static function run(
string $keyword,
int $weight
string $keyword
): Keyword {
// @todo barryvdh/laravel-ide-helperにより強制的に型付け
/* @var Keyword */
return Keyword::createOrFirst(
compact("keyword", "weight"),
compact("keyword", "weight")
["keyword" => $keyword],
["keyword" => $keyword]
);
}
}
Loading

0 comments on commit 71ea8a0

Please sign in to comment.