Skip to content

Commit 165c266

Browse files
authored
Merge pull request #2206 from opensource-workshop/word-count
[課題管理] レポートのコメントに単語数と文字数を表示する機能を追加しました
2 parents 58f4c1d + 8bf6aec commit 165c266

File tree

9 files changed

+184
-5
lines changed

9 files changed

+184
-5
lines changed

app/Enums/LearningtaskUseFunction.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ final class LearningtaskUseFunction extends EnumsBase
4343
const use_report_reference_mail = 'use_'.self::report.'_reference_mail';
4444
// [表示方法]
4545
const use_report_status_collapse = 'use_'.self::report.'_status_collapse';
46+
const use_report_show_word_count = 'use_'.self::report.'_show_word_count';
47+
const use_report_show_char_count = 'use_'.self::report.'_show_char_count';
4648

4749
// --- 試験設定
4850
// [利用する試験提出機能]
@@ -110,6 +112,8 @@ final class LearningtaskUseFunction extends EnumsBase
110112
self::use_report_reference_mail => 'メール送信(受講者宛)',
111113
// 表示方法
112114
self::use_report_status_collapse => '履歴を開閉する',
115+
self::use_report_show_word_count => '文字数を表示する',
116+
self::use_report_show_char_count => '字数を表示する',
113117
// --- 試験設定
114118
// 利用する試験提出機能
115119
self::use_examination => '提出',

app/Models/User/Learningtasks/LearningtasksUsersStatuses.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,23 @@ public function upload()
192192
// withDefault() を指定しておくことで、Uploads がないときに空のオブジェクトが返ってくるので、null po 防止。
193193
return $this->hasOne(Uploads::class, 'id', 'upload_id')->withDefault();
194194
}
195+
196+
/**
197+
* 単語数を取得する
198+
*/
199+
public function getWordCountAttribute()
200+
{
201+
// マルチバイト文字などは適切な値を取得できない
202+
// 必要になれば形態素解析などを行う必要がある
203+
return $this->comment ? str_word_count($this->comment) : 0;
204+
}
205+
206+
/**
207+
* 字数を取得する
208+
* 文字数は、全角文字も半角文字も1文字としてカウントする。
209+
*/
210+
public function getCharCountAttribute()
211+
{
212+
return $this->comment ? mb_strlen($this->comment) : 0;
213+
}
195214
}

app/Plugins/User/Learningtasks/DataProviders/ReportCsvDataProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ private function getColumnDataGenerators(
171171
},
172172
'評価' => fn() => optional($last_evaluation)->grade,
173173
'評価コメント' => fn() => optional($last_evaluation)->comment,
174+
'単語数' => fn() => optional($last_submission)->word_count,
175+
'字数' => fn() => optional($last_submission)->char_count,
174176
];
175177
}
176178
}

app/Plugins/User/Learningtasks/Services/LearningtaskReportColumnDefinition.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class LearningtaskReportColumnDefinition implements ColumnDefinitionInterface //
2828
'ファイルURL' => 'file_url',
2929
'評価' => 'grade',
3030
'評価コメント' => 'comment',
31+
'単語数' => 'word_count',
32+
'字数' => 'char_count',
3133
];
3234

