owned this note
owned this note
Published
Linked with GitHub
# subtask
subtask功能:可將題目的測資進行分類,並且給予權重進行配分。

# **操作步驟如下:**
1. 於problem編輯頁面設定subtask數量

3. 設定task分別的權重(wt)

5. 進入測資編輯頁面將測資分類

7. 於競賽中設定問題分數

# **實現步驟如下:**
1.Problems 頁面:
===

# 後端
## src/Entity/Problem.php
於Entity中的Problem.php新增`$subtask (int)` 及`$task_point (json)` 欄位至資料庫,
也可以直接從phpmyAdmin內新增。
```
subtask: 儲存該問題有多少種類的task
task_point: 儲存每個種類對應的權重
```
```php=
//add subtask
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $subtask;
//add task_point
/**
* @var array
* @ORM\Column(type="json", name="task_point",
* options={"comment"="JSON-encoded restrictions","default"="NULL"},
* nullable=true)
*
*/
private $task_point;
/**
* Get subtask
*
* @return integer
*/
public function getSubtask()
{
return $this->subtask;
}
/**
* Set subtask
*
* @return integer
*/
public function setSubtask($subtask)
{
$this->subtask = $subtask;
return $this;
}
/**
* Get TaskPoint
*
* @return json
*/
public function getTaskPoint(): ?string
{
return $this->task_point;
}
/**
* Set TaskPoint
*
*
*/
public function setTaskPoint(?string $task_point): self
{
$this->task_point = $task_point;
return $this;
}
```
當新增完後,於terminal中執行資料庫更新指令
```
php bin/console doctrine:schema:update --force
```
## Controller/jury/ProblemCntroller.php
>### function indexAction
將subtask與task_point加入$problem選取的資料欄位,並於table_fields新增subtask與task_point的命名
```php=
$problems = $this->em->createQueryBuilder()
->select('partial p.{probid,externalid,name,timelimit,memlimit,outputlimit,subtask,restriction_languages,problems_group,task_point}', 'COUNT(tc.testcaseid) AS testdatacount')
->from(Problem::class, 'p')
->leftJoin('p.testcases', 'tc')
->orderBy('p.probid', 'ASC')
->groupBy('p.probid')
->getQuery()->getResult();
$table_fields = [
'probid' => ['title' => 'ID', 'sort' => true, 'default_sort' => true],
'name' => ['title' => 'name', 'sort' => true],
'num_contests' => ['title' => '# contests', 'sort' => true],
'timelimit' => ['title' => 'time limit', 'sort' => true],
'memlimit' => ['title' => 'memory limit', 'sophp namespace jsonrt' => true],
'outputlimit' => ['title' => 'output limit', 'sort' => true],
'num_testcases' => ['title' => '# test cases', 'sort' => true],
//在 problems 頁面新增的內容
'subtask' => ['title' => 'subtask', 'sort' => true],
'restriction_languages' => ['title' => 'restriction language', 'sort' => true],
'problems_group' => ['title' => 'problems group', 'sort' => true],
'task_point' => ['title' => 'task point', 'sort'=>true],
];
```
2.Edit problem 頁面
===
## 後端
### Controller/jury/ProblemCntroller.php
>### function editAction
由於新增ajax功能,因此要新增判定form傳送時不是利用ajax方法傳送
```php=
//add (!$request->isXmlHttpRequest()) to check if data is submitted by ajax (don't save table)
/*-------CCU-------*/
if ($form->isSubmitted() && $form->isValid()&& !$request->isXmlHttpRequest() ) {
/*-------CCU-------*/
$this->saveEntity($this->em, $this->eventLogService, $this->dj, $problem,
$problem->getProbid(), false);
return $this->redirectToRoute('jury_problem', ['probId' => $problem->getProbid()]);
}
```
取得task_point中的資料,並轉成php格式後丟入return的data中
```php=
//get Taskpoint
/*-------CCU-------*/
$all_task_point = $problem->getTaskPoint();
$task_point="";
if ($all_task_point != null)
{
$task_point = explode(",",$all_task_point);
}
return $this->render('jury/problem_edit.html.twig', [
'problem' => $problem,
'task_point' => $task_point,
'problemgroup_judge' => $problemgroup_judge,
'group_restriction' => $group_restriction,
'form' => $form->createView(),
'uploadForm' => $uploadForm->createView(),
]);
/*-------CCU-------*/
```
### Src/Type/ProblemType.php
於FormType表單中新增subtask與task_point,並將task_point設定hiddenType
```php=
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
```
> ### function buildForm
```php=
//add subtask form table
$builder->add('subtask', IntegerType::class, [
'required' => false,
'attr' => array(
'min' => '0',
'max' => '20',
)
]);
//hide taskpoint table , use to save the data of taskpoint(with javascript)
$builder->add('taskpoint', HiddenType::class, [
]);
```
## 前端
### templates/jury/partials/problem_form.html.twig
將表單樣式加入頁面
```php
{# import form select css #}
{% import "jury/jury_macros.twig" as macros %}
{% block extrahead %}
{{ macros.table_extrahead() }}
{{ macros.select2_extrahead() }}
{% endblock %}
```
顯示出subtask及task_point的欄位並且讓task_point欄位會依照subtask去做改動
```htmlmixed=
{# table for subtask and task point #}
<div class="form-group">
{{ form_label(form.subtask) }}
<div class="input-group">
{{ form_widget(form.subtask) }}
<div class="input-group-append">
<div class="input-group-text">task</div>
</div>
</div>
</div>
<div id ='task_point'>
<input type="hidden" id = "subtask_old" value = '{{form.vars.value.subtask}}'></input>
{% if form.vars.value.subtask != 0 and form.vars.value.subtask != null %}
{% for i in 1..(form.vars.value.subtask) %}
<div class="form-group" id = "group_{{i}}">
<div class="input-group">
<label class="required">Task{{i}}</label>
{#if has task_point in database output the task_point #}
<div class = "input-group">
{% if task_point is defined %}
{% if task_point != null %}
<input type="number" class="form-control" min="1" id="task_{{i}}" value="{{task_point[i-1]}}" >
<div class="input-group-append">
<div class="input-group-text">wt</div>
</div>
{% endif %}
{% else %}
<input type="number" class="form-control" min="1" id="task_{{i}}" >
<div class="input-group-append">
<div class="input-group-text">wt</div>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% endif %}
</div>
<div class="form-group">
<div class="input-group">
{{ form_widget(form.taskpoint) }}
</div>
</div>
```
新增task_point的防呆機制(javascript、ajax),當使用者選擇subtask數量時,task_point會及時更新,並且表單送出時,將所有task_point組合成陣列。
```javascript=
// control task_point table by ajax
$( document ).ready(function() {
//get the new subtask and old subtask number
var sub_new = $('#problem_subtask');
var sub_old = $('#subtask_old');
var new_subtask1 = sub_new.val();
var old_subtask1 = sub_old.val();
console.log(sub_new.val());
console.log(sub_old.val());
if (old_subtask1 != 0 )
{
if( new_subtask1 > old_subtask1)
{
old_subtask1++;
for ( i = old_subtask1; i <= new_subtask1 ; ++i )
{
item = '<div class="form-group" id = group_'+ i +'><label class=required>Task'+ i +'</label><div class =input-group><input type="number" min="0" class="form-control" id="task_'+ i +'" value=""></div></div>';
$('#task_point').append(item);
}
}
else
{
for ( i = old_subtask1 ; i>new_subtask1 ; --i)
{
$('#group_'+ i ).remove();
}
}
}
});
$(function () {
var $contests =$('#problem_subtask');
var $problem = $('#problem_subtask');
var $inputVal = $problem.val();
//If change the number
$contests.on('change', function () {
var $form = $(this).closest('form');
var data = {};
if ($inputVal<0 || $inputVal == "")
{
$inputVal = 0;
}
if ($inputVal>20)
{
$inputVal = 20;
}
data['old_subtask'] = $inputVal;
data[$problem.attr('name')] = $problem.val();
//change the tabel num by ajax
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: data,
success: function (html) {
var new_subtask = data[$problem.attr('name')];
console.log(data['old_subtask']);
console.log(new_subtask);
var old_subtask = parseInt(data['old_subtask']);
var $newProblems = $(html).find('#problem_taskpoint');
var i;
if (new_subtask<0)
{
new_subtask = 0;
$('#problem_subtask').val(0);
}
if (new_subtask>20)
{
new_subtask = 20;
$('#problem_subtask').val(20);
}
if( new_subtask >= old_subtask)
{
old_subtask++;
for ( i = old_subtask; i <= new_subtask ; ++i )
{
item = '<div class="form-group" id = group_'+ i +'><label class=required>Task'+ i +'</label><div class =input-group><input type="number" min="0" class="form-control" id="task_'+ i +'" value=""><div class="input-group-append"><div class="input-group-text">wt</div></div></div></div>';
$('#task_point').append(item);
}
}
else
{
for ( i = old_subtask ; i>new_subtask ; --i)
{
$('#group_'+ i ).remove();
}
}
$('#as').closest('.form-group').replaceWith(
$newProblems.closest('.form-group')
);
}
});
$inputVal = $(this).val();
});
$("form").submit(function(e){
var $problem2 = $('#problem_subtask');
var $taskpoint = $('#problem_taskpoint');
var $subtask = $problem2.val();
var $i ;
var $all_taskpoint = [];
for ( $i = 1 ; $i <= $subtask ; $i++)
{
var $point = $('#task_'+($i)).val();
$all_taskpoint[$i-1] = $point;
}
$taskpoint.val($all_taskpoint);
});
});
```
3.Testcase edit 頁面
===
# 後端
## Controller/jury/ProblemCntroller.php
> ### function testcasesAction
新增task成功修改的訊息
```php=
//add task classification
/*-------CCU-------*/
$newTask = (int)$request->request->get('task')[$rank];
if ($newTask !== (int)$testcase->getTask()) {
$testcase->setTask($newTask);
$messages[] = sprintf('Updated Task of testcase%d to %s', $rank,$newTask);
}
/*-------CCU-------*/
```
此處為7.2.0的bug,利用7.3.0的程式碼進行替換
```php=
//7.2.0的bug 修改成7.3.0此處的程式碼
/*-------CCU-------*/
$haswarnings = false;
if ($allOk) {
$newTestcase = new Testcase();
$newTestcaseContent = new TestcaseContent();
$newTestcase
->setContent($newTestcaseContent)
->setRank($maxrank)
->setProblem($problem)
->setDescription($request->request->get('add_desc'))
->setSample($request->request->has('add_sample'))
->setTask((int)$request->request->get('add_task'));
foreach (['input', 'output'] as $type) {
$file = $request->files->get('add_' . $type);
$content = file_get_contents($file->getRealPath());
$contentMethod = sprintf('set%s', ucfirst($type));
$md5Method = sprintf('setMd5sum%s', ucfirst($type));
$newTestcaseContent->{$contentMethod}($content);
$newTestcase->{$md5Method}(md5($content));
if ($type == 'input') {
$newTestcase->setOrigInputFilename(basename($file->getClientOriginalName(), '.in'));
}
}
if ($imageFile = $request->files->get('add_image')) {
if (!$imageFile->isValid()) {
$this->addFlash('danger', sprintf(
'File upload error new image: %s',
$imageFile->getErrorMessage()
));
return $this->redirectToRoute('jury_problem_testcases', ['probId' => $probId]);
}
$content = file_get_contents($imageFile->getRealPath());
$imageType = Utils::getImageType($content, $error);
if ($imageType === false) {
$this->addFlash('danger', sprintf('image: %s', $error));
return $this->redirectToRoute('jury_problem_testcases', ['probId' => $probId]);
}
$thumb = Utils::getImageThumb($content, $thumbnailSize,
$this->dj->getDomjudgeTmpDir(), $error);
if ($thumb === false) {
$thumb = null;
$this->addFlash('danger', sprintf('image: %s', $error));
return $this->redirectToRoute('jury_problem_testcases', ['probId' => $probId]);
}
$newTestcase->setImageType($imageType);
$newTestcaseContent
->setImageThumb($thumb)
->setimage($content);
}
$this->em->persist($newTestcase);
$this->dj->auditlog('testcase', $probId, 'added', sprintf("rank %d", $maxrank));
$inFile = $request->files->get('add_input');
$outFile = $request->files->get('add_output');
$message = sprintf(
'Added new testcase %d from files %s (%s) and %s (%s)', $maxrank,
$inFile->getClientOriginalName(), Utils::printsize($inFile->getSize()),
$outFile->getClientOriginalName(), Utils::printsize($outFile->getSize())
);
$haswarnings = false;
if (strlen($newTestcaseContent->getOutput()) > $outputLimit * 1024) {
$message .= sprintf(
'<br><b>Warning: file size exceeds <code>output_limit</code> ' .
'of %s kB. This will always result in wrong answers!</b>',
$outputLimit
);
$haswarnings = true;
}
if (empty($newTestcaseContent->getInput()) ||
empty($newTestcaseContent->getOutput())) {
$message .= '<br /><b>Warning: empty testcase file(s)!</b>';
$haswarnings = true;
}
$messages[] = $message;
}
$this->em->flush();
/*-------CCU-------*/
```
# 前端
## templates/jury/problem_testcases.html.twig
新增task的欄位,並且設置防呆判斷
```htmlmixed=
{#-------CCU-------#}
{% if problem.subtask != null and problem.subtask != 0 %}
<th class="testsubtask">task</th>
{% endif %}
{#-------CCU-------#}
{% if problem.subtask != null and problem.subtask != 0 %}
<td rowspan="2" class="testsubtask">
<input type="number" class="inputsubtask" min=1 max ="{{problem.subtask}}" id="task[{{ testcase.rank }}]" name="task[{{ testcase.rank }}]" value="{{testcase.task}}"/>
</td>
{% endif %}
{#-------CCU-------#}
{#-------CCU-------#}
<div class="form-row">
<div class="form-group col-sm-4">
{% if problem.subtask != null and problem.subtask != 0 %}
<td rowspan="2" class="testsubtask">
<input type="number" class="inputsubtask" min=1 max ="{{problem.subtask}}" name="add_task" id="add_task" /> Task
</td>
{% endif %}
</div>
</div>
{#-------CCU-------#}
```
4.計分方式修改
===
## 後端
### Judgehost/lib/judge/Judgedaemon.main.php
為了讓所有測資能夠跑完,需要在**Judgehost**的檔案中進行更動,於`$lastcase_correct`條件進行修改,並且將task加進資料array()之中,方便未來做更正。
```php=
/*-----CCU-----*/
//add "wrong-answer" let all testcase run
$lastcase_correct = ($result === 'correct' || $result ==='wrong-answer');
$new_judging_run = array(
'testcaseid' => urlencode((string)$tc['testcaseid']),
'runresult' => urlencode($result),
'task' => urlencode((string)$tc['task']),
'runtime' => urlencode((string)$runtime),
'output_run' => rest_encode_file($testcasedir . '/program.out', false),
'output_error' => rest_encode_file($testcasedir . '/program.err', $output_storage_limit),
'output_system' => rest_encode_file($testcasedir . '/system.out', $output_storage_limit),
'metadata' => rest_encode_file($testcasedir . '/program.meta', $output_storage_limit),
'output_diff' => rest_encode_file($testcasedir . '/feedback/judgemessage.txt', $output_storage_limit)
);
logmsg(LOG_DEBUG, "Testcase_task $tc[task] done, result: " . $new_judging_run['task']);
/*-------CCU-------*/
```
### src/Entity/Judging.php
於Entity中Judging.php新增`$task_result(json)`欄位,用於儲存task的所有結果,
如果該task正確則陣列儲存為1。
例如: task1 : 正確、task2 : 錯誤、task3 : 正確,則`$task_result = [1,0,1]`
```php=
/*-----CCU-----*/
/**
* @ORM\Column(type="json", nullable=true)
*/
private $task_result = [];
/*-----CCU-----*/
/*-----CCU-----*/
public function getTask(): ?int
{
return $this->task;
}
public function setTask(?int $task): self
{
$this->task = $task;
return $this;
}
public function getTaskResult()
{
return $this->task_result;
}
public function setTaskResult($task_result)
{
$this->task_result = $task_result;
return $this;
}
/*-----CCU-----*/
```
### src/Entity/Scorecache.php
於Entity中Scorecache.php新增`$all_task_point(json)`欄位,用於儲存該題的所有結果中,最高的權重與總權重。
例如: 總權重:21、最高權重為:10 ,則`$all_task_point = [21,10]`
```php=
/*-----CCU-----*/
/**
* @ORM\Column(type="json", nullable=true)
*/
private $all_task_point = [];
/*-----CCU-----*/
/*-----CCU-----*/
public function getAllTaskPoint(): ?array
{
return $this->all_task_point;
}
public function setAllTaskPoint(?array $all_task_point): self
{
$this->all_task_point = $all_task_point;
return $this;
}
/*-----CCU-----*/
```
當新增完後,於terminal中執行資料庫更新指令
```
php bin/console doctrine:schema:update --force
```
### src/Controller/API/JudgehostController.php
>### function addSingleJudgingRun
當所有的testcase judging完畢,新增該題擁有subtask時,會將所有的類別進行計算的判定,並判定當該類別全部正確時,將`$task_result[task-1] = 1`。
另外,原先設計為該題**測資全部正確**才會更新分數板(scorecache),因此需要底下新增當有subtask時也要執行calculateScoreRow函式並且儲存`$task_result`欄位的結果。
```php=
/*-----CCU-----*/
$submission = $judging->getSubmission();
$subtask = $submission->getProblem()->getSubtask();
//get subtask if exist then calucate the judging task_result array
if(!empty($subtask))
{
$task_result = array();
//initial all array
for( $i=0 ; $i < $subtask ; $i++ )
{
$task_result[$i] = 0;
$task_result_count[$i] = 0;
$task_result_correct[$i] = 0;
}
foreach ($runs as $run)
{
$run_result = $run->getRunresult();
$run_task = $run->getTestcase()->getTask();
if($run_result === 'correct')
{
$task_result_correct[(int)$run_task - 1] ++;
}
$task_result_count[(int)$run_task - 1] ++;
}
//if all task is success that $task_result[task-1] = 1
for( $i=0 ; $i < $subtask ; $i++ )
{
if( $task_result_count[$i] == $task_result_correct[$i] && $task_result_count[$i] != 0 )
{
$task_result[$i] = 1 ;
}
}
}
// updat when it has subtask
if (!empty($task_result) || !isset($task_result))
{
$judging->setTaskResult($task_result);
$submission = $judging->getSubmission();
$contest = $submission->getContest();
$team = $submission->getTeam();
$problem = $submission->getProblem();
$this->scoreboardService->calculateScoreRow($contest, $team, $problem);
}
/*-----CCU-----*/
```
### src/service/ScoreboardService.php
>### function calculateScoreRow
於計算計分板的函式中加入計算subtask分數的方法。
首先先於計算所有submissions之前,判斷該問題是否有subtask。
```php=
/*-----CCU-----*/
//GET subtask if it's exist
$subtask = $problem->getSubtask();
if(!empty($subtask) )
{
// if subtask is not null/zero , get all task point source
// set all task point(json)->task_point_result(array) and max_task_point = 0
$task_point_json = $problem->getTaskPoint();
$task_point = explode(",",$task_point_json);
$all_task_point = 0;
$max_task_point = 0;
$task_point_result = array();
for ( $i = 0 ; $i< $subtask ; $i++)
{
$all_task_point = $all_task_point + (int)$task_point[$i];
}
$task_point_result[0] = $all_task_point;
}
/*-----CCU-----*/
```
計算每個submission的獲得的總權重,並且儲存最高的權重。
```php=
/*-----CCU-----*/
//calculate the max point
if(!empty($subtask) )
{
//if judging is wrong answer then calculat MAX point
if($judging->getResult() == Judging::RESULT_WRONG_ANSWER )
{
$task_result = $judging->getTaskResult();
$new_task_point = 0;
if(!empty($task_result))
{
for ( $i = 0 ; $i< $subtask ; $i++)
{
$new_task_point = $new_task_point + ((int)$task_point[$i] * (int)$task_result[$i]);
$this->logger->debug(
"new_point = '%d' task_result['%d'] ='%d'",
[ $new_task_point, $i ,(int)$task_result[$i],]
);
}
if ($new_task_point > $max_task_point)
{
$max_task_point= $new_task_point;
}
}
$task_point_result[1] = $max_task_point;
$this->logger->debug(
"max_point = '%d',$task_point_result[0] = '%d',$task_point_result[1] = '%d'",
[ $max_task_point,$task_point_result[0],$task_point_result[1]]
);
}
}
/*-----CCU-----*/
```
## 前端
### public/style_domjudge.css
新增partially_correct的樣式
```css=
.sol_partially_correct { color: rgb(240, 186, 9); }
```
### src/Twig/TwigExtension.php
新增partially_correct的case
```php=
/*-----CCU-----*/
case 'partially_correct':
$style = 'sol_partially_correct';
break;
/*-----CCU-----*/
```
### templates/team/partials/submission_list.html.twig
新增並判定partially_correct的顯示方式
```htmlmixed=
{#-----CCU-----#}
{#new result for partially correct #}
{%- elseif submission.judgings.first.result in ['correct','no-output'] %}
{{- submission.judgings.first.result | printResult -}}
{%- else %}
{% set part = false %}
{%- if submission.problem.subtask is not empty %}
{%- set TaskResults = submission.judgings.first.TaskResult %}
{%- for TaskResult in TaskResults if TaskResult == 1 %}
{% set part = true %}
{%- endfor %}
{%- endif %}
{%- if part %}
{{- 'partially_correct' | printResult -}}
{%- else %}
{{- submission.judgings.first.result | printResult -}}
{%- endif %}
{%- endif %}
{#-----CCU-----#}
```
### src/Twig/TwigExtension.php
新增subtask的測資顯示的函式
```php=
/*-----CCU-----*/
$results = '';
$results_2 ='';
foreach ($testcases as $key => $testcase) {
$class = $submissionDone ? 'secondary' : 'primary';
$text = '?';
if ($testcase['runresult'] !== null) {
$text = substr($testcase['runresult'], 0, 1);
$class = 'danger';
if ($testcase['runresult'] === Judging::RESULT_CORRECT) {
$text = '✓';
$class = 'success';
}
}
if (!empty($testcase['description'])) {
$title = sprintf('Run %d: %s', $key + 1,
Utils::specialchars($testcase['description']));
} else {
$title = sprintf('Run %d', $key + 1);
}
$results .= sprintf('<span class="badge badge-%s badge-testcase" title="%s">%s</span>', $class, $title,
$text);
$results_2 .= sprintf('<span class="badge badge-%s badge-testcase" title="%s">%s</span>', $class, $title,
$text)."'cut'";
$task .= $testcase['task']."'cut'";
}
if ($subtask == NULL && $subtask == 0)
{
return $results;
}
else
{
$results ='';
$resultarray = explode("'cut'",$results_2);
$taskarray = explode("'cut'",$task);
for ( $i = 1 ; $i<$subtask + 1 ; $i++ )
{
$results .= "Task".$i." : ";
for ($j = 0 ; $j < count($taskarray) ; $j++)
{
if ($taskarray[$j] == $i)
{
$results .= $resultarray[$j];
}
}
$results .= " ";
}
return $results;
}
/*-----CCU-----*/
```
### templates/team/partials/submission.html.twig
將資料丟入顯示測資的函式中,並顯示回傳結果
```htmlmixed=
{#-----CCU-----#}
{#勾勾圖案 將judging.submission 丟進 TwigExtension.php(Twig Filters) 的 testcaseResults 函式處理 回傳顯示結果 #}
<div class="p-2">
Testcase: {{- judging.submission | testcaseResults -}}
</div>
{#-----CCU-----#}
```
### templates/partials/scoreboard_table.html.twig
新增計分板顯示分數的方式,當問題錯誤會查看scorecache裡的分數,並計算分數。
```htmlmixed=
{#-----CCU-----#}
{% if scoreboard.Scorecache[num] is defined and scoreboard.Scorecache[num] is not empty %}
{% set Scorecache = scoreboard.Scorecache[num] %}
{% endif %}
{#-----CCU-----#}
{#-----CCU-----#}
{% if time == ' ' %}
{% if Scorecache is defined and scoreInSeconds == false and matrixItem.AllTaskPoint is not empty and matrixItem.AllTaskPoint[1] is defined %}
{% set A = matrixItem.AllTaskPoint[1] / matrixItem.AllTaskPoint[0] * problem.points %}
{% if A != 0 and problem.subtask is not empty and problem.subtask != 0 %}
{{A | round }} points
{% else %}
{{ time| raw }}
{% endif %}
{% else %}
{{ time| raw }}
{% endif %}
{% else %}
{{ time| raw }}
{% endif %}
{#-----CCU-----#}
```