ProblemController::form()   F
last analyzed

Complexity

Conditions 34
Paths 1

Size

Total Lines 240
Code Lines 163

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 3 Features 4
Metric Value
cc 34
eloc 163
nc 1
nop 1
dl 0
loc 240
rs 3.3333
c 9
b 3
f 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace App\Admin\Controllers;
4
5
use App\Models\Eloquent\Problem;
6
use App\Models\Eloquent\OJ;
7
use App\Http\Controllers\Controller;
8
use App\Admin\Forms\ImportPOEM;
9
use Encore\Admin\Controllers\HasResourceActions;
10
use Encore\Admin\Form;
11
use Encore\Admin\Grid;
12
use Encore\Admin\Layout\Content;
13
use Encore\Admin\Show;
14
use Exception;
15
use Illuminate\Support\MessageBag;
16
use ZipArchive;
17
use Illuminate\Support\Facades\Storage;
18
19
class ProblemController extends Controller
20
{
21
    use HasResourceActions;
22
23
    /**
24
     * Index interface.
25
     *
26
     * @param Content $content
27
     * @return Content
28
     */
29
    public function index(Content $content)
30
    {
31
        return $content
32
            ->header('Problems')
33
            ->description('all problems, problems managed by babel will not show here')
34
            ->body($this->grid()->render());
35
    }
36
37
    /**
38
     * Show interface.
39
     *
40
     * @param mixed $id
41
     * @param Content $content
42
     * @return Content
43
     */
44
    public function show($id, Content $content)
45
    {
46
        $problem=Problem::findOrFail($id);
47
        if (!$problem->markdown || $problem->onlinejudge->ocode!=='noj') {
48
            return abort('403', 'Problem managed by BABEL cannot be accessed by Admin Portal.');
49
        }
50
        return $content
51
            ->header('Problem Detail')
52
            ->description('the detail of problems')
53
            ->body($this->detail($id));
54
    }
55
56
    /**
57
     * Edit interface.
58
     *
59
     * @param mixed $id
60
     * @param Content $content
61
     * @return Content
62
     */
63
    public function edit($id, Content $content)
64
    {
65
        $problem=Problem::findOrFail($id);
66
        if (!$problem->markdown || $problem->onlinejudge->ocode!=='noj') {
67
            return abort('403', 'Problem managed by BABEL cannot be accessed by Admin Portal.');
68
        }
69
        return $content
70
            ->header('Edit Problem')
71
            ->description('edit the detail of problems')
72
            ->body($this->form()->edit($id));
73
    }
74
75
    /**
76
     * Import interface.
77
     *
78
     * @param Content $content
79
     * @return Content
80
     */
81
    public function import(Content $content)
82
    {
83
        return $content
84
            ->header('Import New Problem')
85
            ->description('import a new problem from POEM or POETRY')
86
            ->body(new ImportPOEM());
87
    }
88
89
    /**
90
     * Create interface.
91
     *
92
     * @param Content $content
93
     * @return Content
94
     */
95
    public function create(Content $content)
96
    {
97
        return $content
98
            ->header('Create New Problem')
99
            ->description('create a new problem')
100
            ->body($this->form(true));
101
    }
102
103
    /**
104
     * Make a grid builder.
105
     *
106
     * @return Grid
107
     */
108
    protected function grid()
109
    {
110
        $grid=new Grid(new Problem);
111
        $grid->model()->where([
112
            'markdown'=>1,
113
            'OJ'=>OJ::where(['ocode'=>'noj'])->first()->oid,
114
        ])->orderBy('pcode', 'asc');
115
        $grid->column('pid', "ID")->sortable();
116
        $grid->column('pcode', "PCode")->editable()->sortable();
117
        $grid->title("Title")->editable();
118
        $grid->solved_count();
119
        $grid->time_limit("Time/ms")->editable();
120
        $grid->memory_limit("Memory/kb")->editable();
121
        $grid->OJ("OJ")->display(function() {
122
            return $this->onlinejudge->name;
0 ignored issues
show
Bug Best Practice introduced by
The property onlinejudge does not exist on App\Admin\Controllers\ProblemController. Did you maybe forget to declare it?
Loading history...
123
        });
124
        $grid->tot_score("Score");
125
        $grid->partial("Partial")->display(function($partial) {
126
            return $partial ? 'Yes' : 'No';
127
        });
128
        $grid->markdown("Markdown")->display(function($markdown) {
129
            return $markdown ? 'Yes' : 'No';
130
        });
131
        $grid->column('hide', 'Hide')->switch();
132
        $grid->update_date('Updated At');
133
        // $grid->order_index("order")->sortable();
134
        $grid->filter(function(Grid\Filter $filter) {
135
            $filter->disableIdFilter();
136
            $filter->like('pcode');
137
            $filter->like('title');
138
        });
139
140
        // $grid->disableCreateButton();
141
142
        return $grid;
143
    }
144
145
    /**
146
     * Make a show builder.
147
     *
148
     * @param mixed $id
149
     * @return Show
150
     */
151
    protected function detail($id)
152
    {
153
        $show=new Show(Problem::findOrFail($id));
154
        return $show;
155
    }
156
157
    /**
158
     * Make a form builder for create view and edit.
159
     *
160
     * @return Form
161
     */
162
    protected function form($create=false)
0 ignored issues
show
Unused Code introduced by
The parameter $create is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

162
    protected function form(/** @scrutinizer ignore-unused */ $create=false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
163
    {
164
        $form=new Form(new Problem);
165
        $form->tab('Basic', function(Form $form) {
166
            $form->text('pid')->icon('MDI key')->readonly();
167
            $form->text('pcode')->icon('MDI label-black')->rules('required|alpha_dash|min:3|max:20');
168
            $form->text('title')->icon('MDI format-title')->rules('required');
169
            $form->text('time_limit')->icon('MDI timer')->default(1000)->append('MS')->rules('required');
170
            $form->text('memory_limit')->icon('MDI memory')->default(65536)->append('Kb')->rules('required');
171
            $form->simplemde('description')->rules('required');
172
            $form->simplemde('input');
173
            $form->simplemde('output');
174
            $form->simplemde('note');
175
            $form->hasMany('problemSamples', 'samples', function(Form\NestedForm $form) {
176
                $form->textarea('sample_input', 'sample input')->rows(3);
177
                $form->textarea('sample_output', 'sample output')->rows(3);
178
                $form->textarea('sample_note', 'sample note')->rows(3);
179
            });
180
            /* $form->table('samples', function ($table) {
181
                $table->textarea('sample_input', 'sample input');
182
                $table->textarea('sample_output', 'sample output');
183
                $table->textarea('sample_note', 'sample note');
184
            }); */
185
            $ojs_temp=OJ::select('oid', 'name')->get()->all();
186
            $ojs=[];
187
            foreach ($ojs_temp as $v) {
188
                $ojs[$v->oid]=$v->name;
189
            }
190
            $form->select('oj', 'OJ')->options($ojs)->default(1)->rules('required');
191
            /* $form->display('update_date'); */
192
            /* $form->text('tot_score')->rules('required');
193
            $form->select('partial', 'Partial Score')->options([
194
                0 => "No",
195
                1 => "Yes"
196
            ])->rules('required'); */
197
            $form->radio('hide', 'Hide')
198
                ->options([
199
                    0 => 'NO',
200
                    1 => 'YES'
201
                ])->default(0)->rules('required');
202
            $form->radio('spj', 'Use SPJ')
203
                ->options([
204
                    0 => 'NO',
205
                    1 => 'YES',
206
                ])->default(0)->rules('required');
207
            $form->clang('spj_src', 'SPJ Source Code');
208
            if ($form->isCreating()) {
209
                $form->chunk_file('test_case')->rules('required');
210
            } else {
211
                $form->chunk_file('test_case');
212
            }
213
214
            $form->ignore(['test_case']);
215
216
            //Hidden parameters
217
218
            $form->hidden('markdown');
219
            $form->hidden('input_type');
220
            $form->hidden('output_type');
221
            $form->hidden('solved_count');
222
            $form->hidden('difficulty');
223
            $form->hidden('partial');
224
            $form->hidden('tot_score');
225
            $form->hidden('file');
226
            $form->hidden('spj_lang');
227
            $form->hidden('spj_version');
228
        });
229
        /* if($create){
230
            $form->tools(function (Form\Tools $tools) {
231
                $tools->append('<a href="/'.config('admin.route.prefix').'/problems/import" class="btn btn-sm btn-success" style="margin-right:1rem"><i class="MDI file-powerpoint-box"></i>&nbsp;&nbsp;Import from file</a>');
232
            });
233
        } */
234
        $form->saving(function(Form $form) {
235
            $err=function($msg, $title='Test case file parse faild.') {
236
                $error=new MessageBag([
237
                    'title'   => $title,
238
                    'message' => $msg,
239
                ]);
240
                return back()->with(compact('error'));
241
            };
242
            $pcode=$form->pcode;
243
            $p=Problem::where('pcode', $pcode)->first();
244
            //check pcode has been token.
245
            $pid=$form->pid ?? null;
246
            if (!empty($p) && $p->pid!=$pid) {
247
                return $err('Pcode has been token', 'Error occur.');
248
            }
249
            //Make sure the user enters SPJ_SRc in spj problem.
250
            if ($form->spj && empty($form->spj_src)) {
251
                return $err('The SPJ problem must provide spj_src', 'create problem error');
252
            }
253
254
            $test_case=null;
255
256
            if (!is_null(request()->get('test_case'))) {
257
                $test_case=explode('http://fake.path/', request()->get('test_case'), 2)[1];
0 ignored issues
show
Bug introduced by
It seems like request()->get('test_case') can also be of type null; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

257
                $test_case=explode('http://fake.path/', /** @scrutinizer ignore-type */ request()->get('test_case'), 2)[1];
Loading history...
258
                $path=Storage::disk('temp')->path($test_case);
259
260
                if (pathinfo($path, PATHINFO_EXTENSION)!=='zip') {
261
                    return $err('You must upload a zip file iuclude test case info and content.');
262
                }
263
264
                $zip=new ZipArchive;
265
266
                if ($zip->open($path)!==true) {
267
                    return $err('You must upload a zip file without encrypt and can open successfully.');
268
                };
269
270
                //check info file. Try to generate if it does not exist.
271
                $info_content=[];
272
                if (($zip->getFromName('info'))===false) {
273
                    if (!$form->spj) {
274
                        $info_content=[
275
                            'spj' => false,
276
                            'test_cases' => []
277
                        ];
278
                        $files=[];
279
                        for ($i=0; $i<$zip->numFiles; $i++) {
280
                            $filename=$zip->getNameIndex($i);
281
                            $files[]=$filename;
282
                        }
283
                        $files_in=array_filter($files, function($filename) {
284
                            return pathinfo($filename, PATHINFO_EXTENSION)=='in';
285
                        });
286
                        sort($files_in);
287
                        $testcase_index=1;
288
                        foreach ($files_in as $filename_in) {
289
                            $filename=basename($filename_in, '.in');
290
                            $filename_out=$filename.'.out';
291
                            if (($zip->getFromName($filename_out))===false) {
292
                                continue;
293
                            }
294
                            $test_case_in=preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $zip->getFromName($filename_in));
295
                            $test_case_out=preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $zip->getFromName($filename_out));
296
                            $info_content['test_cases']["{$testcase_index}"]=[
297
                                'input_size' => strlen($test_case_in),
298
                                'input_name' => $filename_in,
299
                                'output_size' => strlen($test_case_out),
300
                                'output_name' => $filename_out,
301
                                'stripped_output_md5' => md5(utf8_encode(rtrim($test_case_out)))
302
                            ];
303
                            $testcase_index+=1;
304
                        }
305
                        if ($testcase_index==1) {
0 ignored issues
show
introduced by
The condition $testcase_index == 1 is always true.
Loading history...
306
                            return $err('Cannot detect any validate testcases, please make sure they are placed under the root directory of the zip file.');
307
                        }
308
                    } else {
309
                        $info_content=[
310
                            'spj' => true,
311
                            'test_cases' => []
312
                        ];
313
                        $files=[];
314
                        for ($i=0; $i<$zip->numFiles; $i++) {
315
                            $filename=$zip->getNameIndex($i);
316
                            $files[]=$filename;
317
                        }
318
                        $files_in=array_filter($files, function($filename) {
319
                            return pathinfo($filename, PATHINFO_EXTENSION)=='in';
320
                        });
321
                        sort($files_in);
322
                        $testcase_index=1;
323
                        foreach ($files_in as $filename_in) {
324
                            $test_case_in=$zip->getFromName($filename_in);
325
                            $info_content['test_cases']["{$testcase_index}"]=[
326
                                'input_size' => strlen($test_case_in),
327
                                'input_name' => $filename_in
328
                            ];
329
                            $testcase_index+=1;
330
                        }
331
                        if ($testcase_index==1) {
0 ignored issues
show
introduced by
The condition $testcase_index == 1 is always true.
Loading history...
332
                            return $err('Cannot detect any validate testcases, please make sure they are placed under the root directory of the zip file.');
333
                        }
334
                    }
335
                    $zip->addFromString('info', json_encode($info_content));
336
                    $zip->close();
337
                    //return $err('The zip files must include a file named info including info of test cases, and the format can see ZsgsDesign/NOJ wiki.');
338
                } else {
339
                    $info_content=json_decode($zip->getFromName('info'), true);
340
                };
341
                $zip->open($path);
342
                //If there is an INFO file, check that the contents of the file match the actual situation
343
                $test_cases=$info_content['test_cases'];
344
                foreach ($test_cases as $index => $case) {
345
                    if (!isset($case['input_name']) || (!$form->spj && !isset($case['output_name']))) {
346
                        return $err("Test case index {$index}: configuration missing input/output files name.");
347
                    }
348
                    $test_case_in=$zip->getFromName($case['input_name']);
349
                    if (!$form->spj) {
350
                        $test_case_out=$zip->getFromName($case['output_name']);
351
                    }
352
                    if ($test_case_in===false || (!$form->spj && $test_case_out===false)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $test_case_out does not seem to be defined for all execution paths leading up to this point.
Loading history...
353
                        return $err("Test case index {$index}: missing input/output files that record in the configuration.");
354
                    }
355
                    $zip->addFromString($case['input_name'], preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $test_case_in));
356
                    if (!$form->spj) {
357
                        $zip->addFromString($case['output_name'], preg_replace('~(*BSR_ANYCRLF)\R~', "\n", $test_case_out));
358
                    }
359
                }
360
                $zip->close();
361
                $zip->open($path);
362
                if (!empty($form->pid)) {
363
                    $problem=Problem::find($form->pid);
364
                    if (!empty($problem)) {
365
                        $pcode=$problem->pcode;
366
                    } else {
367
                        $pcode=$form->pcode;
368
                    }
369
                } else {
370
                    $pcode=$form->pcode;
371
                }
372
373
                if (Storage::disk('test_case')->exists($pcode)) {
0 ignored issues
show
Bug introduced by
It seems like $pcode can also be of type array; however, parameter $path of Illuminate\Filesystem\FilesystemAdapter::exists() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

373
                if (Storage::disk('test_case')->exists(/** @scrutinizer ignore-type */ $pcode)) {
Loading history...
374
                    Storage::disk('test_case')->deleteDirectory($pcode);
0 ignored issues
show
Bug introduced by
It seems like $pcode can also be of type array; however, parameter $directory of Illuminate\Filesystem\Fi...pter::deleteDirectory() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

374
                    Storage::disk('test_case')->deleteDirectory(/** @scrutinizer ignore-type */ $pcode);
Loading history...
375
                }
376
377
                Storage::disk('test_case')->makeDirectory($pcode);
0 ignored issues
show
Bug introduced by
It seems like $pcode can also be of type array; however, parameter $path of Illuminate\Filesystem\Fi...dapter::makeDirectory() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

377
                Storage::disk('test_case')->makeDirectory(/** @scrutinizer ignore-type */ $pcode);
Loading history...
378
379
                $zip->extractTo(Storage::disk('test_case')->path($pcode));
0 ignored issues
show
Bug introduced by
It seems like $pcode can also be of type array; however, parameter $path of Illuminate\Filesystem\FilesystemAdapter::path() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

379
                $zip->extractTo(Storage::disk('test_case')->path(/** @scrutinizer ignore-type */ $pcode));
Loading history...
380
381
                $form->tot_score=count($info_content['test_cases']);
382
383
            }
384
            //Set the spj-related data
385
            if ($form->spj) {
386
                $form->spj_lang='c';
387
                $form->spj_version="{$form->pcode}#".time();
388
            }
389
            //Set default data
390
            if ($form->isCreating() && empty($test_case)) {
391
                $form->tot_score=0;
392
            }
393
            $form->markdown=true;
394
            $form->input_type='standard input';
395
            $form->output_type='standard output';
396
            $form->solved_count=0;
397
            $form->difficulty=-1;
398
            $form->partial=1;
399
            $form->file=0;
400
        });
401
        return $form;
402
    }
403
}
404