3335
/**
@@ -58,6 +60,12 @@ public function getHeaders(): array
5860
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_comment)) {
5961
$header_columns[] = '本文';
6062
}
63+
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_show_word_count)) {
64+
$header_columns[] = '単語数';
65+
}
66+
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_show_char_count)) {
67+
$header_columns[] = '字数';
68+
}
6169
if ($this->setting_checker->isEnabled(LearningtaskUseFunction::use_report_file)) {
6270
$header_columns[] = 'ファイルURL';
6371
}

resources/views/plugins/user/learningtasks/default/learningtasks_edit_learningtasks.blade.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,11 +340,20 @@ class="custom-control-input"
340340

341341
<div class="form-group row mb-0">
342342
<label class="{{$frame->getSettingLabelClass()}}">表示方法</label>
343-
<div class="{{$frame->getSettingInputClass(true)}}">
344-
<div class="custom-control custom-checkbox mr-3">
343+
<div class="{{$frame->getSettingInputClass()}}">
344+
<div class="custom-control custom-checkbox custom-control-inline">
345345
<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>
346346
<label class="custom-control-label" for="use_report_status_collapse">履歴を開閉する</label>
347347
</div>
348+
<div class="custom-control custom-checkbox custom-control-inline">
349+
<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>
350+
<label class="custom-control-label" for="use_report_show_word_count">単語数を表示する</label>
351+
</div>
352+
<div class="custom-control custom-checkbox custom-control-inline">
353+
<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>
354+
<label class="custom-control-label" for="use_report_show_char_count">文字数を表示する</label>
355+
</div>
356+
<small class="form-text text-muted">単語数の表示は、日本語などのマルチバイト文字に対応していません。</small>
348357
</div>
349358
</div>
350359

resources/views/plugins/user/learningtasks/default/learningtasks_edit_report.blade.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,20 @@ class="custom-control-input"
231231

232232
<div class="form-group row">
233233
<label class="col-md-3 text-md-right">表示方法</label>
234-
<div class="col-md-9 d-md-flex">
235-
<div class="custom-control custom-checkbox mr-3">
234+
<div class="col-md-9">
235+
<div class="custom-control custom-checkbox custom-control-inline">
236236
<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>
237237
<label class="custom-control-label" for="use_report_status_collapse">履歴を開閉する</label>
238238
</div>
239+
<div class="custom-control custom-checkbox custom-control-inline">
240+
<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>
241+
<label class="custom-control-label" for="use_report_show_word_count">単語数を表示する</label>
242+
</div>
243+
<div class="custom-control custom-checkbox custom-control-inline">
244+
<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>
245+
<label class="custom-control-label" for="use_report_show_char_count">文字数を表示する</label>
246+
</div>
247+
<small class="form-text text-muted">単語数の表示は、日本語などのマルチバイト文字に対応していません。</small>
239248
</div>
240249
</div>
241250
</div>

resources/views/plugins/user/learningtasks/default/learningtasks_show_report_status.blade.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,28 @@
3131
@if ($tool->isUseFunction($user_status->task_status, 'comment'))
3232
<tr>
3333
<th>コメント</th>
34-
<td>{!!nl2br(e($user_status->comment))!!}</td>
34+
<td>{!!nl2br(e($user_status->comment))!!}
35+
{{-- 文字数、単語数の表示 --}}
36+
@php
37+
$should_show_word_count = $tool->isUseFunction($user_status->task_status, 'show_word_count');
38+
$should_show_char_count = $tool->isUseFunction($user_status->task_status, 'show_char_count');
39+
$word_count = $user_status->word_count;
40+
$char_count = $user_status->char_count;
41+
@endphp
42+
@if ($should_show_word_count || $should_show_char_count)
43+
<div><small class="text-muted">
44+
@if ($should_show_word_count)
45+
{{ $word_count }}単語
46+
@endif
47+
@if ($should_show_word_count && $should_show_char_count)
48+
/
49+
@endif
50+
@if ($should_show_char_count)
51+
{{ $char_count }}文字
52+
@endif
53+
</small></div>
54+
@endif
55+
</td>
3556
</tr>
3657
@endif
3758
</tbody>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Tests\Unit\Models\User\Learningtasks;
4+
5+
use App\Models\User\Learningtasks\LearningtasksUsersStatuses;
6+
use Illuminate\Foundation\Testing\RefreshDatabase;
7+
use Tests\TestCase;
8+
9+
class LearningtasksUsersStatusesTest extends TestCase
10+
{
11+
use RefreshDatabase;
12+
13+
/**
14+
* commentがnullまたは空文字の場合、word_countは0を返すことをテスト
15+
*/
16+
public function testWordCountIsZeroWhenCommentIsNullOrEmpty(): void
17+
{
18+
$model = new LearningtasksUsersStatuses(['comment' => null]);
19+
$this->assertSame(0, $model->word_count);
20+
$model = new LearningtasksUsersStatuses(['comment' => '']);
21+
$this->assertSame(0, $model->word_count);
22+
}
23+
24+
/**
25+
* commentの内容に応じてword_countが正しい値を返すことをテスト
26+
*/
27+
public function testWordCountReturnsExpectedValue(): void
28+
{
29+
$model = new LearningtasksUsersStatuses(['comment' => 'This is a test']);
30+
$this->assertSame(4, $model->word_count);
31+
}
32+
33+
/**
34+
* commentがnullまたは空文字の場合、char_countは0を返すことをテスト
35+
*/
36+
public function testCharCountIsZeroWhenCommentIsNullOrEmpty(): void
37+
{
38+
$model = new LearningtasksUsersStatuses(['comment' => null]);
39+
$this->assertSame(0, $model->char_count);
40+
$model = new LearningtasksUsersStatuses(['comment' => '']);
41+
$this->assertSame(0, $model->char_count);
42+
}
43+
44+
/**
45+
* commentの内容に応じてchar_countが正しい値を返すことをテスト
46+
*/
47+
public function testCharCountReturnsExpectedValue(): void
48+
{
49+
$model = new LearningtasksUsersStatuses(['comment' => 'abc']);
50+
$this->assertSame(3, $model->char_count);
51+
$model = new LearningtasksUsersStatuses(['comment' => 'テストword']);
52+
$this->assertSame(7, $model->char_count);
53+
}
54+
}

