From 45d1f172747ba3854144966a26ae110b402b098f Mon Sep 17 00:00:00 2001 From: Chalom Date: Thu, 16 May 2024 15:56:48 -0300 Subject: [PATCH 1/8] Limites de memoria e CPU, mais small fixes --- app/Http/Controllers/ExercicioController.php | 4 ++++ deploy.sh | 15 +++++++++++---- docker-compose.local-override.yml | 19 ------------------- docker-compose.yml | 1 - docker-deploy.sh | 2 +- docker/R/Dockerfile | 3 ++- docker/R/bootstrap.R | 4 ++++ docker/R/corretor.R | 2 +- 8 files changed, 23 insertions(+), 27 deletions(-) delete mode 100644 docker-compose.local-override.yml create mode 100644 docker/R/bootstrap.R diff --git a/app/Http/Controllers/ExercicioController.php b/app/Http/Controllers/ExercicioController.php index 2fbfed7..089e6ee 100644 --- a/app/Http/Controllers/ExercicioController.php +++ b/app/Http/Controllers/ExercicioController.php @@ -164,6 +164,9 @@ private function corretoR (Exercicio $exercicio, string $file) { $cnx = new Connection('r'); $rcode = 'source("/usr/local/src/notar/corretor.R");' + // Limits memory usage + . 'rlimit_as(1e9);' + . 'rlimit_cpu(15);' // database auth . 'dbusr <- "'. env('DB_USERNAME') . '";' . 'dbpass <- "'. env('DB_PASSWORD') . '";' @@ -174,6 +177,7 @@ private function corretoR (Exercicio $exercicio, string $file) { // run corretoR . 'res <- notaR('. $exercicio->id .',"'.$file.'");' . 'unlink("*",recursive=TRUE);' + // Returns the "res" object to caller . 'res;' ; $r = $cnx->evalString($rcode); diff --git a/deploy.sh b/deploy.sh index 6e230e0..3e4854f 100755 --- a/deploy.sh +++ b/deploy.sh @@ -1,6 +1,13 @@ #!/bin/sh git pull -docker-compose down -docker-compose build -docker-compose up -d -docker exec -t --env-file .env notar_app_1 ./docker-deploy.sh +COMPOSE="docker compose" +CONTAINER="notar-app-1" +if command -v docker-compose; then + COMPOSE="docker-compose" + CONTAINER="notar_app_1" +fi + +$COMPOSE down +$COMPOSE build +$COMPOSE up -d +docker exec -t --env-file .env $CONTAINER ./docker-deploy.sh diff --git a/docker-compose.local-override.yml b/docker-compose.local-override.yml deleted file mode 100644 index a6eedda..0000000 --- a/docker-compose.local-override.yml +++ /dev/null @@ -1,19 +0,0 @@ -# For more information: https://laravel.com/docs/sail -version: '3' -services: - mailhog: - image: 'mailhog/mailhog:latest' - ports: - - 1025:1025 - - 8025:8025 - networks: - - sail - r: - build: - context: ./docker/R/ - dockerfile: Dockerfile - image: r - expose: - - '6311' - networks: - - sail diff --git a/docker-compose.yml b/docker-compose.yml index 82a5822..1a93164 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,4 @@ # For more information: https://laravel.com/docs/sail -version: '3' services: app: build: diff --git a/docker-deploy.sh b/docker-deploy.sh index ce80715..0f602ba 100755 --- a/docker-deploy.sh +++ b/docker-deploy.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Installs and upgrades any new PHP libraries if [ "$APP_ENV" == "production" ]; then diff --git a/docker/R/Dockerfile b/docker/R/Dockerfile index d15e4c5..b4a72ae 100644 --- a/docker/R/Dockerfile +++ b/docker/R/Dockerfile @@ -30,6 +30,7 @@ RUN apt-get update --fix-missing \ RUN install2.r --error --skipinstalled --ncpus -1 \ Rsampling \ EcoVirtual \ + unix \ && rm -rf /tmp/downloaded_packages @@ -37,4 +38,4 @@ RUN install2.r --error --skipinstalled --ncpus -1 \ EXPOSE 6311 # Run the Rserve library: -CMD R --vanilla -e "library(Rserve);" -e "run.Rserve(remote=TRUE);" +CMD Rscript bootstrap.R diff --git a/docker/R/bootstrap.R b/docker/R/bootstrap.R new file mode 100644 index 0000000..b38312a --- /dev/null +++ b/docker/R/bootstrap.R @@ -0,0 +1,4 @@ +library(Rserve); +library(unix); +run.Rserve(remote=TRUE); + diff --git a/docker/R/corretor.R b/docker/R/corretor.R index 6ac6f94..9e3f1c5 100644 --- a/docker/R/corretor.R +++ b/docker/R/corretor.R @@ -72,4 +72,4 @@ notaR <- function (id.exerc, arquivo) { # con <- connect('notaR', 'notaRPW', 'notaR') # corretoR(1, "y<-1;x<-2") # ou -# notaR(1, "/tmp/file.R") \ No newline at end of file +# notaR(1, "/tmp/file.R") From 04eb6acaf9533a2d301466020eca4db90c62d1fa Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Fri, 17 May 2024 16:05:05 -0300 Subject: [PATCH 2/8] added a message just to inform which docker composer is used --- deploy.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/deploy.sh b/deploy.sh index 3e4854f..ed70cd7 100755 --- a/deploy.sh +++ b/deploy.sh @@ -3,6 +3,7 @@ git pull COMPOSE="docker compose" CONTAINER="notar-app-1" if command -v docker-compose; then + echo "Using docker-compose" COMPOSE="docker-compose" CONTAINER="notar_app_1" fi From a34834189c9d94750aac41025b308e1f49a362a3 Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Fri, 17 May 2024 16:05:17 -0300 Subject: [PATCH 3/8] changed order (for caching reasons) --- docker/R/Dockerfile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docker/R/Dockerfile b/docker/R/Dockerfile index b4a72ae..5bd51fe 100644 --- a/docker/R/Dockerfile +++ b/docker/R/Dockerfile @@ -12,12 +12,6 @@ RUN apt-get update --fix-missing \ r-cran-rmysql \ && rm -rf /var/lib/apt/lists/* - -# Copies the required files for notaR -RUN mkdir -p /usr/local/src/notar -COPY . /usr/local/src/notar -WORKDIR /usr/local/src/notar - # Install packages used in exercises RUN apt-get update --fix-missing \ && apt-get install -y --no-install-recommends \ @@ -33,6 +27,10 @@ RUN install2.r --error --skipinstalled --ncpus -1 \ unix \ && rm -rf /tmp/downloaded_packages +# Copies the required files for notaR +RUN mkdir -p /usr/local/src/notar +COPY . /usr/local/src/notar +WORKDIR /usr/local/src/notar # We will listen on 6311 EXPOSE 6311 From 89fdc35fe52fe06355d70a13f322c80098086713 Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Fri, 17 May 2024 16:11:27 -0300 Subject: [PATCH 4/8] fix indent --- app/Http/Controllers/ExercicioController.php | 536 +++++++++---------- 1 file changed, 268 insertions(+), 268 deletions(-) diff --git a/app/Http/Controllers/ExercicioController.php b/app/Http/Controllers/ExercicioController.php index 089e6ee..5798c03 100644 --- a/app/Http/Controllers/ExercicioController.php +++ b/app/Http/Controllers/ExercicioController.php @@ -30,13 +30,13 @@ class ExercicioController extends Controller */ public function index() { - $exercicios = Exercicio::orderBy('name'); - /** @var App\Models\User */ - $user = Auth::user(); - if(!$user || !$user->isAdmin()) { + $exercicios = Exercicio::orderBy('name'); + /** @var App\Models\User */ + $user = Auth::user(); + if (!$user || !$user->isAdmin()) { $exercicios = $exercicios->published(); } - return View('exercicio.index')->with('exercicios',$exercicios->get()); + return View('exercicio.index')->with('exercicios', $exercicios->get()); } /** @@ -47,8 +47,8 @@ public function index() public function create() { $this->authorize('create', Exercicio::class); - return View('exercicio.create')->with('pacotesR',$this->getInstalledPackages()); - } + return View('exercicio.create')->with('pacotesR', $this->getInstalledPackages()); + } /** * Store a newly created resource in storage. @@ -56,15 +56,16 @@ public function create() * @param \Illuminate\Http\Request $request * @return mixed */ - private function validateExercicio (Request $request, Exercicio|Null $exercicio) { + private function validateExercicio(Request $request, Exercicio|null $exercicio) + { $rules = array( - 'name' => 'required|string|unique:exercicios'.($exercicio ? ',name,'.$exercicio->id : ''), - 'description'=> 'required', - 'precondicoes'=>'sometimes', - 'dicas' => 'array', + 'name' => 'required|string|unique:exercicios' . ($exercicio ? ',name,' . $exercicio->id : ''), + 'description' => 'required', + 'precondicoes' => 'sometimes', + 'dicas' => 'array', 'condicoes' => 'array', 'pesos' => 'array', - 'dicas.*' => 'required', + 'dicas.*' => 'required', 'condicoes.*' => 'required', 'pesos.*' => 'required|numeric|min:0', 'draft' => 'required|boolean', @@ -72,11 +73,11 @@ private function validateExercicio (Request $request, Exercicio|Null $exercicio) $data = $request->validate($rules); // corrigir EOL - $data['precondicoes'] = str_replace("\r\n","\n",$data['precondicoes']); - $data['description'] = str_replace("\r\n","\n",$data['description']); + $data['precondicoes'] = str_replace("\r\n", "\n", $data['precondicoes']); + $data['description'] = str_replace("\r\n", "\n", $data['description']); - return $data; - } + return $data; + } /** * Store a newly created resource in storage. @@ -86,46 +87,47 @@ private function validateExercicio (Request $request, Exercicio|Null $exercicio) */ public function store(Request $request) { - $this->authorize('create', Exercicio::class); + $this->authorize('create', Exercicio::class); - $data = $this->validateExercicio($request, null); + $data = $this->validateExercicio($request, null); // store $exercicio = new Exercicio($data); - DB::transaction(function() use ($data, $exercicio) { + DB::transaction(function () use ($data, $exercicio) { $exercicio->save(); $n = count($data['dicas']); for ($i = 0; $i < $n; $i++) { - Teste::create(['condicao' => $data['condicoes'][$i], - 'dica' => $data['dicas'][$i], - 'peso' => $data['pesos'][$i], - 'exercicio_id' => $exercicio->id - ]); + Teste::create([ + 'condicao' => $data['condicoes'][$i], + 'dica' => $data['dicas'][$i], + 'peso' => $data['pesos'][$i], + 'exercicio_id' => $exercicio->id + ]); } - }); + }); - $test = $this->corretoR($exercicio, "/usr/local/src/notar/runtest.R"); - if ($test['status']=='danger') { - return redirect()->action([ExercicioController::class,'edit'],['exercicio' => $exercicio]) - ->withErrors(['precondicoes' => 'Ocorreu um erro ao testar as precondições.']); - } + $test = $this->corretoR($exercicio, "/usr/local/src/notar/runtest.R"); + if ($test['status'] == 'danger') { + return redirect()->action([ExercicioController::class, 'edit'], ['exercicio' => $exercicio]) + ->withErrors(['precondicoes' => 'Ocorreu um erro ao testar as precondições.']); + } - return redirect()->action([ExercicioController::class,'show'],['exercicio' => $exercicio]); + return redirect()->action([ExercicioController::class, 'show'], ['exercicio' => $exercicio]); } /** * Show the profile of a given Exercicio. * - * @param \App\Models\Exercicio $exercicio + * @param \App\Models\Exercicio $exercicio * @return \Illuminate\View\View */ public function show(Exercicio $exercicio) { $this->authorize('view', $exercicio); - $prazo = Auth::user() ? Auth::user()->prazo($exercicio) : false; + $prazo = Auth::user() ? Auth::user()->prazo($exercicio) : false; - return View('exercicio.show')->with('exercicio',$exercicio) - ->with('foraDoPrazo', $prazo ? $prazo->prazo <= now() : false); + return View('exercicio.show')->with('exercicio', $exercicio) + ->with('foraDoPrazo', $prazo ? $prazo->prazo <= now() : false); } /** @@ -133,59 +135,59 @@ public function show(Exercicio $exercicio) * * @return Array */ - private function getInstalledPackages () { - try { - $cnx = new Connection('r'); - - $rcode = 'pkgs <- installed.packages();' - . 'pkgs[,1];' - ; - // resposta do R - $r = $cnx->evalString($rcode); - - return ($r); - } - catch (Exception $e) { - Log::error('Erro de conexão em getInstalledPackages'); - return null; - } + private function getInstalledPackages() + { + try { + $cnx = new Connection('r'); + + $rcode = 'pkgs <- installed.packages();' + . 'pkgs[,1];' + ; + // resposta do R + $r = $cnx->evalString($rcode); + + return ($r); + } catch (Exception $e) { + Log::error('Erro de conexão em getInstalledPackages'); + return null; + } } /** * Roda o corretoR usando o código * * @param Exercicio $exercicio - * @param string $codigo + * @param string $codigo * @return Array */ - private function corretoR (Exercicio $exercicio, string $file) { + private function corretoR(Exercicio $exercicio, string $file) + { // resposta do R try { $cnx = new Connection('r'); $rcode = 'source("/usr/local/src/notar/corretor.R");' - // Limits memory usage - . 'rlimit_as(1e9);' - . 'rlimit_cpu(15);' - // database auth - . 'dbusr <- "'. env('DB_USERNAME') . '";' - . 'dbpass <- "'. env('DB_PASSWORD') . '";' - . 'dbname <- "'. env('DB_DATABASE') . '";' - . 'con <- connect(dbusr, dbpass, dbname);' - // import files - . 'file.copy(list.files("/arquivos/",recursive=TRUE,full.names=TRUE),".");' - // run corretoR - . 'res <- notaR('. $exercicio->id .',"'.$file.'");' - . 'unlink("*",recursive=TRUE);' - // Returns the "res" object to caller - . 'res;' - ; + // Limits memory usage + . 'rlimit_as(1e9);' + . 'rlimit_cpu(15);' + // database auth + . 'dbusr <- "' . env('DB_USERNAME') . '";' + . 'dbpass <- "' . env('DB_PASSWORD') . '";' + . 'dbname <- "' . env('DB_DATABASE') . '";' + . 'con <- connect(dbusr, dbpass, dbname);' + // import files + . 'file.copy(list.files("/arquivos/",recursive=TRUE,full.names=TRUE),".");' + // run corretoR + . 'res <- notaR(' . $exercicio->id . ',"' . $file . '");' + . 'unlink("*",recursive=TRUE);' + // Returns the "res" object to caller + . 'res;' + ; $r = $cnx->evalString($rcode); - } - catch (RserveException $e){ + } catch (RserveException $e) { return [ 'status' => 'danger', - 'mensagem' => 'Ocorreu um erro na correção do exercício! Por favor verifique seu código ou contate um administrador.' , + 'mensagem' => 'Ocorreu um erro na correção do exercício! Por favor verifique seu código ou contate um administrador.', 'resultado' => null, 'nota' => 0 ]; @@ -195,23 +197,23 @@ private function corretoR (Exercicio $exercicio, string $file) { if ($r === null) { return [ 'status' => 'danger', - 'mensagem' => 'Ocorreu um erro na execução do seu código! Corrija e tente novamente.' , + 'mensagem' => 'Ocorreu um erro na execução do seu código! Corrija e tente novamente.', 'resultado' => $r, 'nota' => 0 ]; } // garantee that $r is an array ffs - if(is_bool($r)) { + if (is_bool($r)) { $r = [$r]; } // 100% - if(in_array(false, $r, true) === false) { + if (in_array(false, $r, true) === false) { return [ 'status' => 'success', 'mensagem' => 'Parabéns! Seu código passou em todos os testes!
' - . 'Toca aqui!', + . 'Toca aqui!', 'resultado' => $r, 'nota' => 100 ]; @@ -222,17 +224,16 @@ private function corretoR (Exercicio $exercicio, string $file) { // calcula nota $nota = 0; $firstmistake = -1; - for($i = 0; $ipeso; - } - else if ($firstmistake == -1) { + } else if ($firstmistake == -1) { $firstmistake = $i; } } $dica = $testes[$firstmistake]->dica; $notamax = $testes->sum('peso'); - $notanormalizada = 100*$nota/$notamax; + $notanormalizada = 100 * $nota / $notamax; return [ 'status' => 'warning', @@ -247,7 +248,7 @@ private function corretoR (Exercicio $exercicio, string $file) { * Ação de fazer exercício usando o form * * @param \Illuminate\Http\Request $request - * @param \App\Models\Exercicio $exercicio + * @param \App\Models\Exercicio $exercicio * @return \Illuminate\View\View */ public function submit(Request $request, Exercicio $exercicio) @@ -257,15 +258,15 @@ public function submit(Request $request, Exercicio $exercicio) ); $data = $request->validate($rules); - $validator = Validator::make($request->all(), $rules); - return $this->recebeCodigo($data['codigo'], $exercicio, $validator); - } + $validator = Validator::make($request->all(), $rules); + return $this->recebeCodigo($data['codigo'], $exercicio, $validator); + } /** * Ação de fazer exercício usando file upload * * @param \Illuminate\Http\Request $request - * @param \App\Models\Exercicio $exercicio + * @param \App\Models\Exercicio $exercicio * @return \Illuminate\View\View */ public function upload(Request $request, Exercicio $exercicio) @@ -277,259 +278,258 @@ public function upload(Request $request, Exercicio $exercicio) $validator = Validator::make($request->all(), $rules); - $codigo = $request->file('file')->get(); - // convert to - $codigo = Encoding::toUTF8($codigo); - return $this->recebeCodigo($codigo, $exercicio, $validator); + $codigo = $request->file('file')->get(); + // convert to + $codigo = Encoding::toUTF8($codigo); + return $this->recebeCodigo($codigo, $exercicio, $validator); } - private function recebeCodigo (String $codigo, Exercicio $exercicio, \Illuminate\Contracts\Validation\Validator $validator) { + private function recebeCodigo(string $codigo, Exercicio $exercicio, \Illuminate\Contracts\Validation\Validator $validator) + { - $user = Auth::user(); - if($user) { - Log::info('User '.$user->id.' submitted an answer to exercise '.$exercicio->id); - } - else { - Log::info('Guest submitted an answer to exercise '.$exercicio->id); - } + $user = Auth::user(); + if ($user) { + Log::info('User ' . $user->id . ' submitted an answer to exercise ' . $exercicio->id); + } else { + Log::info('Guest submitted an answer to exercise ' . $exercicio->id); + } - $validator->after(function ($validator) use($codigo) { - foreach(Impedimento::all()->pluck('palavra') as $palavra) { - if (Str::contains($codigo,$palavra)) { + $validator->after(function ($validator) use ($codigo) { + foreach (Impedimento::all()->pluck('palavra') as $palavra) { + if (Str::contains($codigo, $palavra)) { $validator->errors()->add( - 'codigo', 'Código não pode conter a palavra: '.$palavra + 'codigo', + 'Código não pode conter a palavra: ' . $palavra ); } } }); - if ($validator->fails()) { - return redirect(URL::to('exercicio/'.$exercicio->id)) - ->withErrors($validator) - ->withInput(); - } + if ($validator->fails()) { + return redirect(URL::to('exercicio/' . $exercicio->id)) + ->withErrors($validator) + ->withInput(); + } // corrigir EOL - $codigo = str_replace("\r\n","\n",$codigo); + $codigo = str_replace("\r\n", "\n", $codigo); // salva um arquivo com o codigo $tempfile = TmpFile::generateTmpFileName(md5($codigo), '.R'); Storage::put($tempfile, $codigo); // corrige - $respostaR = $this->corretoR($exercicio,$tempfile); + $respostaR = $this->corretoR($exercicio, $tempfile); // deleta arquivo temporário Storage::delete($tempfile); // salvar nota no banco de dados - if($user && !$exercicio->draft) { + if ($user && !$exercicio->draft) { $exercicio->notas()->create([ 'nota' => $respostaR['nota'], 'user_id' => $user->id, 'testes' => $respostaR['resultado'], 'codigo' => $codigo ]); - } + } - $prazo = $user ? $user->prazo($exercicio) : false; + $prazo = $user ? $user->prazo($exercicio) : false; return View('exercicio.show')->with('exercicio', $exercicio) - ->with('foraDoPrazo', $prazo ? $prazo->prazo <= now() : false) - ->with('respostaR', $respostaR) - ->with('codigo', $codigo); + ->with('foraDoPrazo', $prazo ? $prazo->prazo <= now() : false) + ->with('respostaR', $respostaR) + ->with('codigo', $codigo); } /** * Show the form for editing the specified resource. * - * @param \App\Models\Exercicio $exercicio + * @param \App\Models\Exercicio $exercicio * @return \Illuminate\Http\Response */ public function edit(Exercicio $exercicio) { $this->authorize('edit', $exercicio); - return View('exercicio.edit')->with('exercicio',$exercicio)->with('exercicio.testes',$exercicio->testes)->with('pacotesR',$this->getInstalledPackages()); + return View('exercicio.edit')->with('exercicio', $exercicio)->with('exercicio.testes', $exercicio->testes)->with('pacotesR', $this->getInstalledPackages()); } - /** - * Update the specified resource in storage. - * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Exercicio $exercicio - * @return \Illuminate\Http\Response - */ - public function update(Request $request, Exercicio $exercicio) - { - $this->authorize('edit',$exercicio); - $data = $this->validateExercicio($request, $exercicio); + /** + * Update the specified resource in storage. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Exercicio $exercicio + * @return \Illuminate\Http\Response + */ + public function update(Request $request, Exercicio $exercicio) + { + $this->authorize('edit', $exercicio); + $data = $this->validateExercicio($request, $exercicio); // store - DB::transaction(function() use ($data, $exercicio) { + DB::transaction(function () use ($data, $exercicio) { $exercicio->update($data); $exercicio->testes()->delete(); // delete all testes because we're lazy $n = count($data['dicas']); for ($i = 0; $i < $n; $i++) { - $teste = Teste::create(['condicao' => $data['condicoes'][$i], - 'dica' => $data['dicas'][$i], - 'peso' => $data['pesos'][$i], - 'exercicio_id' => $exercicio->id - ]); + $teste = Teste::create([ + 'condicao' => $data['condicoes'][$i], + 'dica' => $data['dicas'][$i], + 'peso' => $data['pesos'][$i], + 'exercicio_id' => $exercicio->id + ]); } }); - // testa as preconds - $test = $this->corretoR($exercicio, "/usr/local/src/notar/runtest.R"); - if ($test['status']=='danger') { - return redirect()->action([ExercicioController::class,'edit'],['exercicio' => $exercicio]) - ->withErrors(['precondicoes' => 'Ocorreu um erro ao testar as precondições.']); - } + // testa as preconds + $test = $this->corretoR($exercicio, "/usr/local/src/notar/runtest.R"); + if ($test['status'] == 'danger') { + return redirect()->action([ExercicioController::class, 'edit'], ['exercicio' => $exercicio]) + ->withErrors(['precondicoes' => 'Ocorreu um erro ao testar as precondições.']); + } - return redirect()->action([ExercicioController::class,'show'],['exercicio' => $exercicio]); - } + return redirect()->action([ExercicioController::class, 'show'], ['exercicio' => $exercicio]); + } /** * Export to text file * - * @param int $id + * @param int $id * @return \Illuminate\Http\Response */ public function export(int $id) { - $exercicio = Exercicio::with('testes')->find($id); - $this->authorize('edit', $exercicio); - - try - { - $filename = TmpFile::generateTmpFileName('notaR_exercicio'.$exercicio->name,'.yaml'); - Storage::put($filename, $exercicio->export()); - } - catch (Exception $e) - { - return back()->withErrors('Erro ao exportar exercício.'); - }; + $exercicio = Exercicio::with('testes')->find($id); + $this->authorize('edit', $exercicio); + + try { + $filename = TmpFile::generateTmpFileName('notaR_exercicio' . $exercicio->name, '.yaml'); + Storage::put($filename, $exercicio->export()); + } catch (Exception $e) { + return back()->withErrors('Erro ao exportar exercício.'); + } + ; return response()->download($filename)->deleteFileAfterSend(true); - } - - /** Export all exercicios - * - * @return \Illuminate\Http\Response - */ - public function exportAll () - { - $this->authorize('bulk', Exercicio::class); - - // Create files for each model - // Create a file containing all of that - $exercicios = Exercicio::with('testes')->get(); - $filename = TmpFile::generateTmpFileName('notaR_exercicios','.zip'); - - try - { - $zip = new ZipArchive; - if ($zip->open($filename, ZipArchive::CREATE) === TRUE) { - foreach ($exercicios as $key => $value) { - $fn = Str::slug($value->name).'.yaml'; - $zip->addFromString($fn, $value->export()); - } - $zip->close(); - } - } - catch (Exception $e) - { - return back()->withErrors('Erro ao exportar exercícios.'); - }; - return response()->download($filename)->deleteFileAfterSend(true); - } - - private function importInput ($data) { - $input = ['from_import' => true]; - if (property_exists($data,'testes')) { - $testes = collect($data->testes); - $input = [ - 'dicas' => $testes->pluck('dica'), - 'condicoes' => $testes->pluck('condicao'), - 'pesos' => $testes->pluck('peso'), - 'from_import' => true - ]; - } - - if (property_exists($data, 'name')) { - $input['name'] = $data->name; - } - - if (property_exists($data, 'description')) { - $input['description'] = $data->description; - } - - if (property_exists($data, 'precondicoes')) { - $input['precondicoes'] = $data->precondicoes; - } - - return $input; - } + } + + /** Export all exercicios + * + * @return \Illuminate\Http\Response + */ + public function exportAll() + { + $this->authorize('bulk', Exercicio::class); + + // Create files for each model + // Create a file containing all of that + $exercicios = Exercicio::with('testes')->get(); + $filename = TmpFile::generateTmpFileName('notaR_exercicios', '.zip'); + + try { + $zip = new ZipArchive; + if ($zip->open($filename, ZipArchive::CREATE) === TRUE) { + foreach ($exercicios as $key => $value) { + $fn = Str::slug($value->name) . '.yaml'; + $zip->addFromString($fn, $value->export()); + } + $zip->close(); + } + } catch (Exception $e) { + return back()->withErrors('Erro ao exportar exercícios.'); + } + ; + return response()->download($filename)->deleteFileAfterSend(true); + } + + private function importInput($data) + { + $input = ['from_import' => true]; + if (property_exists($data, 'testes')) { + $testes = collect($data->testes); + $input = [ + 'dicas' => $testes->pluck('dica'), + 'condicoes' => $testes->pluck('condicao'), + 'pesos' => $testes->pluck('peso'), + 'from_import' => true + ]; + } + + if (property_exists($data, 'name')) { + $input['name'] = $data->name; + } + + if (property_exists($data, 'description')) { + $input['description'] = $data->description; + } + + if (property_exists($data, 'precondicoes')) { + $input['precondicoes'] = $data->precondicoes; + } + + return $input; + } /** * Import from yaml file * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Exercicio $exercicio - * @return \Illuminate\Http\Response - */ - public function importEdit (Request $request, Exercicio $exercicio) + * @param \Illuminate\Http\Request $request + * @param \App\Models\Exercicio $exercicio + * @return \Illuminate\Http\Response + */ + public function importEdit(Request $request, Exercicio $exercicio) { - $this->authorize('edit', $exercicio); - $request->validate([ - 'file' => 'required', - ]); + $this->authorize('edit', $exercicio); + $request->validate([ + 'file' => 'required', + ]); - $j = $request->file('file')->get(); + $j = $request->file('file')->get(); - $data = Yaml::parse($j); - if( is_null($data)) { - return redirect()->action([ExercicioController::class,'edit'],['exercicio' => $exercicio]) - ->withErrors(['file' => 'O arquivo enviado não é um json válido']); - } + $data = Yaml::parse($j); + if (is_null($data)) { + return redirect()->action([ExercicioController::class, 'edit'], ['exercicio' => $exercicio]) + ->withErrors(['file' => 'O arquivo enviado não é um json válido']); + } - return redirect()->action([ExercicioController::class,'edit'],['exercicio' => $exercicio]) - ->withInput($this->importInput((object)$data)); - } + return redirect()->action([ExercicioController::class, 'edit'], ['exercicio' => $exercicio]) + ->withInput($this->importInput((object) $data)); + } /** * Import from json file * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\Response - */ - public function import (Request $request) + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\Response + */ + public function import(Request $request) { - $this->authorize('create', Exercicio::class ); - $request->validate([ - 'file' => 'required', - ]); - - $j = $request->file('file')->get(); - - $data = Yaml::parse($j); - if( is_null($data)) { - return redirect()->action([ExercicioController::class,'create']) - ->withErrors(['file' => 'O arquivo enviado não é um yaml válido']); - } - - return redirect()->action([ExercicioController::class,'create']) - ->withInput($this->importInput((object) $data)); - } - - - /** - * Remove the specified resource from storage. - * - * @param \App\Models\Exercicio $exercicio - * @return \Illuminate\Http\Response - */ - public function destroy(Exercicio $exercicio) - { - $this->authorize('delete', $exercicio); - $exercicio->testes()->delete(); - $exercicio->delete(); - return redirect()->action([ExercicioController::class,'index']); - } + $this->authorize('create', Exercicio::class); + $request->validate([ + 'file' => 'required', + ]); + + $j = $request->file('file')->get(); + + $data = Yaml::parse($j); + if (is_null($data)) { + return redirect()->action([ExercicioController::class, 'create']) + ->withErrors(['file' => 'O arquivo enviado não é um yaml válido']); + } + + return redirect()->action([ExercicioController::class, 'create']) + ->withInput($this->importInput((object) $data)); + } + + + /** + * Remove the specified resource from storage. + * + * @param \App\Models\Exercicio $exercicio + * @return \Illuminate\Http\Response + */ + public function destroy(Exercicio $exercicio) + { + $this->authorize('delete', $exercicio); + $exercicio->testes()->delete(); + $exercicio->delete(); + return redirect()->action([ExercicioController::class, 'index']); + } } From 6d48a9fb73b4d779f5a682d33003761324edf872 Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Fri, 17 May 2024 18:04:46 -0300 Subject: [PATCH 5/8] rlimit was impeding the loading of packages --- app/Http/Controllers/ExercicioController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ExercicioController.php b/app/Http/Controllers/ExercicioController.php index b1ec170..3533f31 100644 --- a/app/Http/Controllers/ExercicioController.php +++ b/app/Http/Controllers/ExercicioController.php @@ -171,9 +171,6 @@ private function corretoR(Exercicio $exercicio, string $file) $cnx = new Connection('r'); $rcode = 'source("/usr/local/src/notar/corretor.R");' - // Limits memory usage - . 'rlimit_as(1e9);' - . 'rlimit_cpu(15);' // database auth . 'dbusr <- "' . env('DB_USERNAME') . '";' . 'dbpass <- "' . env('DB_PASSWORD') . '";' @@ -181,6 +178,9 @@ private function corretoR(Exercicio $exercicio, string $file) . 'con <- connect(dbusr, dbpass, dbname);' // import files . 'file.copy(list.files("/arquivos/",recursive=TRUE,full.names=TRUE),".");' + // Limits memory usage + . 'rlimit_as(1e9);' + . 'rlimit_cpu(15);' // run corretoR . 'res <- notaR(' . $exercicio->id . ',"' . $file . '");' . 'unlink("*",recursive=TRUE);' From 452fcd6e296ce4bf8ed149de24116e73b5b91413 Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Fri, 17 May 2024 18:10:16 -0300 Subject: [PATCH 6/8] minor linting --- app/Http/Controllers/ExercicioController.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ExercicioController.php b/app/Http/Controllers/ExercicioController.php index 3533f31..de117db 100644 --- a/app/Http/Controllers/ExercicioController.php +++ b/app/Http/Controllers/ExercicioController.php @@ -347,12 +347,15 @@ private function recebeCodigo(string $codigo, Exercicio $exercicio, \Illuminate\ * Show the form for editing the specified resource. * * @param \App\Models\Exercicio $exercicio - * @return \Illuminate\Http\Response + * @return \Illuminate\Contracts\View\View */ public function edit(Exercicio $exercicio) { $this->authorize('edit', $exercicio); - return View('exercicio.edit')->with('exercicio', $exercicio)->with('exercicio.testes', $exercicio->testes)->with('pacotesR', $this->getInstalledPackages()); + return View('exercicio.edit') + ->with('exercicio', $exercicio) + ->with('exercicio.testes', $exercicio->testes) + ->with('pacotesR', $this->getInstalledPackages()); } /** @@ -368,7 +371,7 @@ public function update(Request $request, Exercicio $exercicio) $data = $this->validateExercicio($request, $exercicio); // store - DB::transaction(function () use ($data, $exercicio) { + DB::transaction(function () use ($data, $exercicio) { $exercicio->update($data); $exercicio->testes()->delete(); // delete all testes because we're lazy $n = count($data['dicas']); From 9f7760ecb1aa1a543f49923b0404913097376cde Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Fri, 17 May 2024 19:26:09 -0300 Subject: [PATCH 7/8] Catch all exceptions (even ones Rserve doesn't catch) --- app/Http/Controllers/ExercicioController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ExercicioController.php b/app/Http/Controllers/ExercicioController.php index de117db..c720fcf 100644 --- a/app/Http/Controllers/ExercicioController.php +++ b/app/Http/Controllers/ExercicioController.php @@ -179,8 +179,8 @@ private function corretoR(Exercicio $exercicio, string $file) // import files . 'file.copy(list.files("/arquivos/",recursive=TRUE,full.names=TRUE),".");' // Limits memory usage - . 'rlimit_as(1e9);' - . 'rlimit_cpu(15);' + . 'rlimit_as(1e10);' + . 'rlimit_cpu(1);' // run corretoR . 'res <- notaR(' . $exercicio->id . ',"' . $file . '");' . 'unlink("*",recursive=TRUE);' @@ -188,7 +188,7 @@ private function corretoR(Exercicio $exercicio, string $file) . 'res;' ; $r = $cnx->evalString($rcode); - } catch (RserveException $e) { + } catch (Exception $e) { return [ 'status' => 'danger', 'mensagem' => 'Ocorreu um erro na correção do exercício! Por favor verifique seu código ou contate um administrador.', From 6c0e1a43abf9a5a18d248fbe002caee84a72d933 Mon Sep 17 00:00:00 2001 From: Mali Oz Date: Mon, 20 May 2024 17:32:17 -0300 Subject: [PATCH 8/8] source corretor now in R layer --- app/Http/Controllers/ExercicioController.php | 4 ++-- docker/R/bootstrap.R | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Http/Controllers/ExercicioController.php b/app/Http/Controllers/ExercicioController.php index c720fcf..2a6aaa0 100644 --- a/app/Http/Controllers/ExercicioController.php +++ b/app/Http/Controllers/ExercicioController.php @@ -170,7 +170,7 @@ private function corretoR(Exercicio $exercicio, string $file) try { $cnx = new Connection('r'); - $rcode = 'source("/usr/local/src/notar/corretor.R");' + $rcode = '' // database auth . 'dbusr <- "' . env('DB_USERNAME') . '";' . 'dbpass <- "' . env('DB_PASSWORD') . '";' @@ -180,7 +180,7 @@ private function corretoR(Exercicio $exercicio, string $file) . 'file.copy(list.files("/arquivos/",recursive=TRUE,full.names=TRUE),".");' // Limits memory usage . 'rlimit_as(1e10);' - . 'rlimit_cpu(1);' + . 'rlimit_cpu(15);' // run corretoR . 'res <- notaR(' . $exercicio->id . ',"' . $file . '");' . 'unlink("*",recursive=TRUE);' diff --git a/docker/R/bootstrap.R b/docker/R/bootstrap.R index b38312a..4c33f90 100644 --- a/docker/R/bootstrap.R +++ b/docker/R/bootstrap.R @@ -1,4 +1,4 @@ library(Rserve); library(unix); +source("/usr/local/src/notar/corretor.R"); run.Rserve(remote=TRUE); -