Skip to content

[課題管理] レポートのコメントに単語数と文字数を表示する機能を追加しました #2206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/Enums/LearningtaskUseFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ final class LearningtaskUseFunction extends EnumsBase
const use_report_reference_mail = 'use_'.self::report.'_reference_mail';
// [表示方法]
const use_report_status_collapse = 'use_'.self::report.'_status_collapse';
const use_report_show_word_count = 'use_'.self::report.'_show_word_count';
const use_report_show_char_count = 'use_'.self::report.'_show_char_count';

// --- 試験設定
// [利用する試験提出機能]
Expand Down Expand Up @@ -110,6 +112,8 @@ final class LearningtaskUseFunction extends EnumsBase
self::use_report_reference_mail => 'メール送信(受講者宛)',
// 表示方法
self::use_report_status_collapse => '履歴を開閉する',
self::use_report_show_word_count => '文字数を表示する',
self::use_report_show_char_count => '字数を表示する',
// --- 試験設定
// 利用する試験提出機能
self::use_examination => '提出',
Expand Down
19 changes: 19 additions & 0 deletions app/Models/User/Learningtasks/LearningtasksUsersStatuses.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,4 +192,23 @@ public function upload()
// withDefault() を指定しておくことで、Uploads がないときに空のオブジェクトが返ってくるので、null po 防止。
return $this->hasOne(Uploads::class, 'id', 'upload_id')->withDefault();
}

/**
* 単語数を取得する
*/
public function getWordCountAttribute()
{
// マルチバイト文字などは適切な値を取得できない
// 必要になれば形態素解析などを行う必要がある
return $this->comment ? str_word_count($this->comment) : 0;
}

/**
* 字数を取得する
* 文字数は、全角文字も半角文字も1文字としてカウントする。
*/
public function getCharCountAttribute()
{
return $this->comment ? mb_strlen($this->comment) : 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ private function getColumnDataGenerators(
},
'評価' => fn() => optional($last_evaluation)->grade,
'評価コメント' => fn() => optional($last_evaluation)->comment,
'単語数' => fn() => optional($last_submission)->word_count,
'字数' => fn() => optional($last_submission)->char_count,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class LearningtaskReportColumnDefinition implements ColumnDefinitionInterface //
'ファイルURL' => 'file_url',
'評価' => 'grade',
'評価コメント' => 'comment',
'単語数' => 'word_count',
'字数' => 'char_count',
];

/**
Expand Down Expand Up @@ -58,6 +60,12 @@ public function getHeaders(): array
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_comment)) {
$header_columns[] = '本文';
}
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_show_word_count)) {
$header_columns[] = '単語数';
}
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_show_char_count)) {
$header_columns[] = '字数';
}
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_file)) {
$header_columns[] = 'ファイルURL';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,20 @@ class="custom-control-input"

<div class="form-group row mb-0">
<label class="{{$frame->getSettingLabelClass()}}">表示方法</label>
<div class="{{$frame->getSettingInputClass(true)}}">
<div class="custom-control custom-checkbox mr-3">
<div class="{{$frame->getSettingInputClass()}}">
<div class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" name="base_settings[use_report_status_collapse]" value="on" class="custom-control-input" id="use_report_status_collapse" @if(old("base_settings.use_report_status_collapse", $tool->getFunction('use_report_status_collapse')) == 'on') checked=checked @endif>
<label class="custom-control-label" for="use_report_status_collapse">履歴を開閉する</label>
</div>
<div class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" name="base_settings[use_report_show_word_count]" value="on" class="custom-control-input" id="use_report_show_word_count" @if(old("base_settings.use_report_show_word_count", $tool->getFunction('use_report_show_word_count')) == 'on') checked=checked @endif>
<label class="custom-control-label" for="use_report_show_word_count">単語数を表示する</label>
</div>
<div class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" name="base_settings[use_report_show_char_count]" value="on" class="custom-control-input" id="use_report_show_char_count" @if(old("base_settings.use_report_show_char_count", $tool->getFunction('use_report_show_char_count')) == 'on') checked=checked @endif>
<label class="custom-control-label" for="use_report_show_char_count">文字数を表示する</label>
</div>
<small class="form-text text-muted">単語数の表示は、日本語などのマルチバイト文字に対応していません。</small>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,20 @@ class="custom-control-input"

<div class="form-group row">
<label class="col-md-3 text-md-right">表示方法</label>
<div class="col-md-9 d-md-flex">
<div class="custom-control custom-checkbox mr-3">
<div class="col-md-9">
<div class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" name="post_settings[use_report_status_collapse]" value="on" class="custom-control-input" id="use_report_status_collapse" @if(old("post_settings.use_report_status_collapse", $tool->getFunction('use_report_status_collapse', true)) == 'on') checked=checked @endif>
<label class="custom-control-label" for="use_report_status_collapse">履歴を開閉する</label>
</div>
<div class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" name="post_settings[use_report_show_word_count]" value="on" class="custom-control-input" id="use_report_show_word_count" @if(old("post_settings.use_report_show_word_count", $tool->getFunction('use_report_show_word_count', true)) == 'on') checked=checked @endif>
<label class="custom-control-label" for="use_report_show_word_count">単語数を表示する</label>
</div>
<div class="custom-control custom-checkbox custom-control-inline">
<input type="checkbox" name="post_settings[use_report_show_char_count]" value="on" class="custom-control-input" id="use_report_show_char_count" @if(old("post_settings.use_report_show_char_count", $tool->getFunction('use_report_show_char_count', true)) == 'on') checked=checked @endif>
<label class="custom-control-label" for="use_report_show_char_count">文字数を表示する</label>
</div>
<small class="form-text text-muted">単語数の表示は、日本語などのマルチバイト文字に対応していません。</small>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,28 @@
@if ($tool->isUseFunction($user_status->task_status, 'comment'))
<tr>
<th>コメント</th>
<td>{!!nl2br(e($user_status->comment))!!}</td>
<td>{!!nl2br(e($user_status->comment))!!}
{{-- 文字数、単語数の表示 --}}
@php
$should_show_word_count = $tool->isUseFunction($user_status->task_status, 'show_word_count');
$should_show_char_count = $tool->isUseFunction($user_status->task_status, 'show_char_count');
$word_count = $user_status->word_count;
$char_count = $user_status->char_count;
@endphp
@if ($should_show_word_count || $should_show_char_count)
<div><small class="text-muted">
@if ($should_show_word_count)
{{ $word_count }}単語
@endif
@if ($should_show_word_count && $should_show_char_count)
/
@endif
@if ($should_show_char_count)
{{ $char_count }}文字
@endif
</small></div>
@endif
</td>
</tr>
@endif
</tbody>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Tests\Unit\Models\User\Learningtasks;

use App\Models\User\Learningtasks\LearningtasksUsersStatuses;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LearningtasksUsersStatusesTest extends TestCase
{
use RefreshDatabase;

/**
* commentがnullまたは空文字の場合、word_countは0を返すことをテスト
*/
public function testWordCountIsZeroWhenCommentIsNullOrEmpty(): void
{
$model = new LearningtasksUsersStatuses(['comment' => null]);
$this->assertSame(0, $model->word_count);
$model = new LearningtasksUsersStatuses(['comment' => '']);
$this->assertSame(0, $model->word_count);
}