tests/Unit/Plugins/User/Learningtasks/DataProviders/ReportCsvDataProviderTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,4 +277,57 @@ public function getRowsYieldsDataWithEmptyFieldsWhenNoStatuses(): void
277277
];
278278
$this->assertEquals($expected_row_data, $result_rows_array[0], '提出/評価がない学生のデータが期待通りであること');
279279
}
280+
281+
/**
282+
* 単語数・字数カラムが有効な場合に正しい値が出力されるテスト
283+
* @test
284+
* @covers ::getRows
285+
* @group learningtasks
286+
* @group learningtasks-dataprovider
287+
*/
288+
public function getRowsYieldsWordAndCharCountColumns(): void
289+
{
290+
// Arrange
291+
$student = User::factory()->create(['userid' => 's003', 'name' => '学生 丙']);
292+
$submission_comment = 'word テスト 123'; // 半角2単語+全角1単語+数字1単語 → str_word_count=3, mb_strlen=11
293+
$submission_created_at_str = now()->subHour()->toDateTimeString();
294+
$submission = LearningtasksUsersStatuses::factory()->create([
295+
'post_id' => $this->post->id, 'user_id' => $student->id, 'task_status' => 1,
296+
'comment' => $submission_comment, 'upload_id' => 888, 'created_at' => $submission_created_at_str,
297+
]);
298+
// 評価データも追加(値は使わない)
299+
LearningtasksUsersStatuses::factory()->create([
300+
'post_id' => $this->post->id, 'user_id' => $student->id, 'task_status' => 2,
301+
'grade' => '', 'comment' => '評価コメント',
302+
]);
303+
304+
// ヘッダーに単語数・字数を含める
305+
$headers = ['ログインID', 'ユーザ名', '提出日時', '提出回数', '単語数', '字数'];
306+
$this->mock_column_definition->shouldReceive('getHeaders')->once()->andReturn($headers);
307+
$this->mock_user_repository->shouldReceive('getStudents')
308+
->with($this->post, $this->page)
309+
->once()
310+
->andReturn(new EloquentCollection([$student]));
311+
312+
// Act
313+
$result_iterable = $this->data_provider->getRows(
314+
$this->mock_column_definition,
315+
$this->post,
316+
$this->page,
317+
$this->site_url
318+
);
319+
$result_rows_array = iterator_to_array($result_iterable);
320+
321+
// Assert
322+
$this->assertCount(1, $result_rows_array, '学生1名分のデータが yield されるべき');
323+
$expected_row = [
324+
$student->userid,
325+
$student->name,
326+
$submission_created_at_str,
327+
'1', // 提出回数
328+
str_word_count($submission_comment), // 単語数(PHPのstr_word_count仕様)
329+
mb_strlen($submission_comment), // 字数(全角・半角問わず)
330+
];
331+
$this->assertEquals($expected_row, $result_rows_array[0], '単語数・字数カラムの値が正しく出力されること');
332+
}
280333
}

0 commit comments

Comments
 (0)