/**
* commentの内容に応じてword_countが正しい値を返すことをテスト
*/
public function testWordCountReturnsExpectedValue(): void
{
$model = new LearningtasksUsersStatuses(['comment' => 'This is a test']);
$this->assertSame(4, $model->word_count);
}

/**
* commentがnullまたは空文字の場合、char_countは0を返すことをテスト
*/
public function testCharCountIsZeroWhenCommentIsNullOrEmpty(): void
{
$model = new LearningtasksUsersStatuses(['comment' => null]);
$this->assertSame(0, $model->char_count);
$model = new LearningtasksUsersStatuses(['comment' => '']);
$this->assertSame(0, $model->char_count);
}

/**
* commentの内容に応じてchar_countが正しい値を返すことをテスト
*/
public function testCharCountReturnsExpectedValue(): void
{
$model = new LearningtasksUsersStatuses(['comment' => 'abc']);
$this->assertSame(3, $model->char_count);
$model = new LearningtasksUsersStatuses(['comment' => 'テストword']);
$this->assertSame(7, $model->char_count);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,57 @@ public function getRowsYieldsDataWithEmptyFieldsWhenNoStatuses(): void
];
$this->assertEquals($expected_row_data, $result_rows_array[0], '提出/評価がない学生のデータが期待通りであること');
}

/**
* 単語数・字数カラムが有効な場合に正しい値が出力されるテスト
* @test
* @covers ::getRows
* @group learningtasks
* @group learningtasks-dataprovider
*/
public function getRowsYieldsWordAndCharCountColumns(): void
{
// Arrange
$student = User::factory()->create(['userid' => 's003', 'name' => '学生 丙']);
$submission_comment = 'word テスト 123'; // 半角2単語+全角1単語+数字1単語 → str_word_count=3, mb_strlen=11
$submission_created_at_str = now()->subHour()->toDateTimeString();
$submission = LearningtasksUsersStatuses::factory()->create([
'post_id' => $this->post->id, 'user_id' => $student->id, 'task_status' => 1,
'comment' => $submission_comment, 'upload_id' => 888, 'created_at' => $submission_created_at_str,
]);
// 評価データも追加(値は使わない)
LearningtasksUsersStatuses::factory()->create([
'post_id' => $this->post->id, 'user_id' => $student->id, 'task_status' => 2,
'grade' => '優', 'comment' => '評価コメント',
]);

// ヘッダーに単語数・字数を含める
$headers = ['ログインID', 'ユーザ名', '提出日時', '提出回数', '単語数', '字数'];
$this->mock_column_definition->shouldReceive('getHeaders')->once()->andReturn($headers);
$this->mock_user_repository->shouldReceive('getStudents')
->with($this->post, $this->page)
->once()
->andReturn(new EloquentCollection([$student]));

// Act
$result_iterable = $this->data_provider->getRows(
$this->mock_column_definition,
$this->post,
$this->page,
$this->site_url
);
$result_rows_array = iterator_to_array($result_iterable);

// Assert
$this->assertCount(1, $result_rows_array, '学生1名分のデータが yield されるべき');
$expected_row = [
$student->userid,
$student->name,
$submission_created_at_str,
'1', // 提出回数
str_word_count($submission_comment), // 単語数(PHPのstr_word_count仕様)
mb_strlen($submission_comment), // 字数(全角・半角問わず)
];
$this->assertEquals($expected_row, $result_rows_array[0], '単語数・字数カラムの値が正しく出力されること');
}
}