create: new folder
This commit is contained in:
parent
5f4b02868d
commit
de5ff91f97
18
iCLOP-V2-NEW/iCLOP-V2-NEW/.editorconfig
Normal file
18
iCLOP-V2-NEW/iCLOP-V2-NEW/.editorconfig
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
65
iCLOP-V2-NEW/iCLOP-V2-NEW/.env.example
Normal file
65
iCLOP-V2-NEW/iCLOP-V2-NEW/.env.example
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
APP_NAME=iCLOP
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
NODEJS_DB_HOST=127.0.0.1
|
||||
NODEJS_DB_PORT=3306
|
||||
NODEJS_DB_DATABASE=iclop_nodejs
|
||||
NODEJS_DB_USERNAME=root
|
||||
NODEJS_DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailpit
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_HOST=
|
||||
PUSHER_PORT=443
|
||||
PUSHER_SCHEME=https
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
11
iCLOP-V2-NEW/iCLOP-V2-NEW/.gitattributes
vendored
Normal file
11
iCLOP-V2-NEW/iCLOP-V2-NEW/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
19
iCLOP-V2-NEW/iCLOP-V2-NEW/.gitignore
vendored
Normal file
19
iCLOP-V2-NEW/iCLOP-V2-NEW/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/.phpunit.cache
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/vendor
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpunit.result.cache
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
auth.json
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/.fleet
|
||||
/.idea
|
||||
/.vscode
|
||||
66
iCLOP-V2-NEW/iCLOP-V2-NEW/README.md
Normal file
66
iCLOP-V2-NEW/iCLOP-V2-NEW/README.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Learning Laravel
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 2000 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||
|
||||
### Premium Partners
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[Many](https://www.many.co.uk)**
|
||||
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||
- **[DevSquad](https://devsquad.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
|
||||
- **[Lendio](https://lendio.com)**
|
||||
|
||||
## Contributing
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
## Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Task;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class MigratePdfData extends Command
|
||||
{
|
||||
protected $signature = 'migrate:pdf-data';
|
||||
protected $description = 'Migrate PDF data from material column to pdf_path';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$tasks = Task::whereNotNull('material')->get();
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
// Ekstrak nama file dari string PDF
|
||||
preg_match('/(\w+\.pdf)/', $task->material, $matches);
|
||||
if (isset($matches[1])) {
|
||||
$filename = $matches[1];
|
||||
// Simpan string PDF sebagai file
|
||||
Storage::put('public/pdfs/' . $filename, $task->material);
|
||||
// Update record task
|
||||
$task->update(['pdf_path' => 'pdfs/' . $filename]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->info('PDF data migration completed.');
|
||||
}
|
||||
}
|
||||
27
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Console/Kernel.php
Normal file
27
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Console/Kernel.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
// $schedule->command('inspire')->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*/
|
||||
protected function commands(): void
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
30
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Exceptions/Handler.php
Normal file
30
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Exceptions/Handler.php
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* The list of the inputs that are never flashed to the session on validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
//
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,844 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Android;
|
||||
|
||||
use App\Models\Android\Topic;
|
||||
use App\Models\User;
|
||||
use App\Models\Android\Task;
|
||||
use App\Models\Android\Task_waiting;
|
||||
use App\Models\Android\Enrollment;
|
||||
use App\Models\Android\SubmitstestCase;
|
||||
use App\Models\Android\SubmitsFinalSubmission;
|
||||
use App\Models\Android\Submits;
|
||||
use App\Models\Android\Testcase;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use DB;
|
||||
|
||||
class AndroidController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
- LECTURER
|
||||
*/
|
||||
public function lecturer_material(){
|
||||
|
||||
$dt_keseluruhan = array();
|
||||
$topic = Topic::all();
|
||||
|
||||
$notifikasi = $this->notify_validator();
|
||||
|
||||
$total_mhs = 0;
|
||||
|
||||
if ( $topic->count() > 0 ) {
|
||||
|
||||
foreach ( $topic AS $isi ) {
|
||||
|
||||
// count data task
|
||||
$isi->total = Task::where('android_topic_id', $isi->id)->count();
|
||||
|
||||
$total_mhs_bytopik = Enrollment::where('android_topic_id', $isi->id)->whereNotIn('status', ['cancel'])->get()->count();
|
||||
$isi->enroll = $total_mhs_bytopik;
|
||||
|
||||
array_push( $dt_keseluruhan, $isi );
|
||||
|
||||
|
||||
$total_mhs = $total_mhs + $total_mhs_bytopik;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return view('android.teacher.material', compact('dt_keseluruhan', 'total_mhs', 'notifikasi'));
|
||||
}
|
||||
|
||||
|
||||
public function lecturer_overview( $id ){
|
||||
|
||||
$topic = Topic::findOrFail( $id );
|
||||
$task = Task::where('android_topic_id', $id)->get();
|
||||
|
||||
|
||||
$dt_enrollment = array();
|
||||
$enrollment = Enrollment::where('android_topic_id', $id)->get();
|
||||
|
||||
foreach ( $enrollment As $isi ) {
|
||||
|
||||
// informasi validation
|
||||
$total_request = 0;
|
||||
$total_validate = 0;
|
||||
$user_id = $isi->user->id;
|
||||
|
||||
|
||||
|
||||
// ambil informasi data testcase
|
||||
$where = array(
|
||||
|
||||
'user_id' => $user_id,
|
||||
'android_topic_id' => $id
|
||||
);
|
||||
$submit = Submits::where($where)->get();
|
||||
|
||||
|
||||
// - - - - - - - -
|
||||
$NA = 0;
|
||||
$total_submit = $submit->count();
|
||||
|
||||
// - - - - - - - -
|
||||
|
||||
if ( $submit->count() > 0 ) {
|
||||
|
||||
|
||||
$total_NA = 0;
|
||||
foreach ( $submit AS $isi_s ) {
|
||||
|
||||
// ambil data testcase
|
||||
$SubmitstestCase = SubmitstestCase::where("android_submit_id", $isi_s->id)->get();
|
||||
$isi->testcase = $SubmitstestCase;
|
||||
|
||||
|
||||
|
||||
// auto
|
||||
$where_passed = [
|
||||
|
||||
'android_submit_id' => $isi_s->id,
|
||||
'status_validate' => "passed"
|
||||
];
|
||||
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||
|
||||
// ambil informasi data task untuk memanggil bobot
|
||||
$info_testcase = Testcase::where("task_id", $isi_s->android_task_id)->get();
|
||||
|
||||
$bobot = 0;
|
||||
foreach ( $info_testcase AS $isiit ) {
|
||||
|
||||
$bobot += $isiit->score;
|
||||
}
|
||||
|
||||
$skor = 0;
|
||||
foreach ( $test_passed AS $isitp ) {
|
||||
|
||||
$skor += $isitp->score;
|
||||
}
|
||||
|
||||
$nilai = 0;
|
||||
if ( $skor > 0 && $bobot > 0 ) {
|
||||
|
||||
$nilai = $skor / $bobot * 100;
|
||||
}
|
||||
|
||||
$total_NA += $nilai;
|
||||
|
||||
|
||||
|
||||
// autograding
|
||||
// $passed = 0;
|
||||
// foreach ( $SubmitstestCase AS $det ) {
|
||||
|
||||
// if ( $det->status_validate == "passed" ) {
|
||||
|
||||
// $passed++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $total_NA += ($passed / $SubmitstestCase->count() * 100);
|
||||
}
|
||||
|
||||
// echo $total_submit;
|
||||
$NA = $total_NA / $total_submit;
|
||||
|
||||
|
||||
$total_request = $submit->count();
|
||||
foreach ( $submit AS $det ) {
|
||||
|
||||
if ( $det->validator == "validated" ) $total_validate++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$progress = 0;
|
||||
$total_task = Task::where("android_topic_id", $isi->android_topic_id)->count();
|
||||
$status = $isi->status;
|
||||
|
||||
// check progress
|
||||
if ( $status != "cancel") {
|
||||
|
||||
$task_waiting = Task_waiting::where( $where );
|
||||
$total_task_waiting = $task_waiting->count();
|
||||
|
||||
if ( $total_task_waiting > 0 ) {
|
||||
|
||||
$info = Task::find( $task_waiting->first()->android_task_id );
|
||||
$progress = $total_task_waiting / $total_task * 100;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$isi->total_request = $total_request;
|
||||
$isi->total_validate = $total_validate;
|
||||
$isi->NA = $NA;
|
||||
$isi->progress = $progress;
|
||||
|
||||
|
||||
array_push( $dt_enrollment, $isi );
|
||||
}
|
||||
|
||||
$data = array(
|
||||
|
||||
'task' => $task,
|
||||
'topic' => $topic,
|
||||
'enrollment' => $dt_enrollment
|
||||
);
|
||||
// print_r( $Enrollment->user->name );
|
||||
|
||||
return view('android.teacher.overview', $data);
|
||||
}
|
||||
|
||||
|
||||
public function lecturer_waiting() {
|
||||
|
||||
// ambil data
|
||||
$dt_keseluruhan = array();
|
||||
$topic = Topic::all();
|
||||
|
||||
$notifikasi = $this->notify_validator();
|
||||
|
||||
$total_mhs = 0;
|
||||
|
||||
if ( $topic->count() > 0 ) {
|
||||
|
||||
foreach ( $topic AS $isi ) {
|
||||
|
||||
// count data task
|
||||
$isi->total = Task::where('android_topic_id', $isi->id)->count();
|
||||
|
||||
$total_mhs_bytopik = Enrollment::where('android_topic_id', $isi->id)->whereNotIn('status', ['cancel'])->get()->count();
|
||||
$isi->enroll = $total_mhs_bytopik;
|
||||
|
||||
array_push( $dt_keseluruhan, $isi );
|
||||
|
||||
|
||||
$total_mhs = $total_mhs + $total_mhs_bytopik;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cek */
|
||||
$submits = Submits::where('validator', 'process')->get();
|
||||
$dt_need_validator = array();
|
||||
|
||||
if ( $submits->count() > 0 ) {
|
||||
|
||||
// group by
|
||||
$dt_submit_topic = array();
|
||||
foreach ( $submits AS $index => $isi ) {
|
||||
|
||||
if ( $index > 0 ) {
|
||||
|
||||
$find = in_array($isi->android_topic_id, array_column($dt_submit_topic, 'android_topic_id'));
|
||||
if ( $find == false ) {
|
||||
|
||||
array_push( $dt_submit_topic, [
|
||||
'android_topic_id' => $isi->android_topic_id,
|
||||
]);
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
|
||||
array_push( $dt_submit_topic, [
|
||||
'android_topic_id' => $isi->android_topic_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// akumulasi mahasiswa
|
||||
foreach ( $dt_submit_topic AS $isi ) {
|
||||
|
||||
$where = array(
|
||||
|
||||
'android_topic_id' => $isi,
|
||||
'validator' => "process"
|
||||
);
|
||||
$all_mhs = Submits::where($where)->get();
|
||||
$jumlah = $all_mhs->count();
|
||||
|
||||
$dt_all_mahasiswa_by_topic = array();
|
||||
|
||||
|
||||
$title = "";
|
||||
if ( $jumlah > 0 ) {
|
||||
|
||||
foreach ( $all_mhs AS $mhs ) {
|
||||
|
||||
$title = $mhs->topic->title;
|
||||
array_push( $dt_all_mahasiswa_by_topic, $mhs );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// push to validator
|
||||
array_push( $dt_need_validator, [
|
||||
|
||||
'android_topic_id' => $isi,
|
||||
'title' => $title,
|
||||
'jumlah' => $jumlah,
|
||||
'all_mhs' => $dt_all_mahasiswa_by_topic
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return view('android.teacher.validator_waiting', compact('notifikasi', 'total_mhs', 'dt_need_validator'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// detail task
|
||||
public function lecturer_waiting_preview( $id_submit ) {
|
||||
|
||||
$submit = Submits::findOrFail( $id_submit );
|
||||
// $testcase = SubmitstestCase::where("android_submit_id", $id_submit)->->get();
|
||||
$testcase = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where("android_submit_id", $id_submit)->select("android_submits_testcase.*", "case", "score")->get();
|
||||
|
||||
// echo json_encode($testcase);
|
||||
return view('android.teacher.validator_detail', compact('submit', 'testcase'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// do validate
|
||||
public function lecturer_do_validate( $id_testcase, $android_submit_id ) {
|
||||
|
||||
|
||||
// update all status waiting to failed (default)
|
||||
$where = array(
|
||||
|
||||
'android_submit_id' => $android_submit_id,
|
||||
'status_validate' => "waiting"
|
||||
);
|
||||
SubmitstestCase::where($where)->update(['status_validate' => "failed"]);
|
||||
|
||||
$submittestcase = SubmitstestCase::findOrFail( $id_testcase );
|
||||
$submit = Submits::findOrFail( $android_submit_id );
|
||||
|
||||
$submit->validator = "validated";
|
||||
$submit->save();
|
||||
|
||||
|
||||
if ( $submittestcase->status_validate == "failed" ) {
|
||||
$submittestcase->status_validate = "passed";
|
||||
} else if ( $submittestcase->status_validate == "passed" ) {
|
||||
$submittestcase->status_validate = "failed";
|
||||
}
|
||||
|
||||
$submittestcase->save();
|
||||
|
||||
|
||||
// echo $;
|
||||
// return "oke";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function lecturer_load_point_testcase( $android_submit_id ) {
|
||||
|
||||
|
||||
|
||||
$where = [
|
||||
|
||||
'android_submit_id' => $android_submit_id,
|
||||
'status_validate' => "passed"
|
||||
];
|
||||
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where)->get();
|
||||
|
||||
|
||||
// ambil data submit berdasarkan id
|
||||
$submit = Submits::findOrFail( $android_submit_id );
|
||||
$task = Task::findOrFail( $submit->android_task_id );
|
||||
|
||||
$info_testcase = Testcase::where("task_id", $task->id)->get();
|
||||
|
||||
$bobot = 0;
|
||||
foreach ( $info_testcase AS $isi ) {
|
||||
|
||||
$bobot += $isi->score;
|
||||
}
|
||||
|
||||
|
||||
$skor = 0;
|
||||
foreach ( $test_passed AS $isi ) {
|
||||
|
||||
$skor += $isi->score;
|
||||
}
|
||||
// hitung bobot per testcase
|
||||
$nilai = 0;
|
||||
|
||||
if ( $skor > 0 && $bobot > 0 ) {
|
||||
|
||||
$nilai = $skor / $bobot * 100;
|
||||
}
|
||||
|
||||
echo json_encode(["point" => number_format($nilai, 2), "bobot" => $bobot, 'skor' => $test_passed->count()]);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// overview student
|
||||
public function lecturer_overview_student( $topic_id, $user_id ) {
|
||||
|
||||
$enrollment = Enrollment::where('android_topic_id', $topic_id)->first();
|
||||
$user = User::findOrFail( $user_id );
|
||||
|
||||
$where = array(
|
||||
'android_topic_id' => $topic_id,
|
||||
'user_id' => $user_id
|
||||
);
|
||||
$submit = Submits::where( $where )->get();
|
||||
|
||||
$dt_keseluruhan = array();
|
||||
$NA = 0;
|
||||
|
||||
if ( $submit->count() > 0 ) {
|
||||
|
||||
$total_NA = 0;
|
||||
$total_submit = $submit->count();
|
||||
foreach ( $submit AS $isi ) {
|
||||
|
||||
|
||||
// ambil data info nilai yang passed
|
||||
$where_passed = [
|
||||
|
||||
'android_submit_id' => $isi->id,
|
||||
'status_validate' => "passed"
|
||||
];
|
||||
// $test_passed = SubmitstestCase::where($where_passed)->get();
|
||||
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||
|
||||
// ambil informasi data task untuk memanggil bobot
|
||||
$info_testcase = Testcase::where("task_id", $isi->android_task_id)->get();
|
||||
|
||||
$bobot = 0;
|
||||
// $dt = [];
|
||||
foreach ( $info_testcase AS $isiit ) {
|
||||
|
||||
$bobot += $isiit->score;
|
||||
}
|
||||
|
||||
$skor = 0;
|
||||
foreach ( $test_passed AS $isitp ) {
|
||||
|
||||
$skor += $isitp->score;
|
||||
}
|
||||
// hitung bobot per testcase
|
||||
$nilai = 0;
|
||||
if ( $skor > 0 && $bobot > 0 ) {
|
||||
|
||||
$nilai = $skor / $bobot * 100;
|
||||
}
|
||||
|
||||
// ambil data testcase
|
||||
$isi->testcase = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where('android_submit_id', $isi->id)->get();
|
||||
$isi->nilai = $nilai;
|
||||
$isi->bobot = $bobot;
|
||||
|
||||
// echo $nilai.' = '.($nilai / $bobot * 100);
|
||||
// echo "<br>";
|
||||
|
||||
|
||||
|
||||
// autograding
|
||||
$total_NA += $nilai;
|
||||
// echo $nilai;
|
||||
// echo "<hr>";
|
||||
|
||||
array_push($dt_keseluruhan, $isi);
|
||||
}
|
||||
|
||||
// echo $total_NA;
|
||||
// echo " / ";
|
||||
// echo $total_submit;
|
||||
// echo " = ";
|
||||
$NA = $total_NA / $total_submit;
|
||||
// echo $total_NA;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// submission
|
||||
$submission = SubmitsFinalSubmission::where( $where );
|
||||
|
||||
|
||||
|
||||
// echo $NA;
|
||||
return view('android.teacher.overview_student', compact('enrollment', 'dt_keseluruhan', 'NA', 'user', 'submission'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function lecturer_confirm_student( $topic_id, $user_id, $enroll_id ){
|
||||
|
||||
$enrollment = Enrollment::findOrFail( $enroll_id );
|
||||
|
||||
$enrollment->status = "complete";
|
||||
$enrollment->save();
|
||||
|
||||
return redirect("teacher/android23/overview-student/$topic_id/$user_id");
|
||||
}
|
||||
|
||||
|
||||
// notifikasi validator
|
||||
public function notify_validator() {
|
||||
|
||||
$dt_notify = array();
|
||||
$total = 0;
|
||||
|
||||
// get submit topic
|
||||
$submitTopic = Submits::where('validator', 'process')->groupBy('android_topic_id')->get();
|
||||
|
||||
|
||||
foreach ( $submitTopic AS $isi ) {
|
||||
|
||||
$topic_id = $isi->android_topic_id;
|
||||
$hitung = Submits::where('android_topic_id', $topic_id)->where('validator', 'process')->get()->count();
|
||||
|
||||
$total += $hitung;
|
||||
|
||||
$isi->waiting = $hitung;
|
||||
array_push( $dt_notify, $isi );
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
|
||||
'total' => $total,
|
||||
'notify'=> $dt_notify
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
public function index() {
|
||||
|
||||
|
||||
$dt_keseluruhan = array();
|
||||
$topic = Topic::all();
|
||||
|
||||
if ( $topic->count() > 0 ) {
|
||||
|
||||
foreach ( $topic AS $isi ) {
|
||||
|
||||
// count data task
|
||||
$isi->total = Task::where('android_topic_id', $isi->id)->count();
|
||||
array_push( $dt_keseluruhan, $isi );
|
||||
}
|
||||
}
|
||||
|
||||
return view('android.admin.topic.index', compact('dt_keseluruhan'));
|
||||
}
|
||||
|
||||
|
||||
// tambah topik
|
||||
function add(Request $request) {
|
||||
|
||||
$nama_file = "";
|
||||
$directory_upload = "android23/profile";
|
||||
|
||||
|
||||
// cek direktori
|
||||
$direktori = 'android23/document/'.$request->folder_path;
|
||||
if ( !is_dir( $direktori ) ){
|
||||
|
||||
mkdir( $direktori );
|
||||
}
|
||||
|
||||
if ( $request->hasFile('picturePath') ) {
|
||||
|
||||
$file = $request->file('picturePath');
|
||||
$nama_file = $file->getClientOriginalName();
|
||||
$file->move($directory_upload, $nama_file);
|
||||
}
|
||||
|
||||
|
||||
$data = array(
|
||||
|
||||
'title' => $request->title,
|
||||
'description' => $request->description,
|
||||
'folder_path' => $request->folder_path,
|
||||
'picturePath' => $nama_file,
|
||||
'status' => $request->status
|
||||
);
|
||||
|
||||
Topic::insert($data);
|
||||
|
||||
return redirect('android23/topic');
|
||||
}
|
||||
|
||||
|
||||
// update topik
|
||||
function update( Request $request, $id ) {
|
||||
|
||||
$topik = Topic::where("id", $id)->first();
|
||||
$nama_file = $topik->picturePath;
|
||||
|
||||
$directory_upload = "android23/profile";
|
||||
|
||||
if ( $request->hasFile('picturePath') ) {
|
||||
|
||||
$file = $request->file('picturePath');
|
||||
$nama_file = $file->getClientOriginalName();
|
||||
$file->move($directory_upload, $nama_file);
|
||||
|
||||
|
||||
// delete old pic
|
||||
if ( !empty( $topik->picturePath ) ) {
|
||||
|
||||
unlink( $directory_upload .'/'. $topik->picturePath );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$data = array(
|
||||
|
||||
'title' => $request->title,
|
||||
'description' => $request->description,
|
||||
'folder_path' => $request->folder_path,
|
||||
'picturePath' => $nama_file,
|
||||
'status' => $request->status
|
||||
);
|
||||
|
||||
Topic::where('id', $id)->update($data);
|
||||
return redirect('android23/topic');
|
||||
}
|
||||
|
||||
|
||||
// delete
|
||||
function delete( $id ) {
|
||||
|
||||
Topic::where("id", $id)->delete();
|
||||
return redirect('android23/topic');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// learning task
|
||||
function learning_view( $id ) {
|
||||
|
||||
$topic = Topic::where('id', $id)->first();
|
||||
$dt_task = Task::where('android_topic_id', $id)->get();
|
||||
|
||||
// sum bobot keseluruhan
|
||||
// $total = Task::where('android_topic_id', $id)->sum('bobot');
|
||||
$total = 0;
|
||||
|
||||
$task = array();
|
||||
foreach ( $dt_task AS $isi ) {
|
||||
|
||||
// ambil data testcase
|
||||
$testcase = Testcase::where("task_id", $isi->id)->get();
|
||||
$isi->testcase = $testcase;
|
||||
|
||||
array_push( $task, $isi );
|
||||
}
|
||||
|
||||
|
||||
// print_r( $total );
|
||||
return view('android.admin.topic.learning', compact('topic', 'task', 'total'));
|
||||
}
|
||||
|
||||
|
||||
// tambah learnng
|
||||
function learning_add( Request $request, $id, $tipe ) {
|
||||
|
||||
$topic = Topic::where('id', $id)->first();
|
||||
$nama_file = "";
|
||||
$directory_upload = "android23/document/". $topic->folder_path;
|
||||
|
||||
if ( $request->hasFile('material') ) {
|
||||
|
||||
$file = $request->file('material');
|
||||
$nama_file = $file->getClientOriginalName();
|
||||
$file->move($directory_upload, $nama_file);
|
||||
}
|
||||
|
||||
// insert data task
|
||||
$data = array(
|
||||
|
||||
'android_topic_id' => $id,
|
||||
'task_no' => $request->task_no,
|
||||
'task_name' => $request->title,
|
||||
'caption' => $request->caption,
|
||||
'material' => $nama_file,
|
||||
'tipe' => $tipe,
|
||||
// 'testcase' => implode(',', $tags)
|
||||
);
|
||||
|
||||
|
||||
$id_task = Task::create($data)->id;
|
||||
|
||||
|
||||
// cek apakah mengisi data
|
||||
|
||||
|
||||
$dt_testcase = array();
|
||||
if ( $request->has('tags') && $tipe == "submission" ){
|
||||
|
||||
// convert tags to obj
|
||||
$data_tags = json_decode($request->tags);
|
||||
|
||||
foreach ( $data_tags AS $val ){
|
||||
|
||||
array_push( $dt_testcase, [
|
||||
|
||||
'task_id' => $id_task,
|
||||
'case' => $val->value,
|
||||
'score' => 0
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// print_r( $dt_testcase );
|
||||
// insert data testcase
|
||||
Testcase::insert( $dt_testcase );
|
||||
}
|
||||
|
||||
return redirect('android23/topic/learning/'. $id);
|
||||
}
|
||||
|
||||
|
||||
function learning_update( Request $request, $id_topic, $id_task ) {
|
||||
|
||||
$topik = Topic::where("id", $id_topic)->first();
|
||||
$task = Task::where("id", $id_task)->first();
|
||||
$nama_file = $task->material;
|
||||
|
||||
$directory_upload = "android23/document/$topik->folder_path";
|
||||
|
||||
if ( $request->hasFile('material') ) {
|
||||
|
||||
$file = $request->file('material');
|
||||
$nama_file = $file->getClientOriginalName();
|
||||
$file->move($directory_upload, $nama_file);
|
||||
|
||||
|
||||
// delete old pic
|
||||
if ( !empty( $task->material ) ) {
|
||||
|
||||
unlink( $directory_upload .'/'. $task->material );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$data = array(
|
||||
|
||||
'task_no' => $request->task_no,
|
||||
'task_name' => $request->title,
|
||||
'caption' => $request->caption,
|
||||
'material' => $nama_file,
|
||||
|
||||
);
|
||||
|
||||
Task::where('id', $id_task)->update($data);
|
||||
return redirect('android23/topic/learning/'. $id_topic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function learning_remove( $id_topic, $id_task ) {
|
||||
|
||||
// remove data testcase
|
||||
Testcase::where('task_id', $id_task)->delete();
|
||||
|
||||
Task::where('id', $id_task)->delete();
|
||||
return redirect('android23/topic/learning/'. $id_topic);
|
||||
}
|
||||
|
||||
|
||||
/* Testcase */
|
||||
public function learning_update_testcase( Request $request ) {
|
||||
|
||||
// task id
|
||||
$task_id = $request->task_id;
|
||||
|
||||
$dt_testcase_baru = array();
|
||||
|
||||
foreach ( $request->case AS $index => $isi ) {
|
||||
|
||||
array_push( $dt_testcase_baru, [
|
||||
|
||||
'task_id' => $task_id,
|
||||
'case' => $isi,
|
||||
'score' => $request->score[$index]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
// hapus data testcase lama
|
||||
Testcase::where('task_id', $task_id)->delete();
|
||||
Testcase::insert( $dt_testcase_baru );
|
||||
|
||||
return redirect('android23/topic/learning/'. $request->topic_id);
|
||||
}
|
||||
|
||||
|
||||
public function learning_add_testcase( Request $request, $topic_id, $task_id ) {
|
||||
|
||||
$dt_testcase = array();
|
||||
if ( $request->has('tags') ){
|
||||
|
||||
// convert tags to obj
|
||||
$data_tags = json_decode($request->tags);
|
||||
|
||||
foreach ( $data_tags AS $val ){
|
||||
|
||||
array_push( $dt_testcase, [
|
||||
|
||||
'task_id' => $task_id,
|
||||
'case' => $val->value,
|
||||
'score' => 0
|
||||
]);
|
||||
}
|
||||
|
||||
// insert data testcase
|
||||
Testcase::insert( $dt_testcase );
|
||||
}
|
||||
return redirect('android23/topic/learning/'. $topic_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function learning_reset_testcase( $topic_id, $task_id ) {
|
||||
|
||||
Testcase::where('task_id', $task_id)->delete();
|
||||
return redirect('android23/topic/learning/'. $topic_id);
|
||||
}
|
||||
|
||||
|
||||
public function learning_remove_testcase( $topic_id, $testcase_id ){
|
||||
|
||||
$testcase = Testcase::find( $testcase_id );
|
||||
$testcase->delete();
|
||||
|
||||
return redirect('android23/topic/learning/'. $topic_id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Android23;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class AssignController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Android;
|
||||
|
||||
use App\Models\Android\Enrollment;
|
||||
use App\Models\Android\Topic;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class EnrollController extends Controller
|
||||
{
|
||||
public function enroll( Request $request, $topic_id = null ){
|
||||
|
||||
// cek
|
||||
$topik = Topic::findOrFail($topic_id);
|
||||
|
||||
$where = ['android_topic_id' => $topic_id, 'user_id' => Auth::user()->id];
|
||||
$enroll_status = Enrollment::where($where)->count();
|
||||
|
||||
if ( $enroll_status == 0 ) {
|
||||
|
||||
$data = array(
|
||||
|
||||
'android_topic_id' => $topic_id,
|
||||
'user_id' => Auth::user()->id,
|
||||
'status' => "process"
|
||||
);
|
||||
|
||||
Enrollment::create( $data );
|
||||
return redirect()->route('material');
|
||||
|
||||
} else {
|
||||
|
||||
// sebelumnya sudah pernah melakukan enroll
|
||||
return redirect()->route('material')->withErrors('pesan', 'Invalid Enrollment Request');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,670 @@
|
|||
<?php
|
||||
namespace App\Http\Controllers\Android;
|
||||
|
||||
use App\Models\Android\Enrollment;
|
||||
use App\Models\Android\Submits;
|
||||
use App\Models\Android\SubmitsTestCase;
|
||||
use App\Models\Android\SubmitsFinalSubmission;
|
||||
use App\Models\Android\Task;
|
||||
use App\Models\Android\Task_waiting;
|
||||
use App\Models\Android\Topic;
|
||||
use App\Models\Android\Testcase;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class MaterialController extends Controller
|
||||
{
|
||||
public function index() {
|
||||
|
||||
$topic = DB::table("android_topics");
|
||||
$data_keseluruhan = array();
|
||||
$id = Auth::user()->id;
|
||||
|
||||
$recent_task = array();
|
||||
|
||||
if ( $topic->count() > 0 ) {
|
||||
|
||||
foreach ( $topic->get() AS $isi ){
|
||||
|
||||
// init
|
||||
$status = "enroll";
|
||||
$progress = 0;
|
||||
$grade = "-";
|
||||
$total_task = Task::where("android_topic_id", $isi->id)->count();
|
||||
$recent = "-";
|
||||
$akses = "-";
|
||||
|
||||
$where = array(
|
||||
|
||||
'android_topic_id' => $isi->id,
|
||||
'user_id' => $id
|
||||
);
|
||||
|
||||
// check
|
||||
$enrollment = DB::table("android_enrollment")->where($where);
|
||||
if ( $enrollment->count() != 0 ) {
|
||||
|
||||
$dt_enroll = $enrollment->first();
|
||||
$status = $dt_enroll->status;
|
||||
|
||||
$akses = $enrollment->first()->created_at;
|
||||
}
|
||||
|
||||
// check progress
|
||||
if ( $status != "cancel" ) {
|
||||
|
||||
$task_waiting = Task_waiting::where( $where );
|
||||
|
||||
$total_task_waiting = $task_waiting->count();
|
||||
if ( $total_task_waiting > 0 ) {
|
||||
|
||||
$info = Task::find( $task_waiting->first()->android_task_id );
|
||||
$progress = $total_task_waiting / $total_task * 100;
|
||||
|
||||
$recent = $info->task_name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$NA = 0;
|
||||
if ( $status == "complete" ) {
|
||||
|
||||
$where_enroll = array(
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $isi->id
|
||||
);
|
||||
$enrollment = Enrollment::where($where_enroll)->first();
|
||||
|
||||
|
||||
$dt_all_submit = array();
|
||||
$where = array(
|
||||
|
||||
'user_id' => Auth::user()->id, // 1577
|
||||
'android_submits.android_topic_id' => $isi->id // 11
|
||||
);
|
||||
$submits = Submits::select('android_submits.id','android_task.task_name', 'android_submits.created_at', 'android_submits.duration')
|
||||
->join('android_task', 'android_task.id', '=', 'android_submits.android_task_id')->where($where)->get();
|
||||
|
||||
|
||||
//mulai sini autograding
|
||||
|
||||
$estimate = 0;
|
||||
$total_submit = $submits->count();
|
||||
|
||||
if ( $submits->count() > 0 ) {
|
||||
|
||||
$total_test = 0;
|
||||
|
||||
foreach ( $submits AS $isi_sb ) {
|
||||
|
||||
$submit_id = $isi_sb->id;
|
||||
|
||||
|
||||
$where_passed = [
|
||||
|
||||
'android_submit_id' => $isi_sb->id,
|
||||
'status_validate' => "passed"
|
||||
];
|
||||
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||
|
||||
// ambil informasi data task untuk memanggil bobot
|
||||
$info_testcase = Testcase::where("task_id", $isi_sb->android_task_id)->get();
|
||||
|
||||
$bobot = 0;
|
||||
foreach ( $info_testcase AS $isiit ) {
|
||||
|
||||
$bobot += $isiit->score;
|
||||
}
|
||||
|
||||
$skor = 0;
|
||||
foreach ( $test_passed AS $isitp ) {
|
||||
|
||||
$skor += $isitp->score;
|
||||
}
|
||||
|
||||
$nilai = 0;
|
||||
if ( $skor > 0 && $bobot > 0 ) {
|
||||
|
||||
$nilai = $skor / $bobot * 100;
|
||||
// echo "masuk";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
// $dt_submit_testcase = array();
|
||||
|
||||
// $testcase = SubmitsTestCase::where("android_submit_id", $submit_id)->get();
|
||||
|
||||
// // autograding
|
||||
// $passed = 0;
|
||||
// foreach ( $testcase AS $det ) {
|
||||
|
||||
// if ( $det->status_validate == "passed" ) {
|
||||
|
||||
// $passed++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $persentage = ($passed / $testcase->count() * 100);
|
||||
$total_test += $nilai;
|
||||
$estimate += $isi_sb->duration;
|
||||
|
||||
|
||||
// echo $nilai.'<br>';
|
||||
}
|
||||
|
||||
$NA = $total_test / $total_submit;
|
||||
|
||||
|
||||
// echo $total_test.' - '. $total_submit;
|
||||
// echo $NA;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$isi->status_waiting = $status;
|
||||
$isi->progress = $progress;
|
||||
$isi->grade = $grade;
|
||||
$isi->total_task = $total_task;
|
||||
$isi->akses_materi = $akses;
|
||||
$isi->recent = $recent;
|
||||
$isi->NA = $NA;
|
||||
|
||||
|
||||
|
||||
|
||||
// recent task
|
||||
$where = array(
|
||||
|
||||
'android_task.android_topic_id' => $isi->id,
|
||||
'user_id' => $id
|
||||
);
|
||||
$recent_task = Task_waiting::select("task_name", "title")
|
||||
->join("android_task", "android_task.id", "=", "android_task_waiting.android_task_id")
|
||||
->join("android_topics", "android_topics.id", "=", "android_task_waiting.android_topic_id")
|
||||
->where( $where )->orderBy('android_task.created_at', 'desc')->get()->unique('android_topic_id');
|
||||
|
||||
array_push( $data_keseluruhan, $isi );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return view('android.student.material.index', compact('data_keseluruhan', 'recent_task'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// upload
|
||||
public function upload( Request $request ) {
|
||||
|
||||
$request->validate([
|
||||
|
||||
'file' => 'required'
|
||||
]);
|
||||
|
||||
$fileName = time().'-'.request()->file->getClientOriginalExtension();
|
||||
request()->file->move(public_path('files'), $fileName);
|
||||
|
||||
return request()->json([
|
||||
'success' => "You have successfully upload file"
|
||||
]);
|
||||
}
|
||||
|
||||
public function task( Request $request, $topic_id ) {
|
||||
|
||||
// ambil data informasi topik
|
||||
$topic = Topic::findOrFail( $topic_id );
|
||||
$where = array(
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_task_id' => $request->id
|
||||
);
|
||||
$submit_byId = Submits::where($where);
|
||||
$data = [];
|
||||
|
||||
|
||||
// cek memiliki id atau tidak
|
||||
if ($request->filled('id')) {
|
||||
|
||||
$id_task_recommend = $request->id;
|
||||
|
||||
// ambil informasi data task berdasarkan id
|
||||
$task = Task::findOrFail($id_task_recommend);
|
||||
$testcase = Testcase::where("task_id", $id_task_recommend)->get();
|
||||
|
||||
// $task = Task::where('id', $id_task_recommend);
|
||||
|
||||
|
||||
$submit_information = array();
|
||||
$submit_testcase = array();
|
||||
if ( $submit_byId->count() > 0 ) {
|
||||
|
||||
$submits = $submit_byId->first();
|
||||
|
||||
$submitTestCase = SubmitsTestCase::where("android_submit_id", $submits->id)->get();
|
||||
|
||||
$submit_information = $submits;
|
||||
$submit_testcase = $submitTestCase;
|
||||
|
||||
|
||||
// echo json_encode($submits->id);
|
||||
}
|
||||
|
||||
|
||||
// cek apakah sudah mengakses materi ?
|
||||
|
||||
|
||||
|
||||
// next material
|
||||
$next = Task::where('id', '>', $task->id);
|
||||
|
||||
if ( $next->count() > 0 ) {
|
||||
|
||||
$url = '/android23/task/'.$topic_id.'?id='. $next->min('id');
|
||||
} else {
|
||||
|
||||
$url = '/android23/task/'.$topic_id.'?id='. $id_task_recommend.'&type=final';
|
||||
}
|
||||
|
||||
|
||||
$dt_task = array();
|
||||
$tasks = Task::where("android_topic_id", $topic_id)->orderBy('task_no', 'ASC')->get();
|
||||
|
||||
foreach ( $tasks AS $isi_tsk ) {
|
||||
|
||||
$where = [
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $topic_id,
|
||||
'android_task_id' => $isi_tsk->id,
|
||||
|
||||
];
|
||||
$task_waiting = Task_waiting::where($where)->get();
|
||||
|
||||
if ( $task_waiting->count() == 0 ) {
|
||||
|
||||
$isi_tsk->status_akses = false;
|
||||
} else {
|
||||
|
||||
$isi_tsk->status_akses = true;
|
||||
}
|
||||
|
||||
array_push( $dt_task, $isi_tsk );
|
||||
}
|
||||
|
||||
|
||||
$data = array(
|
||||
|
||||
'topic_id' => $topic_id,
|
||||
'id' => $id_task_recommend,
|
||||
'task' => $task,
|
||||
'testcase' => $testcase,
|
||||
'topic' => $topic,
|
||||
'all_task' => $dt_task,
|
||||
'submit_information' => $submit_information,
|
||||
'submit_testcase' => $submit_testcase,
|
||||
'request' => $request,
|
||||
'taskwaiting' => $task_waiting->count(),
|
||||
'urlku' => $url
|
||||
);
|
||||
|
||||
// echo $url;
|
||||
|
||||
|
||||
|
||||
// cek apabila request sudah final
|
||||
if ( $request->type == "final" ) {
|
||||
|
||||
$where_enroll = array(
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $topic_id
|
||||
);
|
||||
$enrollment = Enrollment::where($where_enroll)->first();
|
||||
|
||||
|
||||
$dt_all_submit = array();
|
||||
$where = array(
|
||||
|
||||
'user_id' => Auth::user()->id, // 1577
|
||||
'android_submits.android_topic_id' => $topic_id // 11
|
||||
);
|
||||
$submits = Submits::select('android_task_id','android_submits.id','android_task.task_name', 'android_submits.created_at', 'android_submits.duration')
|
||||
->join('android_task', 'android_task.id', '=', 'android_submits.android_task_id')->where($where)->get();
|
||||
|
||||
|
||||
//mulai sini autograding
|
||||
$NA = 0;
|
||||
$estimate = 0;
|
||||
$total_submit = $submits->count();
|
||||
|
||||
if ( $submits->count() > 0 ) {
|
||||
|
||||
$total_test = 0;
|
||||
|
||||
foreach ( $submits AS $isi ) {
|
||||
|
||||
$submit_id = $isi->id;
|
||||
$dt_submit_testcase = array();
|
||||
|
||||
$testcase = SubmitsTestCase::where("android_submit_id", $submit_id)->get();
|
||||
|
||||
|
||||
|
||||
|
||||
// autograding
|
||||
$where_passed = [
|
||||
|
||||
'android_submit_id' => $isi->id,
|
||||
'status_validate' => "passed"
|
||||
];
|
||||
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||
|
||||
// ambil informasi data task untuk memanggil bobot
|
||||
$info_testcase = Testcase::where("task_id", $isi->android_task_id)->get();
|
||||
|
||||
$bobot = 0;
|
||||
foreach ( $info_testcase AS $isiit ) {
|
||||
|
||||
$bobot += $isiit->score;
|
||||
}
|
||||
|
||||
$skor = 0;
|
||||
foreach ( $test_passed AS $isitp ) {
|
||||
|
||||
$skor += $isitp->score;
|
||||
}
|
||||
|
||||
$nilai = 0;
|
||||
if ( $skor > 0 && $bobot > 0 ) {
|
||||
|
||||
$nilai = $skor / $bobot * 100;
|
||||
}
|
||||
|
||||
$total_test += $nilai;
|
||||
|
||||
|
||||
// echo $isi->android_task_id.'<br>';
|
||||
|
||||
|
||||
|
||||
|
||||
// autograding (old)
|
||||
// $passed = 0;
|
||||
// foreach ( $testcase AS $det ) {
|
||||
|
||||
// if ( $det->status_validate == "passed" ) {
|
||||
|
||||
// $passed++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $persentage = ($passed / $testcase->count() * 100);
|
||||
// $total_test += $persentage;
|
||||
// total_test = total_NA + persentage
|
||||
// total_test = 0 + 57
|
||||
// total_test = 57
|
||||
|
||||
// total_test = total_test + persentnage
|
||||
// total test = 57 + 75
|
||||
$estimate += $isi->duration;
|
||||
|
||||
array_push( $dt_all_submit, [
|
||||
|
||||
'info' => $isi,
|
||||
'persentage' => $nilai,
|
||||
'testcase' => $testcase
|
||||
]);
|
||||
}
|
||||
|
||||
$NA = $total_test / $total_submit;
|
||||
}
|
||||
|
||||
$add = array(
|
||||
|
||||
'submits' => $dt_all_submit,
|
||||
'NA' => $NA,
|
||||
'submit_submission' => SubmitsFinalSubmission::all(),
|
||||
'enrollment' => $enrollment,
|
||||
'estimate' => $estimate
|
||||
);
|
||||
|
||||
// merge
|
||||
$data = array_merge($data, $add);
|
||||
}
|
||||
|
||||
// return view
|
||||
|
||||
} else {
|
||||
|
||||
// last recomend
|
||||
$id_task_recommend = "";
|
||||
$where = [
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $topic_id,
|
||||
];
|
||||
$recommend = Task_waiting::where( $where )->orderBy('created_at', 'DESC');
|
||||
if ( $recommend->count() == 0 ) {
|
||||
|
||||
// memberikan id awal
|
||||
// ambil materi awal task berdasarkan topic id
|
||||
$dt_task = Task::where('android_topic_id', $topic_id)->first();
|
||||
$id_task_recommend = $dt_task->id;
|
||||
} else {
|
||||
|
||||
$dt_task = $recommend->first();
|
||||
$id_task_recommend = $dt_task->android_task_id;
|
||||
}
|
||||
|
||||
// echo "test";
|
||||
// redirect . ..
|
||||
}
|
||||
|
||||
|
||||
|
||||
// access materi update
|
||||
$cektaskwaiting = array(
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $topic_id,
|
||||
'android_task_id' => $id_task_recommend
|
||||
);
|
||||
|
||||
|
||||
// cek
|
||||
$cekTask = Task_waiting::where( $cektaskwaiting )->get();
|
||||
if ( $cekTask->count() == 0 ) {
|
||||
|
||||
Task_waiting::create( $cektaskwaiting );
|
||||
}
|
||||
|
||||
|
||||
// print_r( $data );
|
||||
if ( $request->filled('id') ) {
|
||||
|
||||
return view('android.student/material/task', $data);
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
return redirect('android23/task/'. $topic_id.'?id='. $id_task_recommend);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//validation result
|
||||
public function validation() {
|
||||
|
||||
// informasi login sebagai
|
||||
$id = Auth::user()->id;
|
||||
|
||||
$kondisi = ["user_id" => $id, 'status' => "complete"];
|
||||
$enrollment = Enrollment::where($kondisi)->get();
|
||||
|
||||
$dt_keseluruhan = array();
|
||||
|
||||
if ( $enrollment->count() > 0 ) {
|
||||
|
||||
foreach ( $enrollment AS $isi ) {
|
||||
|
||||
// id topik
|
||||
$topic_id = $isi->android_topic_id;
|
||||
$topic = Topic::find( $topic_id );
|
||||
|
||||
$where_task = ["android_topic_id" => $topic_id, 'user_id' => $id];
|
||||
// $task = Task::where( $where_task )->get();
|
||||
|
||||
// ambil data submits
|
||||
$submit = Submits::where($where_task)->get();
|
||||
|
||||
$isi->submit = $submit;
|
||||
$isi->topic = $topic;
|
||||
// $isi->task = $task;
|
||||
|
||||
|
||||
array_push( $dt_keseluruhan, $isi );
|
||||
}
|
||||
}
|
||||
|
||||
// echo "oke";
|
||||
return view('android.student.validation.index', compact('dt_keseluruhan'));
|
||||
}
|
||||
|
||||
|
||||
public function validation_detail( $topic_id ) {
|
||||
|
||||
$enrollment = Enrollment::where( "android_topic_id", $topic_id )->first();
|
||||
|
||||
|
||||
$dt_all_submit = array();
|
||||
$where = array(
|
||||
|
||||
'user_id' => Auth::user()->id, // 1577
|
||||
'android_submits.android_topic_id' => $topic_id // 11
|
||||
);
|
||||
$submits = Submits::select('android_task_id','android_submits.id','android_task.task_name', 'android_submits.created_at', 'android_submits.duration')
|
||||
->join('android_task', 'android_task.id', '=', 'android_submits.android_task_id')->where($where)->get();
|
||||
|
||||
|
||||
//mulai sini autograding
|
||||
$NA = 0;
|
||||
$estimate = 0;
|
||||
$total_submit = $submits->count();
|
||||
|
||||
if ( $submits->count() > 0 ) {
|
||||
|
||||
$total_test = 0;
|
||||
|
||||
foreach ( $submits AS $isi ) {
|
||||
|
||||
$submit_id = $isi->id;
|
||||
$dt_submit_testcase = array();
|
||||
|
||||
$testcase = SubmitsTestCase::where("android_submit_id", $submit_id)->get();
|
||||
|
||||
|
||||
|
||||
|
||||
// autograding
|
||||
$where_passed = [
|
||||
|
||||
'android_submit_id' => $isi->id,
|
||||
'status_validate' => "passed"
|
||||
];
|
||||
$test_passed = SubmitstestCase::join("android_testcase", "android_testcase.id", "=", "android_submits_testcase.android_testcase_id")->where($where_passed)->get();
|
||||
|
||||
// ambil informasi data task untuk memanggil bobot
|
||||
$info_testcase = Testcase::where("task_id", $isi->android_task_id)->get();
|
||||
|
||||
$bobot = 0;
|
||||
foreach ( $info_testcase AS $isiit ) {
|
||||
|
||||
$bobot += $isiit->score;
|
||||
}
|
||||
|
||||
$skor = 0;
|
||||
foreach ( $test_passed AS $isitp ) {
|
||||
|
||||
$skor += $isitp->score;
|
||||
}
|
||||
|
||||
$nilai = 0;
|
||||
if ( $skor > 0 && $bobot > 0 ) {
|
||||
|
||||
$nilai = $skor / $bobot * 100;
|
||||
}
|
||||
|
||||
$total_test += $nilai;
|
||||
|
||||
|
||||
// echo $isi->android_task_id.'<br>';
|
||||
|
||||
|
||||
|
||||
|
||||
// autograding (old)
|
||||
// $passed = 0;
|
||||
// foreach ( $testcase AS $det ) {
|
||||
|
||||
// if ( $det->status_validate == "passed" ) {
|
||||
|
||||
// $passed++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// $persentage = ($passed / $testcase->count() * 100);
|
||||
// $total_test += $persentage;
|
||||
// total_test = total_NA + persentage
|
||||
// total_test = 0 + 57
|
||||
// total_test = 57
|
||||
|
||||
// total_test = total_test + persentnage
|
||||
// total test = 57 + 75
|
||||
$estimate += $isi->duration;
|
||||
|
||||
array_push( $dt_all_submit, [
|
||||
|
||||
'info' => $isi,
|
||||
'persentage' => $nilai,
|
||||
'testcase' => $testcase
|
||||
]);
|
||||
}
|
||||
|
||||
$NA = $total_test / $total_submit;
|
||||
}
|
||||
|
||||
$dt_keseluruhan = array(
|
||||
|
||||
'submits' => $dt_all_submit,
|
||||
'NA' => $NA,
|
||||
'submit_submission' => SubmitsFinalSubmission::all(),
|
||||
'enrollment' => $enrollment,
|
||||
'estimate' => $estimate
|
||||
);
|
||||
|
||||
return view('android.student.validation.detail', compact('dt_keseluruhan'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Android;
|
||||
|
||||
use App\Models\Android\Submits;
|
||||
use App\Models\Android\SubmitsTestCase;
|
||||
use App\Models\Android\Enrollment;
|
||||
use App\Models\Android\SubmitsFinalSubmission;
|
||||
use App\Models\Android\Topic;
|
||||
use App\Models\Android\Task;
|
||||
use App\Models\Android\Testcase;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class SubmissionController extends Controller
|
||||
{
|
||||
public function proses_tambah( Request $request ) {
|
||||
|
||||
$request->validate([
|
||||
|
||||
'userfile' => 'required|image',
|
||||
'duration' => 'required',
|
||||
'comment' => 'required|string'
|
||||
]);
|
||||
|
||||
$file = $request->file('userfile');
|
||||
$extension = $file->getClientOriginalExtension();
|
||||
|
||||
// upload path
|
||||
$upload_path = "android23/submission";
|
||||
$fileName = uniqid().'-'.strtotime("now").'.'.$extension;
|
||||
|
||||
$file->move($upload_path, $fileName);
|
||||
$task_id = $request->android_task_id;
|
||||
$topic_id = $request->android_topic_id;
|
||||
$data = array(
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $topic_id,
|
||||
'android_task_id' => $task_id,
|
||||
'duration' => $request->duration,
|
||||
'upload' => $fileName,
|
||||
'comment' => $request->comment,
|
||||
'validator' => "process"
|
||||
);
|
||||
|
||||
$submit = Submits::create( $data );
|
||||
|
||||
$dt_task = Task::findOrFail( $task_id );
|
||||
$dt_testcasefromtask = Testcase::where("task_id", $task_id)->get();
|
||||
|
||||
|
||||
// listdata test case
|
||||
$data_submit = array();
|
||||
|
||||
if ( $request->has('task') ) {
|
||||
foreach ( $dt_testcasefromtask AS $isi ) {
|
||||
|
||||
$status = false;
|
||||
foreach ( $request->task AS $ts ){
|
||||
|
||||
if ( $ts == $isi->id ) {
|
||||
|
||||
$status = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$label = "failed";
|
||||
if ( $status ) {
|
||||
|
||||
$label = "passed";
|
||||
}
|
||||
|
||||
|
||||
array_push($data_submit, [
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_submit_id' => $submit->id,
|
||||
'android_testcase_id'=> $isi->id,
|
||||
'status'=> $label,
|
||||
'status_validate' => 'waiting',
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now(),
|
||||
]);
|
||||
}
|
||||
|
||||
// print_r( $data_submit );
|
||||
SubmitsTestCase::insert($data_submit);
|
||||
}
|
||||
return redirect('android23/task/'.$topic_id.'?id='.$task_id);
|
||||
}
|
||||
|
||||
|
||||
// submit final submission
|
||||
public function submit_final_submission( Request $request, $topic_id ){
|
||||
|
||||
|
||||
if ( !empty( $request->type ) ) {
|
||||
// validation
|
||||
if ( $request->type == "github" ) {
|
||||
|
||||
$rules = array(
|
||||
'link' => 'required|string'
|
||||
);
|
||||
} else {
|
||||
|
||||
$rules = array(
|
||||
'userfile' => 'required|file|mimes:zip|max:3072'
|
||||
);
|
||||
}
|
||||
|
||||
// check rules
|
||||
$request->validate($rules);
|
||||
|
||||
if ( $request->type == "zip" ) {
|
||||
|
||||
// file upload
|
||||
$file = $request->file('userfile');
|
||||
|
||||
// upload path
|
||||
$upload_path = "android23/final-submission";
|
||||
$userfile = uniqid().'-'.strtotime("now").'.zip';
|
||||
|
||||
$file->move($upload_path, $userfile);
|
||||
} else {
|
||||
|
||||
// github
|
||||
$userfile = $request->link;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$data = array(
|
||||
|
||||
'user_id' => Auth::user()->id,
|
||||
'android_topic_id' => $topic_id,
|
||||
'tipe' => $request->type,
|
||||
'userfile' => $userfile
|
||||
);
|
||||
|
||||
SubmitsFinalSubmission::create( $data );
|
||||
|
||||
|
||||
$dataEnrollUpdate = array(
|
||||
|
||||
'status' => "review"
|
||||
);
|
||||
Enrollment::where(['user_id' => $data['user_id'], 'android_topic_id' => $data['android_topic_id']])->update( $dataEnrollUpdate );
|
||||
|
||||
// redirect
|
||||
return redirect('android23/task/'.$topic_id.'?id='.$request->task_id.'&type=final');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function overview( $id ) {
|
||||
|
||||
$data = array(
|
||||
'topic_id' => $id,
|
||||
'topic' => Topic::findOrFail( $id ),
|
||||
'all_task' => Task::orderBy('task_no', 'ASC')->get(),
|
||||
);
|
||||
|
||||
return view('student.android23.material.overview', $data);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function proses( Request $request ) {
|
||||
|
||||
$request->validate([
|
||||
'email' => 'required',
|
||||
'password' => 'required',
|
||||
]);
|
||||
|
||||
$credential = request(['email', 'password']);
|
||||
if ( Auth::attempt( $credential ) ) {
|
||||
|
||||
if ( Auth::user()->role == "student" ) {
|
||||
return redirect('dashboard-student');
|
||||
|
||||
} else if ( Auth::user()->role == "teacher" ) {
|
||||
return redirect('dashboard_teacher');
|
||||
|
||||
} else if( Auth::user()->role == "Admin" ) {
|
||||
|
||||
|
||||
session(['key' => Auth::user()->name]);
|
||||
session(['email' => Auth::user()->email]);
|
||||
return redirect('dashboard-student');
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
echo "okee err";
|
||||
}
|
||||
}
|
||||
public function signup( Request $request ) {
|
||||
$data = $request->validate([
|
||||
'name' => 'required',
|
||||
'email' => 'required',
|
||||
'password' => 'required|confirmed',
|
||||
'teacher' => 'required'
|
||||
|
||||
]);
|
||||
$data['password'] = bcrypt($data['password']);
|
||||
User::create($data);
|
||||
return redirect('/');
|
||||
}
|
||||
public function logoutt(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::logout();
|
||||
$request->session()->invalidate();
|
||||
|
||||
$request->session()->regenerateToken();
|
||||
return redirect('/');
|
||||
}
|
||||
public function redirect() {
|
||||
return Socialite::driver(driver:'google')->redirect();
|
||||
}
|
||||
public function googleCallback()
|
||||
{
|
||||
$user = Socialite::driver('google')->user();
|
||||
$userDatabase = User::where('google_id', $user->getId())->first();
|
||||
$token = $user->token;
|
||||
session(['google_token' => $token]);
|
||||
|
||||
if (!$userDatabase) {
|
||||
$data = [
|
||||
'google_id' => $user->getId(),
|
||||
'name' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
'role' => 'Student',
|
||||
];
|
||||
$existingUser = User::where('email', $data['email'])->first();
|
||||
if ($existingUser) {
|
||||
$existingUser->update($data);
|
||||
$userDatabase = $existingUser;
|
||||
} else {
|
||||
$userDatabase = User::create($data);
|
||||
}
|
||||
auth()->login($userDatabase);
|
||||
|
||||
session()->regenerate();
|
||||
return redirect()->route('dashboard-student');
|
||||
} else {
|
||||
auth()->login($userDatabase);
|
||||
|
||||
session()->regenerate();
|
||||
return redirect()->route('dashboard-student');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, ValidatesRequests;
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DataController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$cards = [
|
||||
[
|
||||
'image' => './images/cards/Android.png',
|
||||
'title' => 'Android programming with Java and Kotlin',
|
||||
'topics' => '18 learning topics',
|
||||
],
|
||||
[
|
||||
'image' => './images/cards/Flutter.png',
|
||||
'title' => 'Mobile programming with Flutter',
|
||||
'topics' => '18 learning topics',
|
||||
],
|
||||
[
|
||||
'image' => './images/cards/Node.js.png',
|
||||
'title' => 'Web application with Node.JS',
|
||||
'topics' => '18 learning topics',
|
||||
],
|
||||
];
|
||||
|
||||
$cardsData = [
|
||||
[
|
||||
'image' => './images/cards/computer.png',
|
||||
'title' => 'Fully Computer-Assisted Learning Platform',
|
||||
'description' => 'Digital educational platform that utilizes artificial intelligence and machine learning to provide a comprehensive and interactive learning experience entirely driven by computer technology.',
|
||||
],
|
||||
[
|
||||
'image' => './images/cards/eos-icons_machine-learning.png',
|
||||
'title' => 'Intelligence Guidance',
|
||||
'description' => 'System or technology that provides automated support and guidance to learners, assisting them in their learning journey through intelligent algorithms and machine learning.',
|
||||
],
|
||||
[
|
||||
'image' => './images/cards/Grading.png',
|
||||
'title' => 'Auto Grading',
|
||||
'description' => 'Technology that automatically evaluates and scores assignments, exams, or assessments, eliminating the need for manual grading by instructors and providing efficient and consistent feedback to students.',
|
||||
],
|
||||
];
|
||||
|
||||
$cardsData2 = [
|
||||
[
|
||||
'image' => './images/cards/Intelligence.png',
|
||||
'title' => 'Intelligence Learning Guidance',
|
||||
'description' => 'Intelligence Learning Guidance utilizes AI and smart algorithms to provide personalized support, adapting to learners needs and optimizing their educational outcomes.',
|
||||
],
|
||||
[
|
||||
'image' => './images/cards/coding.png',
|
||||
'title' => 'Practical Coding Approach',
|
||||
'description' => 'Focuses on teaching coding skills through real-world examples, projects, and problem-solving scenarios, enabling learners to develop practical coding proficiency and problem-solving abilities.',
|
||||
],
|
||||
[
|
||||
'image' => './images/cards/Machine.png',
|
||||
'title' => 'Online Virtual Machine',
|
||||
'description' => 'Virtual computing environment accessible over the internet, enabling users to run applications, perform tasks, and store data without requiring physical hardware.',
|
||||
],
|
||||
];
|
||||
|
||||
return view('welcome', compact('cards', 'cardsData', 'cardsData2'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Task;
|
||||
use App\Models\Material;
|
||||
use App\Models\StudentSubmission;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class MaterialController extends Controller
|
||||
{
|
||||
|
||||
|
||||
public function index()
|
||||
{
|
||||
$materials = Material::all(); // atau query lain sesuai kebutuhan
|
||||
return view('learning_student', compact('materials'));
|
||||
}
|
||||
public function materialDetail(Request $request)
|
||||
{
|
||||
$materialId = $request->query('material_id');
|
||||
$material = Material::findOrFail($materialId);
|
||||
|
||||
return view('material_detail', compact('material'));
|
||||
}
|
||||
public function showTask($taskId)
|
||||
{
|
||||
$task = Task::findOrFail($taskId);
|
||||
$submission = StudentSubmission::where('user_id', auth()->id())
|
||||
->where('task_id', $taskId)
|
||||
->latest()
|
||||
->first();
|
||||
|
||||
$submittedCode = '';
|
||||
if ($submission && $submission->file_path) {
|
||||
$submittedCode = Storage::get($submission->file_path);
|
||||
}
|
||||
|
||||
return view('task_detail', compact('task', 'submission', 'submittedCode'));
|
||||
}
|
||||
|
||||
public function downloadPdf(Task $task)
|
||||
{
|
||||
$pdfPath = 'pdfs/' . $task->pdf_path;
|
||||
if (Storage::disk('public')->exists($pdfPath)) {
|
||||
return Storage::disk('public')->download($pdfPath);
|
||||
}
|
||||
abort(404, 'PDF tidak ditemukan');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\NodeJS\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Yajra\DataTables\Facades\DataTables;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
$projects = Project::skip(0)->take(3)->get();
|
||||
if ($request->ajax()) {
|
||||
$data = DB::connection('nodejsDB')->table('projects')
|
||||
->select('projects.id', 'projects.title', DB::raw('COUNT(submissions.id) as submission_count'))
|
||||
->leftJoin('submissions', function ($join) use ($user) {
|
||||
$join->on('projects.id', '=', 'submissions.project_id')
|
||||
->where('submissions.user_id', '=', $user->id);
|
||||
})
|
||||
->groupBy('projects.id');
|
||||
|
||||
|
||||
return Datatables::of($data)
|
||||
->addIndexColumn()
|
||||
->addColumn('title', function ($row) {
|
||||
$title_button = '<a href="/nodejs/submissions/project/' . $row->id . '" class="underline text-secondary">' . $row->title . '</a>';
|
||||
return $title_button;
|
||||
})
|
||||
->addColumn('submission_count', function ($row) {
|
||||
$submission_count = $row->submission_count ?? 0;
|
||||
$text = $submission_count > 0 ? 'Submitted' : 'No Submission';
|
||||
$span_color = $submission_count > 0 ? 'green' : 'gray';
|
||||
$span = '<span class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none bg-' . $span_color . '-100 text-' . $span_color . '-800">' . ucfirst($text) . '</span>';
|
||||
return $span;
|
||||
})
|
||||
->rawColumns(['title', 'submission_count'])
|
||||
->make(true);
|
||||
}
|
||||
return view('nodejs.dashboard.index', compact('projects'));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ExecutionStepController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\ProfileUpdateRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display the user's profile form.
|
||||
*/
|
||||
public function edit(Request $request): View
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => $request->user(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*/
|
||||
public function update(ProfileUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$request->user()->fill($request->validated());
|
||||
|
||||
if ($request->user()->isDirty('email')) {
|
||||
$request->user()->email_verified_at = null;
|
||||
}
|
||||
|
||||
$request->user()->save();
|
||||
|
||||
return Redirect::route('profile.edit')->with('status', 'profile-updated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the user's account.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
$request->validateWithBag('userDeletion', [
|
||||
'password' => ['required', 'current_password'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$user->delete();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return Redirect::to('/');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use ZipArchive;
|
||||
use App\Models\NodeJS\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$projects = Project::all();
|
||||
return view('nodejs.projects.index', compact('projects'));
|
||||
}
|
||||
|
||||
public function show(Request $request, $project_id)
|
||||
{
|
||||
$project = Project::find($project_id);
|
||||
if (!$project) {
|
||||
return redirect()->route('projects');
|
||||
}
|
||||
return view('nodejs.projects.show', compact('project'));
|
||||
}
|
||||
|
||||
public function showPDF(Request $request)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
if ($request->id) {
|
||||
$mediaModel = new Media();
|
||||
$mediaModel->setConnection('nodejsDB');
|
||||
$media = $mediaModel->find($request->id);
|
||||
if ($media) {
|
||||
$path = $media->getUrl();
|
||||
return response()->json($path, 200);
|
||||
}
|
||||
return response()->json(["message" => "media not found"], 404);
|
||||
}
|
||||
return response()->json(["message" => "no media was requested"], 400);
|
||||
}
|
||||
}
|
||||
|
||||
public function download(Request $request, $project_id)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
$project = Project::find($project_id);
|
||||
if (!$project) {
|
||||
return response()->json(["message" => "project not found"], 404);
|
||||
}
|
||||
if ($request->type) {
|
||||
switch ($request->type) {
|
||||
case 'guides':
|
||||
$zipMedia = $project->getMedia('project_zips')->where('file_name', 'guides.zip')->first();
|
||||
if ($zipMedia) {
|
||||
return response()->json($zipMedia->getUrl(), 200);
|
||||
} else {
|
||||
$guides = $project->getMedia('project_guides');
|
||||
$tempDir = storage_path('app/public/assets/nodejs/projects/' . $project->title . '/zips');
|
||||
if (!is_dir($tempDir)) mkdir($tempDir);
|
||||
foreach ($guides as $guide) {
|
||||
$path = $guide->getPath();
|
||||
$filename = $guide->file_name;
|
||||
copy($path, $tempDir . '/' . $filename);
|
||||
}
|
||||
$zipPath = $tempDir . '/guides.zip';
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($zipPath, ZipArchive::CREATE) === TRUE) {
|
||||
$files = Storage::files('public/assets/nodejs/projects/' . $project->title . '/zips');
|
||||
foreach ($files as $file) {
|
||||
$zip->addFile(storage_path('app/' . $file), basename($file));
|
||||
}
|
||||
$zip->close();
|
||||
foreach ($files as $file) {
|
||||
unlink(storage_path('app/' . $file));
|
||||
}
|
||||
} else {
|
||||
throw new Exception('Failed to create zip archive');
|
||||
}
|
||||
$media = $project->addMedia($zipPath)->toMediaCollection('project_zips', 'nodejs_public_projects_files');
|
||||
return response()->json($media->getUrl(), 200);
|
||||
}
|
||||
break;
|
||||
case 'supplements':
|
||||
$zipMedia = $project->getMedia('project_zips')->where('file_name', 'supplements.zip')->first();
|
||||
if ($zipMedia) {
|
||||
return response()->json($zipMedia->getUrl(), 200);
|
||||
} else {
|
||||
$supplements = $project->getMedia('project_supplements');
|
||||
$tempDir = storage_path('app/public/assets/nodejs/projects/' . $project->title . '/zips');
|
||||
if (!is_dir($tempDir)) mkdir($tempDir);
|
||||
foreach ($supplements as $supplement) {
|
||||
$path = $supplement->getPath();
|
||||
$filename = $supplement->file_name;
|
||||
copy($path, $tempDir . '/' . $filename);
|
||||
}
|
||||
$zipPath = $tempDir . '/supplements.zip';
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($zipPath, ZipArchive::CREATE) === TRUE) {
|
||||
$files = Storage::files('public/assets/nodejs/projects/' . $project->title . '/zips');
|
||||
foreach ($files as $file) {
|
||||
$zip->addFile(storage_path('app/' . $file), basename($file));
|
||||
}
|
||||
$zip->close();
|
||||
foreach ($files as $file) {
|
||||
unlink(storage_path('app/' . $file));
|
||||
}
|
||||
} else {
|
||||
throw new Exception('Failed to create zip archive');
|
||||
}
|
||||
$media = $project->addMedia($zipPath)->toMediaCollection('project_zips', 'nodejs_public_projects_files');
|
||||
return response()->json($media->getUrl(), 200);
|
||||
}
|
||||
break;
|
||||
case 'tests':
|
||||
$zipMedia = $project->getMedia('project_zips')->where('file_name', 'tests.zip')->first();
|
||||
if ($zipMedia) {
|
||||
return response()->json($zipMedia->getUrl(), 200);
|
||||
} else {
|
||||
$tests_api = $project->getMedia('project_tests_api');
|
||||
$tests_web = $project->getMedia('project_tests_web');
|
||||
$tests_images = $project->getMedia('project_tests_images');
|
||||
|
||||
$tempDir = storage_path('app/public/assets/nodejs/projects/' . $project->title . '/zips');
|
||||
if (!is_dir($tempDir)) mkdir($tempDir);
|
||||
if (!is_dir($tempDir . '/tests')) mkdir($tempDir . '/tests');
|
||||
if (!is_dir($tempDir . '/tests/api')) mkdir($tempDir . '/tests/api');
|
||||
if (!is_dir($tempDir . '/tests/web')) mkdir($tempDir . '/tests/web');
|
||||
if (!is_dir($tempDir . '/tests/web/images')) mkdir($tempDir . '/tests/web/images');
|
||||
|
||||
foreach ($tests_api as $test) {
|
||||
$path = $test->getPath();
|
||||
$filename = $test->file_name;
|
||||
copy($path, $tempDir . '/tests/api/' . $filename);
|
||||
}
|
||||
foreach ($tests_web as $test) {
|
||||
$path = $test->getPath();
|
||||
$filename = $test->file_name;
|
||||
copy($path, $tempDir . '/tests/web/' . $filename);
|
||||
}
|
||||
foreach ($tests_images as $test) {
|
||||
$path = $test->getPath();
|
||||
$filename = $test->file_name;
|
||||
copy($path, $tempDir . '/tests/web/images/' . $filename);
|
||||
}
|
||||
|
||||
$zipPath = $tempDir . '/tests.zip';
|
||||
$zip = new ZipArchive;
|
||||
if ($zip->open($zipPath, ZipArchive::CREATE) === TRUE) {
|
||||
$zip->addEmptyDir('api');
|
||||
$zip->addEmptyDir('web');
|
||||
$zip->addEmptyDir('web/images');
|
||||
$api_files = Storage::files('public/assets/nodejs/projects/' . $project->title . '/zips/tests/api');
|
||||
foreach ($api_files as $file) {
|
||||
$zip->addFile(storage_path('app/' . $file), 'api/' . basename($file));
|
||||
}
|
||||
$api_files = Storage::files('public/assets/nodejs/projects/' . $project->title . '/zips/tests/web');
|
||||
foreach ($api_files as $file) {
|
||||
$zip->addFile(storage_path('app/' . $file), 'web/' . basename($file));
|
||||
}
|
||||
$image_files = Storage::files('public/assets/nodejs/projects/' . $project->title . '/zips/tests/web/images');
|
||||
foreach ($image_files as $file) {
|
||||
$zip->addFile(storage_path('app/' . $file), 'web/images/' . basename($file));
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
Process::fromShellCommandline("rm -rf {$tempDir}/tests")->run();
|
||||
} else {
|
||||
throw new Exception('Failed to create zip archive');
|
||||
}
|
||||
$media = $project->addMedia($zipPath)->toMediaCollection('project_zips', 'nodejs_public_projects_files');
|
||||
return response()->json($media->getUrl(), 200);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return response()->json(["message" => "no type was requested"], 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProjectExecutionStepController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProjectsDefaultFileStructureController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -0,0 +1,757 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\NodeJS\AddEnvFile;
|
||||
use App\Jobs\NodeJS\CloneRepository;
|
||||
use App\Jobs\NodeJS\CopyTestsFolder;
|
||||
use App\Jobs\NodeJS\DeleteTempDirectory;
|
||||
use App\Jobs\NodeJS\ExamineFolderStructure;
|
||||
use App\Jobs\NodeJS\NpmInstall;
|
||||
use App\Jobs\NodeJS\NpmRunStart;
|
||||
use App\Jobs\NodeJS\NpmRunTests;
|
||||
use App\Jobs\NodeJS\ReplacePackageJson;
|
||||
use App\Jobs\NodeJS\UnzipZipFiles;
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Project;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use App\Models\NodeJS\SubmissionHistory;
|
||||
use App\Models\NodeJS\TemporaryFile;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
use Yajra\DataTables\Facades\DataTables;
|
||||
|
||||
class SubmissionController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
$projects = Project::all();
|
||||
if ($request->ajax()) {
|
||||
$data = DB::connection('nodejsDB')->table('projects')
|
||||
->select(
|
||||
'projects.id',
|
||||
'projects.title',
|
||||
// DB::raw('(SELECT COUNT(DISTINCT submissions.id) FROM submissions WHERE submissions.project_id = projects.id AND submissions.user_id = ?) as submission_count'),
|
||||
DB::raw('(SELECT COUNT(*) FROM submission_histories INNER JOIN submissions ON submissions.id = submission_histories.submission_id WHERE submissions.project_id = projects.id AND submissions.user_id = ?) as attempts_count'),
|
||||
DB::raw('(SELECT status FROM submissions WHERE submissions.project_id = projects.id AND submissions.user_id = ? ORDER BY id DESC LIMIT 1) as submission_status')
|
||||
)
|
||||
->groupBy('projects.id', 'projects.title')
|
||||
->setBindings([
|
||||
// $user->id,
|
||||
$user->id,
|
||||
$user->id
|
||||
]);
|
||||
|
||||
return DataTables::of($data)
|
||||
->addIndexColumn()
|
||||
->addColumn('title', function ($row) {
|
||||
$title_button = '<a href="/nodejs/submissions/project/' . $row->id . '" class="underline text-secondary">' . $row->title . '</a>';
|
||||
return $title_button;
|
||||
})
|
||||
->addColumn('submission_status', function ($row) {
|
||||
$status = $row->submission_status ?? 'No Submission';
|
||||
$status_color = ($status == 'completed') ? 'green' : (($status == 'pending') ? 'blue' : (($status == 'processing') ? 'secondary' : 'red'));
|
||||
$status_button = $status != 'No Submission' ? '<span class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none bg-' . $status_color . '-100 text-' . $status_color . '-800">' . ucfirst($status) . '</span>'
|
||||
: '<span class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none bg-gray-100 text-gray-800">No Submission</span>';
|
||||
return $status_button;
|
||||
})
|
||||
->addColumn('action', function ($row) use ($user) {
|
||||
$submission = Submission::where('project_id', $row->id)->where('user_id', $user->id)->orderBy('id', 'DESC')->first();
|
||||
$buttons = '
|
||||
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
|
||||
<div @click="open = ! open">
|
||||
<button
|
||||
class="flex items-center text-sm font-medium text-gray-900 hover:text-gray-500 dark:text-white dark:hover:text-gray-300 hover:underline">
|
||||
<svg class="ml-1 h-5 w-5 text-gray-500 dark:text-gray-400"
|
||||
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"
|
||||
aria-hidden="true">
|
||||
<g id="Menu / Menu_Alt_02">
|
||||
<path id="Vector" d="M11 17H19M5 12H19M11 7H19" stroke="currentColor"
|
||||
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="transform opacity-0 scale-95"
|
||||
x-transition:enter-end="transform opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="transform opacity-100 scale-100"
|
||||
x-transition:leave-end="transform opacity-0 scale-95"
|
||||
class="absolute z-50 mt-2 w-48 rounded-md shadow-lg origin-top"
|
||||
style="display: none;"
|
||||
@click="open = false">
|
||||
<div class="rounded-md ring-1 ring-black ring-opacity-5 py-1 bg-white dark:bg-gray-700">
|
||||
';
|
||||
if ($submission !== null) {
|
||||
|
||||
$deleteButton = ' <a data-submission-id="' . $submission->id . '" data-request-type="delete" onclick="requestServer($(this))" class="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out">Delete submission</a> ';
|
||||
$restartButton = ' <a data-submission-id="' . $submission->id . '" data-request-type="restart" onclick="requestServer($(this))" class="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out">Restart submission</a> ';
|
||||
$changeSourceCodeButton = ' <a data-submission-id="' . $submission->id . '" data-request-type="change-source-code" onclick="requestServer($(this))" class="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out">Change source code</a> ';
|
||||
if ($submission->status == 'failed' || $submission->status == 'pending') {
|
||||
if (!$submission->isGithubUrl()) {
|
||||
$buttons .= $restartButton . $changeSourceCodeButton . $deleteButton . '</div></div>';
|
||||
} else {
|
||||
$buttons .= $restartButton . $deleteButton . '</div></div>';
|
||||
}
|
||||
} else if ($submission->status == 'processing') {
|
||||
$buttons .= $restartButton . $deleteButton . '</div></div>';
|
||||
} else if ($submission->status == 'completed') {
|
||||
$buttons .= $deleteButton . '</div></div>';
|
||||
} else {
|
||||
$buttons .= '</div></div>';
|
||||
}
|
||||
} else {
|
||||
$buttons = '';
|
||||
}
|
||||
return $buttons;
|
||||
})
|
||||
->editColumn('attempts_count', function ($row) {
|
||||
$attempts_count = $row->attempts_count ?? 0;
|
||||
return $attempts_count + 1;
|
||||
})
|
||||
->rawColumns(['title', 'submission_status', 'action'])
|
||||
->make(true);
|
||||
}
|
||||
return view('nodejs.submissions.index', compact('projects'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function upload(Request $request, $project_id)
|
||||
{
|
||||
if ($request->hasFile('folder_path')) {
|
||||
$project_title = Project::find($project_id)->title;
|
||||
|
||||
$file = $request->file('folder_path');
|
||||
$file_name = $file->getClientOriginalName();
|
||||
$folder_path = 'public/nodejs/tmp/submissions/' . $request->user()->id . '/' . $project_title;
|
||||
$file->storeAs($folder_path, $file_name);
|
||||
|
||||
TemporaryFile::create([
|
||||
'folder_path' => $folder_path,
|
||||
'file_name' => $file_name,
|
||||
]);
|
||||
|
||||
return $folder_path;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function submit(Request $request)
|
||||
{
|
||||
|
||||
try {
|
||||
$request->validate([
|
||||
'project_id' => 'required|exists:nodejsDB.projects,id',
|
||||
'folder_path' => 'required_without:github_url',
|
||||
'github_url' => 'required_without:folder_path',
|
||||
]);
|
||||
|
||||
if (Submission::where('project_id', $request->project_id)->where('user_id', $request->user()->id)->exists()) {
|
||||
return response()->json([
|
||||
'message' => 'Submission already exists',
|
||||
], 400);
|
||||
}
|
||||
|
||||
$submission = new Submission();
|
||||
$submission->user_id = $request->user()->id;
|
||||
$submission->project_id = $request->project_id;
|
||||
if ($request->has('folder_path')) {
|
||||
$submission->type = Submission::$FILE;
|
||||
$submission->path = $request->folder_path;
|
||||
|
||||
$temporary_file = TemporaryFile::where('folder_path', $request->folder_path)->first();
|
||||
|
||||
if ($temporary_file) {
|
||||
$path = storage_path('app/' . $request->folder_path . '/' . $temporary_file->file_name);
|
||||
$submission->addMedia($path)->toMediaCollection('submissions', 'nodejs_public_submissions_files');
|
||||
if ($this->is_dir_empty(storage_path('app/' . $request->folder_path))) {
|
||||
rmdir(storage_path('app/' . $request->folder_path));
|
||||
}
|
||||
$temporary_file->delete();
|
||||
}
|
||||
} else {
|
||||
$submission->type = Submission::$URL;
|
||||
$submission->path = $request->github_url;
|
||||
}
|
||||
$submission->status = Submission::$PENDING;
|
||||
$submission->start = now();
|
||||
$submission->save();
|
||||
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Submission created successfully',
|
||||
'submission' => $submission,
|
||||
], 201);
|
||||
} catch (\Throwable $th) {
|
||||
return response()->json([
|
||||
'message' => 'Submission failed',
|
||||
'error' => $th->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function showAllSubmissionsBasedOnProject(Request $request, $project_id)
|
||||
{
|
||||
$project = Project::find($project_id);
|
||||
$submissions = Submission::where('project_id', $project_id)
|
||||
->where('user_id', $request->user()->id)->get();
|
||||
$submission_history = SubmissionHistory::whereIn('submission_id', $submissions->pluck('id')->toArray())->get();
|
||||
|
||||
if (!$project) {
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
return view('nodejs.submissions.show', compact('project', 'submissions', 'submission_history'));
|
||||
}
|
||||
|
||||
public function show(Request $request, $submission_id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$submission = Submission::where('id', $request->submission_id)->where('user_id', $user->id)->first();
|
||||
if ($submission) {
|
||||
$steps = $submission->getExecutionSteps();
|
||||
return view('nodejs.submissions.show', compact('submission', 'steps'));
|
||||
}
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
|
||||
public function history(Request $request, $history_id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$submission = SubmissionHistory::where('id', $history_id)->where('user_id', $user->id)->first();
|
||||
if ($submission) {
|
||||
$steps = $submission->getExecutionSteps();
|
||||
return view('nodejs.submissions.show', compact('submission', 'steps'));
|
||||
}
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
|
||||
public function status(Request $request, $submission_id)
|
||||
{
|
||||
$isNotHistory = filter_var($request->isNotHistory, FILTER_VALIDATE_BOOLEAN);
|
||||
$user = Auth::user();
|
||||
$submission = $isNotHistory ? Submission::where('id', $submission_id)->where('user_id', $user->id)->first() : SubmissionHistory::where('id', $submission_id)->where('user_id', $user->id)->first();
|
||||
if (!$submission) {
|
||||
return response()->json([
|
||||
'message' => 'Submission not found',
|
||||
], 404);
|
||||
}
|
||||
$completion_percentage = round($submission->getTotalCompletedSteps() / $submission->getTotalSteps() * 100);
|
||||
if ($submission->status === Submission::$PENDING) {
|
||||
return $this->returnSubmissionResponse(($isNotHistory ? "Submission is processing" : "History"), $submission->status, $submission->results, $currentStep ?? null, $completion_percentage);
|
||||
} else if ($submission->status === Submission::$FAILED) {
|
||||
return $this->returnSubmissionResponse(($isNotHistory ? "Submission has failed" : "History"), $submission->status, $submission->results, null, $completion_percentage);
|
||||
} else if ($submission->status === Submission::$COMPLETED) {
|
||||
return $this->returnSubmissionResponse(($isNotHistory ? "Submission has completed" : "History"), $submission->status, $submission->results, null, $completion_percentage);
|
||||
} else if ($submission->status === Submission::$PROCESSING) {
|
||||
$step = $isNotHistory ? $submission->getCurrentExecutionStep() : null;
|
||||
if ($step) {
|
||||
return $this->returnSubmissionResponse(
|
||||
$isNotHistory ? 'Step ' . $step->executionStep->name . ' is ' . $submission->results->{$step->executionStep->name}->status : "History",
|
||||
$submission->status,
|
||||
$submission->results,
|
||||
$step,
|
||||
$completion_percentage
|
||||
);
|
||||
}
|
||||
return $this->returnSubmissionResponse(
|
||||
($isNotHistory ? 'Submission is processing meanwhile there is no step to execute' : "History"),
|
||||
$submission->status,
|
||||
$submission->results,
|
||||
$step,
|
||||
$completion_percentage
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function process(Request $request)
|
||||
{
|
||||
if ($request->submission_id == null || $request->isNotHistory == null) return response()->json([
|
||||
'message' => 'Submission ID is required',
|
||||
], 404);
|
||||
|
||||
$isNotHistory = filter_var($request->isNotHistory, FILTER_VALIDATE_BOOLEAN);
|
||||
$user = Auth::user();
|
||||
$submission = $isNotHistory ? Submission::where('id', $request->submission_id)->where('user_id', $user->id)->first() : SubmissionHistory::where('id', $request->submission_id)->where('user_id', $user->id)->first();
|
||||
|
||||
if ($submission) {
|
||||
$completion_percentage = round($submission->getTotalCompletedSteps() / $submission->getTotalSteps() * 100);
|
||||
if ($submission->status === Submission::$PENDING) {
|
||||
if ($isNotHistory) {
|
||||
$submission->initializeResults();
|
||||
$submission->updateStatus(Submission::$PROCESSING);
|
||||
$currentStep = $submission->getCurrentExecutionStep();
|
||||
}
|
||||
return $this->returnSubmissionResponse(($isNotHistory ? "Submission is processing" : "History"), $submission->status, $submission->results, $currentStep ?? null, $completion_percentage);
|
||||
} else if ($submission->status === Submission::$COMPLETED) {
|
||||
return $this->returnSubmissionResponse(($isNotHistory ? "Submission has completed" : "History"), $submission->status, $submission->results, null, $completion_percentage);
|
||||
} else if ($submission->status === Submission::$FAILED) {
|
||||
return $this->returnSubmissionResponse(($isNotHistory ? "Submission has failed" : "History"), $submission->status, $submission->results, null, $completion_percentage);
|
||||
} else if ($submission->status === Submission::$PROCESSING) {
|
||||
$step = $isNotHistory ? $submission->getCurrentExecutionStep() : null;
|
||||
if ($step) {
|
||||
if ($submission->results->{$step->executionStep->name}->status == Submission::$PENDING) {
|
||||
$submission->updateOneResult($step->executionStep->name, Submission::$PROCESSING, " ");
|
||||
switch ($step->executionStep->name) {
|
||||
case ExecutionStep::$CLONE_REPOSITORY:
|
||||
$this->lunchCloneRepositoryJob($submission, $submission->path, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$UNZIP_ZIP_FILES:
|
||||
$zipFileDir = $submission->getMedia('submissions')->first()->getPath();
|
||||
$this->lunchUnzipZipFilesJob($submission, $zipFileDir, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$EXAMINE_FOLDER_STRUCTURE:
|
||||
$this->lunchExamineFolderStructureJob($submission, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$ADD_ENV_FILE:
|
||||
$envFile = $submission->project->getMedia('project_files')->where('file_name', '.env')->first()->getPath();
|
||||
$this->lunchAddEnvFileJob($submission, $envFile, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$REPLACE_PACKAGE_JSON:
|
||||
$packageJson = $submission->project->getMedia('project_files')->where('file_name', 'package.json')->first()->getPath();
|
||||
$this->lunchReplacePackageJsonJob($submission, $packageJson, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$COPY_TESTS_FOLDER:
|
||||
$this->lunchCopyTestsFolderJob($submission, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$NPM_INSTALL:
|
||||
$this->lunchNpmInstallJob($submission, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$NPM_RUN_START:
|
||||
$this->lunchNpmRunStartJob($submission, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$NPM_RUN_TESTS:
|
||||
$this->lunchNpmRunTestsJob($submission, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
case ExecutionStep::$DELETE_TEMP_DIRECTORY:
|
||||
$this->lunchDeleteTempDirectoryJob($submission, $this->getTempDir($submission), $step);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->returnSubmissionResponse(
|
||||
$isNotHistory ? 'Step ' . $step->executionStep->name . ' is ' . $submission->results->{$step->executionStep->name}->status : "History",
|
||||
$submission->status,
|
||||
$submission->results,
|
||||
$step,
|
||||
$completion_percentage
|
||||
);
|
||||
}
|
||||
return $this->returnSubmissionResponse(
|
||||
($isNotHistory ? 'Submission is processing meanwhile there is no step to execute' : "History"),
|
||||
$submission->status,
|
||||
$submission->results,
|
||||
$step,
|
||||
$completion_percentage
|
||||
);
|
||||
}
|
||||
}
|
||||
return response()->json([
|
||||
'message' => 'Submission not found',
|
||||
], 404);
|
||||
}
|
||||
|
||||
public function returnSubmissionResponse($message, $status, $results, $next_step = null, $completion_percentage)
|
||||
{
|
||||
return response()->json([
|
||||
'message' => $message,
|
||||
'status' => $status,
|
||||
'results' => $results,
|
||||
'next_step' => $next_step,
|
||||
'completion_percentage' => $completion_percentage,
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function refresh(Request $request)
|
||||
{
|
||||
if ($request->submission_id == null) return response()->json([
|
||||
'message' => 'Submission ID is required',
|
||||
], 404);
|
||||
$user = Auth::user();
|
||||
$submission = Submission::where('id', $request->submission_id)->where('user_id', $user->id)->first();
|
||||
if ($submission and $submission->status === Submission::$FAILED) {
|
||||
// Create submission history
|
||||
$submission->createHistory("Submission has failed, so it has been refreshed");
|
||||
|
||||
// if npm is installed
|
||||
if ($submission->results->{ExecutionStep::$NPM_INSTALL}->status == Submission::$COMPLETED and !$this->is_dir_empty($this->getTempDir($submission))) {
|
||||
$submission->restartAfterNpmInstall();
|
||||
if ($submission->port != null) Process::fromShellCommandline('npx kill-port ' . $submission->port, null, null, null, 120)->run();
|
||||
} else {
|
||||
$commands = [];
|
||||
if ($submission->port != null) {
|
||||
$commands = [
|
||||
['npx', 'kill-port', $submission->port],
|
||||
['rm', '-rf', $this->getTempDir($submission)],
|
||||
];
|
||||
} else {
|
||||
$commands = [
|
||||
['rm', '-rf', $this->getTempDir($submission)],
|
||||
];
|
||||
}
|
||||
// Delete temp directory
|
||||
foreach ($commands as $command) {
|
||||
$process = new Process($command, null, null, null, 120);
|
||||
$process->run();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info('Command ' . implode(" ", $command) . ' is successful');
|
||||
} else {
|
||||
Log::error('Command ' . implode(" ", $command) . ' has failed ' . $process->getErrorOutput());
|
||||
}
|
||||
}
|
||||
|
||||
$submission->initializeResults();
|
||||
$submission->updateStatus(Submission::$PENDING);
|
||||
}
|
||||
|
||||
// Update submission status
|
||||
$submission->increaseAttempts();
|
||||
$submission->updatePort(null);
|
||||
$submission->restartTime();
|
||||
// Return response
|
||||
return response()->json([
|
||||
'message' => 'Submission has been refreshed',
|
||||
'status' => $submission->status,
|
||||
'results' => $submission->results,
|
||||
'attempts' => $submission->attempts,
|
||||
'completion_percentage' => 0,
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
|
||||
private function getTempDir($submission)
|
||||
{
|
||||
return storage_path('app/public/nodejs/tmp/submissions/' . $submission->user_id . '/' . $submission->project->title . '/' . $submission->id);
|
||||
}
|
||||
|
||||
private function is_dir_empty($dir)
|
||||
{
|
||||
if (!is_readable($dir)) return true;
|
||||
$handle = opendir($dir);
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if ($entry != "." && $entry != "..") {
|
||||
closedir($handle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
closedir($handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function replaceCommandArraysWithValues($step_variables, $values, $step)
|
||||
{
|
||||
return array_reduce($step_variables, function ($commands, $variableValue) use ($values) {
|
||||
return array_map(function ($command) use ($variableValue, $values) {
|
||||
return $command === $variableValue ? $values[$variableValue] : $command;
|
||||
}, $commands);
|
||||
}, $step->executionStep->commands);
|
||||
}
|
||||
|
||||
private function lunchCloneRepositoryJob($submission, $repoUrl, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ["{{repoUrl}}" => $repoUrl, '{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
dispatch(new CloneRepository($submission, $repoUrl, $tempDir, $commands))->onQueue(ExecutionStep::$CLONE_REPOSITORY);
|
||||
}
|
||||
|
||||
private function lunchUnzipZipFilesJob($submission, $zipFileDir, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{zipFileDir}}' => $zipFileDir, '{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
dispatch(new UnzipZipFiles($submission, $zipFileDir, $tempDir, $commands))->onQueue(ExecutionStep::$UNZIP_ZIP_FILES);
|
||||
}
|
||||
|
||||
private function lunchExamineFolderStructureJob($submission, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
dispatch(new ExamineFolderStructure($submission, $tempDir, $commands))->onQueue(ExecutionStep::$EXAMINE_FOLDER_STRUCTURE);
|
||||
}
|
||||
|
||||
private function lunchAddEnvFileJob($submission, $envFile, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{envFile}}' => $envFile, '{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
dispatch(new AddEnvFile($submission, $envFile, $tempDir, $commands))->onQueue(ExecutionStep::$ADD_ENV_FILE);
|
||||
}
|
||||
|
||||
private function lunchReplacePackageJsonJob($submission, $packageJson, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{packageJson}}' => $packageJson, '{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
dispatch(new ReplacePackageJson($submission, $packageJson, $tempDir, $commands))->onQueue(ExecutionStep::$REPLACE_PACKAGE_JSON);
|
||||
}
|
||||
|
||||
private function lunchCopyTestsFolderJob($submission, $tempDir, $step)
|
||||
{
|
||||
$testsDir = [
|
||||
'testsDirApi' => $submission->project->getMedia('project_tests_api'),
|
||||
'testsDirWeb' => $submission->project->getMedia('project_tests_web'),
|
||||
'testsDirImage' => $submission->project->getMedia('project_tests_images'),
|
||||
];
|
||||
// command 1: [1]cp [2]-r [3]{{testsDir}} [4]{{tempDir}}
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{testsDir}}' => $testsDir, '{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
$commandsArray = [];
|
||||
foreach ($testsDir['testsDirApi'] as $key => $value) {
|
||||
$commands[2] = $value->getPath();
|
||||
$commands[3] = $tempDir . '/tests/api';
|
||||
array_push($commandsArray, $commands);
|
||||
}
|
||||
foreach ($testsDir['testsDirWeb'] as $key => $value) {
|
||||
$commands[2] = $value->getPath();
|
||||
$commands[3] = $tempDir . '/tests/web';
|
||||
array_push($commandsArray, $commands);
|
||||
}
|
||||
foreach ($testsDir['testsDirImage'] as $key => $value) {
|
||||
$commands[2] = $value->getPath();
|
||||
$commands[3] = $tempDir . '/tests/web/images';
|
||||
array_push($commandsArray, $commands);
|
||||
}
|
||||
dispatch(new CopyTestsFolder($submission, $testsDir, $tempDir, $commandsArray))->onQueue(ExecutionStep::$COPY_TESTS_FOLDER);
|
||||
}
|
||||
|
||||
private function lunchNpmInstallJob($submission, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{options}}' => " "];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
dispatch(new NpmInstall($submission, $tempDir, $commands))->onQueue(ExecutionStep::$NPM_INSTALL);
|
||||
}
|
||||
|
||||
private function lunchNpmRunStartJob($submission, $tempDir, $step)
|
||||
{
|
||||
$commands = $step->executionStep->commands;
|
||||
dispatch_sync(new NpmRunStart($submission, $tempDir, $commands));
|
||||
}
|
||||
|
||||
private function lunchNpmRunTestsJob($submission, $tempDir, $step)
|
||||
{
|
||||
$commands = [];
|
||||
$tests = $submission->project->projectExecutionSteps->where('execution_step_id', $step->executionStep->id)->first()->variables;
|
||||
foreach ($tests as $testCommandValue) {
|
||||
$command = implode(" ", $step->executionStep->commands);
|
||||
$key = explode("=", $testCommandValue)[0];
|
||||
$value = explode("=", $testCommandValue)[1];
|
||||
$testName = str_replace($key, $value, $command);
|
||||
array_push($commands, explode(" ", $testName));
|
||||
}
|
||||
dispatch_sync(new NpmRunTests($submission, $tempDir, $commands));
|
||||
}
|
||||
|
||||
private function lunchDeleteTempDirectoryJob($submission, $tempDir, $step, $commands = null)
|
||||
{
|
||||
if ($commands == null) {
|
||||
$commands = $step->executionStep->commands;
|
||||
$step_variables = $step->variables;
|
||||
$values = ['{{tempDir}}' => $tempDir];
|
||||
$commands = $this->replaceCommandArraysWithValues($step_variables, $values, $step);
|
||||
$commands = [$commands];
|
||||
}
|
||||
dispatch_sync(new DeleteTempDirectory($submission, $tempDir, $commands));
|
||||
}
|
||||
|
||||
public function destroy(Request $request)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
if ($request->submission_id == null) return response()->json([
|
||||
'message' => 'Submission ID is required',
|
||||
], 404);
|
||||
$user = Auth::user();
|
||||
$submission = Submission::where('id', $request->submission_id)->where('user_id', $user->id)->first();
|
||||
if ($submission) {
|
||||
$submission->delete();
|
||||
// delete temp directory and media
|
||||
if ($submission->type == Submission::$FILE) {
|
||||
$submission->getMedia('submissions')->each(function ($media) {
|
||||
$media->delete();
|
||||
});
|
||||
}
|
||||
$tempDir = $this->getTempDir($submission);
|
||||
if (!$this->is_dir_empty($tempDir)) {
|
||||
Process::fromShellCommandline('rm -rf ' . $tempDir)->run();
|
||||
}
|
||||
return response()->json([
|
||||
'message' => 'Submission has been deleted successfully',
|
||||
], 200);
|
||||
}
|
||||
return response()->json([
|
||||
'message' => 'Submission not found',
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function restart(Request $request)
|
||||
{
|
||||
if ($request->ajax()) {
|
||||
if ($request->submission_id == null) return response()->json([
|
||||
'message' => 'Submission ID is required',
|
||||
], 404);
|
||||
$user = Auth::user();
|
||||
$submission = Submission::where('id', $request->submission_id)->where('user_id', $user->id)->first();
|
||||
if ($submission) {
|
||||
$submission->createHistory("Submission has been restarted");
|
||||
|
||||
if ($submission->port != null) {
|
||||
$commands = [
|
||||
['npx', 'kill-port', $submission->port],
|
||||
['rm', '-rf', $this->getTempDir($submission)],
|
||||
];
|
||||
} else {
|
||||
$commands = [
|
||||
['rm', '-rf', $this->getTempDir($submission)],
|
||||
];
|
||||
}
|
||||
// Delete temp directory
|
||||
foreach ($commands as $command) {
|
||||
if (!$this->is_dir_empty($this->getTempDir($submission))) {
|
||||
$process = new Process($command, null, null, null, 120);
|
||||
$process->run();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info('Command ' . implode(" ", $command) . ' is successful');
|
||||
} else {
|
||||
Log::error('Command ' . implode(" ", $command) . ' has failed ' . $process->getErrorOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$submission->restart();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Submission has been restarted successfully',
|
||||
], 200);
|
||||
}
|
||||
return response()->json([
|
||||
'message' => 'Submission not found',
|
||||
], 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function changeSourceCode($submission_id)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$submission = Submission::where('id', $submission_id)->where('user_id', $user->id)->first();
|
||||
if ($submission) {
|
||||
return view('nodejs.submissions.change_source_code', compact('submission'));
|
||||
}
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
try {
|
||||
$request->validate([
|
||||
'submission_id' => 'required|exists:nodejsDB.submissions,id',
|
||||
'folder_path' => 'required_without:github_url',
|
||||
'github_url' => 'required_without:folder_path',
|
||||
]);
|
||||
|
||||
|
||||
$user = Auth::user();
|
||||
$submission = Submission::where('id', $request->submission_id)->where('user_id', $user->id)->first();
|
||||
|
||||
$submission->createHistory("Code has been changed");
|
||||
|
||||
if (!$submission->isGithubUrl()) {
|
||||
$submission->getMedia('submissions')->each(function ($media) {
|
||||
$media->delete();
|
||||
});
|
||||
}
|
||||
|
||||
// delete temp directory if is not empty
|
||||
$tempDir = $this->getTempDir($submission);
|
||||
if (!$this->is_dir_empty($tempDir)) {
|
||||
Process::fromShellCommandline('rm -rf ' . $tempDir)->run();
|
||||
}
|
||||
|
||||
if ($request->has('folder_path')) {
|
||||
$submission->type = Submission::$FILE;
|
||||
$submission->path = $request->folder_path;
|
||||
|
||||
$temporary_file = TemporaryFile::where('folder_path', $request->folder_path)->first();
|
||||
|
||||
if ($temporary_file) {
|
||||
$path = storage_path('app/' . $request->folder_path . '/' . $temporary_file->file_name);
|
||||
$submission->addMedia($path)->toMediaCollection('submissions', 'nodejs_public_submissions_files');
|
||||
if ($this->is_dir_empty(storage_path('app/' . $request->folder_path))) {
|
||||
rmdir(storage_path('app/' . $request->folder_path));
|
||||
}
|
||||
$temporary_file->delete();
|
||||
}
|
||||
} else {
|
||||
$submission->type = Submission::$URL;
|
||||
$submission->path = $request->github_url;
|
||||
}
|
||||
$submission->save();
|
||||
$submission->restart();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Submission created successfully',
|
||||
'submission' => $submission,
|
||||
], 201);
|
||||
} catch (\Throwable $th) {
|
||||
return response()->json([
|
||||
'message' => 'Submission failed',
|
||||
'error' => $th->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function downloadHistory(Request $request, $id)
|
||||
{
|
||||
if (!$request->type) {
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
|
||||
$user = Auth::user();
|
||||
$submission = $request->type == 'history' ? SubmissionHistory::where('id', $id)->where('user_id', $user->id)->first() : Submission::where('id', $id)->where('user_id', $user->id)->first();
|
||||
|
||||
if (!$submission) {
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
|
||||
if ($request->type == 'current' && $submission->status != Submission::$COMPLETED && $submission->status != Submission::$FAILED) {
|
||||
return redirect()->route('submissions');
|
||||
}
|
||||
$results = json_encode($submission->results, JSON_PRETTY_PRINT);
|
||||
$results_array = json_decode($results, true);
|
||||
uasort($results_array, function ($a, $b) {
|
||||
return $a['order'] - $b['order'];
|
||||
});
|
||||
$jsonResults = json_encode($results_array, JSON_PRETTY_PRINT);
|
||||
$filename = 'submission_' . $submission->project->title . '_' . $user->id . '_' . $id . '_' . now()->format('Y-m-d_H-i-s') . '.json';
|
||||
$headers = [
|
||||
'Content-Type' => 'application/json',
|
||||
'Content-Disposition' => 'attachment; filename=' . $filename,
|
||||
];
|
||||
|
||||
return response()->streamDownload(function () use ($submission, $user, $jsonResults) {
|
||||
echo "Submission for project: " . $submission->project->title . " | User: " . $user->name . "\n";
|
||||
echo "====================================================================================================\n";
|
||||
echo $jsonResults;
|
||||
echo "\n====================================================================================================";
|
||||
}, $filename, $headers);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SubmissionHistoryController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\NodeJS\Student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class WelcomeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('nodejs.welcome');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\PHP;
|
||||
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\NodeJS\Project;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Yajra\DataTables\Facades\DataTables;
|
||||
use App\Models\PHP\Topic;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class PHPController extends Controller{
|
||||
public function index(){
|
||||
|
||||
$actual = "";
|
||||
$topics = Topic::all();
|
||||
$topicsCount = count($topics);
|
||||
|
||||
return view('php.student.material.index',[
|
||||
'result_up' => $actual,
|
||||
'topics' => $topics,
|
||||
'topicsCount' => $topicsCount,
|
||||
]);
|
||||
}
|
||||
|
||||
function php_material_detail(){
|
||||
$phpid = isset($_GET['phpid']) ? $_GET['phpid'] : '';
|
||||
$start = isset($_GET['start']) ? $_GET['start'] : '';
|
||||
|
||||
$results = DB::select("select * from php_topics_detail where id_topics = $phpid and id ='$start' ");
|
||||
foreach($results as $r){
|
||||
|
||||
if( $phpid == $r->id_topics){
|
||||
if(empty($r->file_name)){
|
||||
$contain = $r->description;
|
||||
$pdf_reader = 0;
|
||||
}else{
|
||||
$contain = $r->file_name;
|
||||
$pdf_reader = 1;
|
||||
}
|
||||
|
||||
$html_start = $this->html_persyaratan($contain,$pdf_reader);
|
||||
}else{
|
||||
$html_start = "";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$topics = Topic::all();
|
||||
$detail = Topic::findorfail($phpid);
|
||||
$topicsCount = count($topics);
|
||||
$detailCount = ($topicsCount/$topicsCount)*10;
|
||||
return view('php.student.material.topics_detail',[
|
||||
'row' => $detail,
|
||||
'topics' => $topics,
|
||||
'phpid' => $phpid,
|
||||
'html_start' => $html_start,
|
||||
'pdf_reader' => $pdf_reader ,
|
||||
'topicsCount' => $topicsCount ,
|
||||
'detailCount' => $detailCount ,
|
||||
]);
|
||||
}
|
||||
function html_start(){
|
||||
$html ="<div style='text-align:center;font-size:18px'><em>Modul kelas Belajar Pengembangan Aplikasi Android Intermediate dalam bentuk cetak (buku) maupun elektronik sudah didaftarkan ke Dirjen HKI, Kemenkumham RI. Segala bentuk penggandaan dan atau komersialisasi, sebagian atau seluruh bagian, baik cetak maupun elektronik terhadap modul kelas <em>Belajar Pengembangan Aplikasi Android Intermediate</em> tanpa izin formal tertulis kepada pemilik hak cipta akan diproses melalui jalur hukum.</em></div>";
|
||||
return $html;
|
||||
}
|
||||
function html_persyaratan($desc,$pdf_reader){
|
||||
$html = $desc;
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function upload(Request $request)
|
||||
{
|
||||
if ($request->hasFile('upload')) {
|
||||
$originName = $request->file('upload')->getClientOriginalName();
|
||||
$fileName = pathinfo($originName, PATHINFO_FILENAME);
|
||||
$extension = $request->file('upload')->getClientOriginalExtension();
|
||||
$fileName = $fileName . '_' . time() . '.' . $extension;
|
||||
|
||||
$request->file('upload')->move(public_path('media'), $fileName);
|
||||
|
||||
$url = asset('media/' . $fileName);
|
||||
|
||||
return response()->json(['fileName' => $fileName, 'uploaded' => 1, 'url' => $url]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function send_task(){
|
||||
$phpid = isset($_GET['phpid']) ? $_GET['phpid'] : '';
|
||||
$task = isset($_GET['task']) ? $_GET['task'] : '';
|
||||
|
||||
$task_db = DB::table('php_task')
|
||||
->where('id_topics', $phpid)
|
||||
->where('id', $task)
|
||||
->first();
|
||||
if($task_db->id == $task){
|
||||
$html_start = $this->html_task();
|
||||
}else{
|
||||
$html_start = "";
|
||||
}
|
||||
|
||||
|
||||
$pdf_reader = 0;
|
||||
$topics = Topic::all();
|
||||
$detail = Topic::findorfail($phpid);
|
||||
$topicsCount = count($topics);
|
||||
$persen = ($topicsCount/$topicsCount)*10;
|
||||
session(['params' => $persen]);
|
||||
return view('php.student.material.topics_detail',[
|
||||
'row' => $detail,
|
||||
'topics' => $topics,
|
||||
'phpid' => $phpid,
|
||||
'html_start' => $html_start,
|
||||
'pdf_reader' => $pdf_reader ,
|
||||
'detailCount' => $persen,
|
||||
]);
|
||||
|
||||
}
|
||||
function html_task(){
|
||||
return view('php.student.task.form_submission_task',[]);
|
||||
}
|
||||
function php_admin(){
|
||||
return view('php.admin.material.upload_materi',[]);
|
||||
|
||||
}
|
||||
|
||||
function task_submission(Request $request){
|
||||
$this->validate($request, [
|
||||
'file' => 'required',
|
||||
|
||||
]);
|
||||
|
||||
// menyimpan data file yang diupload ke variabel $file
|
||||
$file = $request->file('file');
|
||||
|
||||
$file_name = Auth::user()->name.'_'.$file->getClientOriginalName();
|
||||
|
||||
Storage::disk('local')->makeDirectory('private/'.Auth::user()->name);
|
||||
Storage::disk('local')->put('/private/'.Auth::user()->name.'/'.$file_name,File::get($file));
|
||||
$userName = Auth::user()->name;
|
||||
Session::put('user_name', $userName);
|
||||
$user_name = Session::get('user_name');
|
||||
$name = Session::put('ori_file_name', $file_name);
|
||||
|
||||
$path = storage_path("app/private/{$userName}/{$file_name}");
|
||||
Session::put('path', $path);
|
||||
|
||||
$val = session('key');
|
||||
DB::select("TRUNCATE TABLE php_user_submits");
|
||||
DB::insert("insert into php_user_submits(userid) values ('$val')");
|
||||
|
||||
return redirect('/php/send-task?phpid=7&task=1')->with('status', 'File Berhasil Diupload!');
|
||||
|
||||
}
|
||||
|
||||
|
||||
function unittesting(){
|
||||
$val = session('key');
|
||||
DB::select("TRUNCATE TABLE php_user_submits");
|
||||
DB::insert("insert into php_user_submits(userid) values ('$val')");
|
||||
|
||||
$path_test = base_path("phpunit.xml");
|
||||
$path = base_path("vendor\bin\phpunit -c $path_test");
|
||||
$output = shell_exec($path);
|
||||
|
||||
//echo dd($output);
|
||||
//echo json_encode($output);
|
||||
$string = htmlentities($output);
|
||||
$string = str_replace("\n", ' ', $string);
|
||||
|
||||
$pattern = '/PHPUnit\s+(\d+\.\d+\.\d+).*Runtime:\s+PHP\s+(\d+\.\d+\.\d+).*Time:\s+(\d+:\d+\.\d+),\s+Memory:\s+(\d+\.\d+)\s+MB\s+OK\s+\((\d+)\stests,\s+(\d+)\sassertions\)/';
|
||||
|
||||
if (preg_match($pattern, $string, $matches)) {
|
||||
$phpUnitVersion = $matches[1];
|
||||
$phpVersion = $matches[2];
|
||||
$executionTime = $matches[3];
|
||||
$memoryUsage = $matches[4];
|
||||
$numTests = $matches[5];
|
||||
$numAssertions = $matches[6];
|
||||
|
||||
// Output the extracted information
|
||||
echo "PHPUnit version: $phpUnitVersion <br />";
|
||||
echo "PHP version: $phpVersion <br />";
|
||||
echo "Execution time: $executionTime <br />";
|
||||
echo "Memory usage: $memoryUsage MB <br />";
|
||||
echo "Number of tests: $numTests <br />";
|
||||
echo "Number of assertions: $numAssertions <br />";
|
||||
|
||||
$ok_position = strpos($string, 'OK');
|
||||
if ($ok_position !== false) {
|
||||
$ok_part = substr($string, $ok_position);
|
||||
echo "Tests Run : ". $ok_part;
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
$string = json_encode($output);
|
||||
$text = str_replace("\n", ' ', $output);
|
||||
// Define patterns to extract relevant information
|
||||
$pattern_phpunit_version = '/PHPUnit\s+(\d+\.\d+\.\d+)/';
|
||||
$pattern_php_runtime = '/Runtime:\s+PHP\s+([\d.]+)/';
|
||||
$pattern_configuration = '/Configuration:\s+(.+)/';
|
||||
$pattern_failure_count = '/There was (\d+) failure/';
|
||||
$pattern_failure_test_case = '/Failed asserting that \'(.*?)\' contains \'(.*?)\'./';
|
||||
$pattern_failure_location = '/(C:\\\\.*?\\.php):(\d+)/';
|
||||
|
||||
// Perform matching
|
||||
preg_match($pattern_phpunit_version, $text, $matches_phpunit_version);
|
||||
preg_match($pattern_php_runtime, $text, $matches_php_runtime);
|
||||
preg_match($pattern_configuration, $text, $matches_configuration);
|
||||
preg_match($pattern_failure_count, $text, $matches_failure_count);
|
||||
preg_match($pattern_failure_test_case, $text, $matches_failure_test_case);
|
||||
preg_match($pattern_failure_location, $text, $matches_failure_location);
|
||||
|
||||
// Extracted information
|
||||
$phpunit_version = isset($matches_phpunit_version[1]) ? $matches_phpunit_version[1] : "Not found";
|
||||
$php_runtime = isset($matches_php_runtime[1]) ? $matches_php_runtime[1] : "Not found";
|
||||
$configuration_path = isset($matches_configuration[1]) ? $matches_configuration[1] : "Not found";
|
||||
$num_failures = isset($matches_failure_count[1]) ? $matches_failure_count[1] : "Not found";
|
||||
$failed_assertion = isset($matches_failure_test_case[1]) ? htmlspecialchars($matches_failure_test_case[1]) : "Not found";
|
||||
$expected_content = isset($matches_failure_test_case[2]) ? htmlspecialchars($matches_failure_test_case[2]) : "Not found";
|
||||
$failure_location = isset($matches_failure_location[1]) ? $matches_failure_location[1] : "Not found";
|
||||
$failure_line = isset($matches_failure_location[2]) ? $matches_failure_location[2] : "Not found";
|
||||
|
||||
// Output extracted information
|
||||
echo "PHPUnit version: $phpunit_version <br >";
|
||||
echo "PHP Runtime: $php_runtime <br >";
|
||||
echo "Configuration path: $configuration_path <br >";
|
||||
echo "Number of failures: $num_failures <br >";
|
||||
echo "Failed assertion: $failed_assertion <br >";
|
||||
echo "Expected content: $expected_content <br >";
|
||||
echo "Failure location: $failure_location <br >";
|
||||
echo "Failure line: $failure_line <br >";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function session_progress(){
|
||||
session(['params' => $_POST['params']]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\PHP;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Yajra\DataTables\Facades\DataTables;
|
||||
use App\Models\PHP\Topic;
|
||||
use App\Models\PHP\Topic_detail;
|
||||
use Illuminate\Support\Facades\Redirect;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PHPDosenController extends Controller{
|
||||
|
||||
function topics(){
|
||||
$topics = Topic::all();
|
||||
return view('php.teacher.topics',[
|
||||
'topics' => $topics,
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
function add_topics( Request $request, $id ){
|
||||
$detail = Topic::findorfail($id);
|
||||
$results = DB::select('SELECT * FROM php_topics_detail WHERE id_topics = ?', [$id]);
|
||||
return view('php.teacher.topics_detail',[
|
||||
'results' => $results,
|
||||
'detail' => $detail->title,
|
||||
'id' => $id,
|
||||
]);
|
||||
}
|
||||
|
||||
function simpan( Request $request){
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
'caption' => 'required|string|max:255',
|
||||
'editor' => 'required|string',
|
||||
'id' => 'required|int|max:11',
|
||||
'materials' => 'required|file|max:10240', // Example: max 10MB
|
||||
]);
|
||||
// Periksa apakah file yang diunggah bukan PDF
|
||||
if ($request->file('materials')->getClientOriginalExtension() !== 'pdf') {
|
||||
return redirect()->back()->with('error', 'Materi harus berupa file PDF.');
|
||||
}
|
||||
|
||||
$originName = $request->file('materials')->getClientOriginalName();
|
||||
$fileName = pathinfo($originName, PATHINFO_FILENAME);
|
||||
$extension = $request->file('materials')->getClientOriginalExtension();
|
||||
$fileName = $fileName . '_' . time() . '.' . $extension;
|
||||
$new_name = str_replace(" ",'_',$fileName);
|
||||
|
||||
$path = $request->file('materials')->move(public_path('php/document/A1_BASIC_PHP'), $new_name);
|
||||
Topic_detail::create([
|
||||
'title' => $validatedData['title'],
|
||||
'id_topics' => $validatedData['id'],
|
||||
'controller' => $validatedData['caption'],
|
||||
'description' => $validatedData['editor'],
|
||||
'folder_path' => $path, // Save the path to the uploaded file
|
||||
'file_name' => $new_name,
|
||||
]);
|
||||
|
||||
$id = $validatedData['id'];
|
||||
return Redirect::to("/php/teacher/topics/add/$id")->with('message', 'Data Berhasil Diinputkan');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Http\Controllers\PHP\Student;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class DashboardUnitControllers extends Controller
|
||||
{
|
||||
//
|
||||
public function index(){
|
||||
$page_title = "Persetujuan Hak Cipta";
|
||||
$page_link = "02";
|
||||
$content = "";
|
||||
$form_upload = 'N';
|
||||
return view('phpunit.dashboard', ['content' => $content,
|
||||
'page_title' => $page_title,
|
||||
'page_link' => $page_link,
|
||||
'form_upload' => $form_upload
|
||||
])->render();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\PHP\Student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
class StudikasusController extends Controller
|
||||
{
|
||||
//
|
||||
function index(){
|
||||
echo 1;
|
||||
}
|
||||
////
|
||||
function upload_jawaban(Request $request){
|
||||
$this->validate($request, [
|
||||
'file' => 'required',
|
||||
|
||||
]);
|
||||
|
||||
// menyimpan data file yang diupload ke variabel $file
|
||||
$file = $request->file('file');
|
||||
|
||||
// nama file
|
||||
echo 'File Name: '.$file->getClientOriginalName();
|
||||
echo '<br>';
|
||||
|
||||
// ekstensi file
|
||||
echo 'File Extension: '.$file->getClientOriginalExtension();
|
||||
echo '<br>';
|
||||
|
||||
// real path
|
||||
echo 'File Real Path: '.$file->getRealPath();
|
||||
echo '<br>';
|
||||
|
||||
// ukuran file
|
||||
echo 'File Size: '.$file->getSize();
|
||||
echo '<br>';
|
||||
|
||||
// tipe mime
|
||||
echo 'File Mime Type: '.$file->getMimeType();
|
||||
|
||||
// isi dengan nama folder tempat kemana file diupload
|
||||
|
||||
$file_name = Auth::user()->name.'_'.$file->getClientOriginalName();
|
||||
//$file->move($tujuan_upload,$file->getClientOriginalName());
|
||||
Storage::disk('local')->makeDirectory('private/'.Auth::user()->name);
|
||||
Storage::disk('local')->put('/private/'.Auth::user()->name.'/'.$file_name,File::get($file));
|
||||
$userName = Auth::user()->name;
|
||||
Session::put('user_name', $userName);
|
||||
$user_name = Session::get('user_name');
|
||||
$name = "Udjir_GuideA1.html";
|
||||
Session::put('sess_path', base_path("storage\app\private\\$user_name\\$name"));
|
||||
|
||||
return redirect('/phpunit/studi-kasus/projects/02')->with('status', 'File Berhasil Diupload!');
|
||||
}
|
||||
function unittesting(){
|
||||
$path_test = base_path("phpunit.xml");
|
||||
$path = base_path("vendor\bin\phpunit -c $path_test");
|
||||
$output = shell_exec($path);
|
||||
echo dd($output);
|
||||
//echo json_encode($output);
|
||||
}
|
||||
function result_test(){
|
||||
|
||||
$path_test = base_path("phpunit.xml");
|
||||
$path = base_path("vendor\bin\phpunit -c $path_test");
|
||||
$output = shell_exec($path);
|
||||
// dd($output);
|
||||
$string = json_encode($output);
|
||||
$pattern = '/OK \((\d+ test), (\d+ assertion)\)/';
|
||||
if (preg_match($pattern, $string, $matches)) {
|
||||
$numberOfTest = $matches[0];
|
||||
$numberOfTests = $matches[1];
|
||||
$numberOfAssertions = $matches[2];
|
||||
echo "Status Tests : $numberOfTest\n";
|
||||
} else {
|
||||
echo "Pattern not found.";
|
||||
|
||||
}
|
||||
}
|
||||
function upload_test(){
|
||||
|
||||
$actual = base_path('storage\app\private\Udjir\Udjir_GuideA1.php');
|
||||
//$filename = $actual."/temp/$file";
|
||||
//include "$filename";
|
||||
$php_output = shell_exec("C:\wamp64\bin\php\php7.4.33\php.exe $actual 2>&1");
|
||||
//echo $php_output;
|
||||
|
||||
|
||||
}
|
||||
//
|
||||
function projects(string $id){
|
||||
$page_title = "Studi Kasus $id";
|
||||
$page_link = "$id";
|
||||
$content = "
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
Buatlah Tampilan Seperti Berikut :
|
||||
<br />
|
||||
<br />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
";
|
||||
$form_upload = 'Y';
|
||||
return view('phpunit.dashboard', ['content' => $content,
|
||||
'page_title' => $page_title,
|
||||
'page_link' => $page_link,
|
||||
'form_upload'=> $form_upload])->with('form_upload','Y');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\PHP\Student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\PHP\TaskSubmission;
|
||||
use App\Models\PHP\UserCheck;
|
||||
|
||||
class WelcomeController extends Controller
|
||||
{
|
||||
|
||||
function result_test(){
|
||||
|
||||
$uname = DB::select("SELECT * FROM users
|
||||
WHERE name COLLATE utf8mb4_general_ci IN (SELECT userid FROM php_user_submits) ");
|
||||
|
||||
if (!empty($uname)) {
|
||||
$firstUser = reset($uname); // Get the first element of the array
|
||||
$sess_name = isset($firstUser->name) ? $firstUser->name : ''; // Access the 'name' property of the first user
|
||||
}
|
||||
|
||||
$task = DB::table('php_submits_submission')->where('username', "$sess_name")->first();
|
||||
$username = $task->username;
|
||||
$userfile = $task->userfile;
|
||||
$ket = htmlspecialchars($task->ket);
|
||||
|
||||
$actual = storage_path("app/private/{$username}/{$userfile}");
|
||||
$php_output = shell_exec("PHP $actual 2>&1");
|
||||
$test = str_replace(array("\r\n","\r","\n"," "),"",htmlspecialchars($php_output));
|
||||
|
||||
return view('php.student.task.result_submssion_task',[
|
||||
'result_up' => $test,
|
||||
]);
|
||||
|
||||
// echo "$value == $sess_name";
|
||||
|
||||
}
|
||||
function get_session(){
|
||||
if (Auth::check()) {
|
||||
$value = Auth::user()->name;
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'email' => ['required', 'string', 'email'],
|
||||
'password' => ['required', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate the request's credentials.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function authenticate(): void
|
||||
{
|
||||
$this->ensureIsNotRateLimited();
|
||||
|
||||
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
|
||||
RateLimiter::hit($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.failed'),
|
||||
]);
|
||||
}
|
||||
|
||||
RateLimiter::clear($this->throttleKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the login request is not rate limited.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function ensureIsNotRateLimited(): void
|
||||
{
|
||||
if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event(new Lockout($this));
|
||||
|
||||
$seconds = RateLimiter::availableIn($this->throttleKey());
|
||||
|
||||
throw ValidationException::withMessages([
|
||||
'email' => trans('auth.throttle', [
|
||||
'seconds' => $seconds,
|
||||
'minutes' => ceil($seconds / 60),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the rate limiting throttle key for the request.
|
||||
*/
|
||||
public function throttleKey(): string
|
||||
{
|
||||
return Str::transliterate(Str::lower($this->input('email')).'|'.$this->ip());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ProfileUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['string', 'max:255'],
|
||||
'email' => ['email', 'max:255', Rule::unique(User::class)->ignore($this->user()->id)],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\StudentSubmission;
|
||||
use App\Models\Task;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
|
||||
class StudentSubmissionController extends Controller
|
||||
{
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
|
||||
// dd($request->all());
|
||||
// $data =
|
||||
$request->validate([
|
||||
'task_id' => 'required|exists:tasks,id',
|
||||
'file' => 'required|file|mimes:txt,py' // Validasi untuk file Python
|
||||
]);
|
||||
|
||||
// dd($data);
|
||||
|
||||
$user = auth()->user();
|
||||
$task = Task::findOrFail($request->task_id);
|
||||
$uploadedFile = $request->file('file');
|
||||
$expectedFileName = $this->getExpectedFileName($task);
|
||||
|
||||
// Simpan file dengan nama yang ditentukan
|
||||
$filePath = $uploadedFile ->storeAs('./public/submissions', $expectedFileName, 'local');
|
||||
|
||||
//Menambahkan validasi nama file sebelum menyimpan file:
|
||||
if (!$this->isFileNameValid($uploadedFile->getClientOriginalName(), $expectedFileName)) {
|
||||
return redirect()->back()->with('error', 'Nama file tidak sesuai. Harap gunakan format: ' . $expectedFileName);
|
||||
}
|
||||
|
||||
|
||||
// Log untuk memastikan file tersimpan dengan benar
|
||||
\Log::info('File saved at: ' . $filePath);
|
||||
\Log::info('File content after save: ' . Storage::disk('public')->get($filePath));
|
||||
|
||||
// Tambahkan kode debugging di sini
|
||||
\Log::info('File uploaded: ' . $filePath);
|
||||
if (Storage::disk('local')->exists($filePath)) {
|
||||
\Log::info('File exists at: ' . Storage::disk('local')->path($filePath));
|
||||
} else {
|
||||
\Log::error('File not found at: ' . Storage::disk('local')->path($filePath));
|
||||
}
|
||||
|
||||
|
||||
$task = Task::findOrFail($request->task_id);
|
||||
$existingSubmission = StudentSubmission::where('user_id', $user->id)
|
||||
->where('task_id', $task->id)
|
||||
->first();
|
||||
if ($existingSubmission) {
|
||||
// Jika sudah ada, update submission yang ada
|
||||
$submissionCount = $existingSubmission->submission_count + 1;
|
||||
$existingSubmission->update([
|
||||
'submission_count' => $submissionCount,
|
||||
'file_path' => $filePath,
|
||||
]);
|
||||
} else {
|
||||
// Jika belum ada, buat submission baru
|
||||
$submissionCount = 1;
|
||||
StudentSubmission::create([
|
||||
'user_id' => $user->id,
|
||||
'task_id' => $task->id,
|
||||
'submission_count' => $submissionCount,
|
||||
'file_path' => $filePath,
|
||||
]);
|
||||
}
|
||||
|
||||
// Di sini Anda bisa menambahkan logika untuk menjalankan tes
|
||||
// dan menyimpan hasilnya ke $submission->test_result
|
||||
|
||||
// return redirect()->back()->with('success', 'Submission berhasil.');
|
||||
}
|
||||
// }
|
||||
private function getExpectedFileName(Task $task)
|
||||
{ // mengekstrak nama file pdf dan merubah namanya menjadi answer_{path}.py
|
||||
// untuk pathnya adalah path dari bab dan percobaan nya
|
||||
// misalnya jika pathnya adalah bab1_Percobaan1 maka nama file nya akan answer_bab1_Percobaan1.py
|
||||
// dari hasil ini kemudian akan jadi pembanding untuk nama file yang diupload
|
||||
|
||||
// Ambil nama file PDF dari kolom pdf_path pada tabel tasks
|
||||
$pdfPath = $task->pdf_path;
|
||||
|
||||
// Ekstrak nama file dari path
|
||||
$pdfFileName = basename($pdfPath);
|
||||
|
||||
// Ekstrak bagian yang diperlukan (misalnya 'bab1_Percobaan1')
|
||||
preg_match('/chapter\d+_Experiments\d+/i', $pdfFileName, $matches);
|
||||
$extractedPart = $matches[0] ?? '';
|
||||
|
||||
// Buat nama file yang diharapkan
|
||||
return 'answer_' . strtolower($extractedPart) . '.py';
|
||||
}
|
||||
|
||||
private function isFileNameValid($uploadedFileName, $expectedFileName)
|
||||
{
|
||||
return strtolower($uploadedFileName) === strtolower($expectedFileName);
|
||||
}
|
||||
|
||||
public function storeTestResult(Request $request)
|
||||
{
|
||||
// Validasi request jika diperlukan
|
||||
$request->validate([
|
||||
'output' => 'required|string',
|
||||
'task_id' => 'required|exists:tasks,id',
|
||||
]);
|
||||
|
||||
// Mendapatkan pengguna yang sedang login
|
||||
$user_id = auth()->user()->id;
|
||||
|
||||
// Mendapatkan data dari request
|
||||
$output = $request->input('output');
|
||||
$task_id = $request->input('task_id');
|
||||
|
||||
// Mencari submission yang sesuai
|
||||
$submission = StudentSubmission::where('user_id', $user_id)
|
||||
->where('task_id', $task_id)
|
||||
->first();
|
||||
|
||||
if ($submission) {
|
||||
// Update kolom test_result dengan data output
|
||||
$submission->update([
|
||||
'test_result' => $output,
|
||||
]);
|
||||
|
||||
// Mengembalikan respons sukses
|
||||
return response()->json(['message' => 'Data berhasil disimpan.', 'data' => $submission]);
|
||||
} else {
|
||||
// Jika submission tidak ditemukan, kembalikan respons error
|
||||
return response()->json(['message' => 'Submission tidak ditemukan.'], 404);
|
||||
}
|
||||
}
|
||||
|
||||
// public function show($id)
|
||||
// {
|
||||
// $submission = StudentSubmission::findOrFail($id);
|
||||
// $task = Task::findOrFail($id);
|
||||
// return view('task_detail', compact('submission','task'));
|
||||
// }
|
||||
|
||||
private function runTest(StudentSubmission $submission)
|
||||
{
|
||||
$testFilePath = storage_path('app/public/test_file/test_' . $submission->task_id . '.py');
|
||||
$submissionFilePath = storage_path('app/' . $submission->file_path);
|
||||
|
||||
$process = new Process(['python', $testFilePath, $submissionFilePath]);
|
||||
$process->run();
|
||||
|
||||
return $process->getOutput();
|
||||
}
|
||||
}
|
||||
70
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Http/Kernel.php
Normal file
70
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Http/Kernel.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's middleware aliases.
|
||||
*
|
||||
* Aliases may be used instead of class names to conveniently assign middleware to routes and groups.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
|
||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'teacher' => \App\Http\Middleware\TeacherMiddleware::class,
|
||||
'student' => \App\Http\Middleware\StudentMiddleware::class
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the path the user should be redirected to when they are not authenticated.
|
||||
*/
|
||||
protected function redirectTo(Request $request): ?string
|
||||
{
|
||||
return $request->expectsJson() ? null : route('route-guest');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||
|
||||
class PreventRequestsDuringMaintenance extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next, string ...$guards): Response
|
||||
{
|
||||
$guards = empty($guards) ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class StudentMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
|
||||
|
||||
if ( Auth::user()->teacher !== "student" ) {
|
||||
|
||||
abort(403, "Unauthorized Action.");
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class TeacherMiddleware
|
||||
{
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
|
||||
if ( Auth::user()->teacher !== "teacher" ) {
|
||||
|
||||
abort(403, "Unauthorized action.");
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
20
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Http/Middleware/TrustHosts.php
Normal file
20
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Http/Middleware/TrustHosts.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the host patterns that should be trusted.
|
||||
*
|
||||
* @return array<int, string|null>
|
||||
*/
|
||||
public function hosts(): array
|
||||
{
|
||||
return [
|
||||
$this->allSubdomainsOfApplicationUrl(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
|
||||
|
||||
class ValidateSignature extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the query string parameters that should be ignored.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
// 'fbclid',
|
||||
// 'utm_campaign',
|
||||
// 'utm_content',
|
||||
// 'utm_medium',
|
||||
// 'utm_source',
|
||||
// 'utm_term',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
71
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/AddEnvFile.php
Normal file
71
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/AddEnvFile.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class AddEnvFile implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $envFile;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $envFile, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->envFile = $envFile;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Adding env file {$this->envFile} into {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Adding env file");
|
||||
try {
|
||||
// processing
|
||||
$process = new Process($this->command);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info("Added env file {$this->envFile} into {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Added env file");
|
||||
} else {
|
||||
Log::error("Failed to add env file {$this->envFile} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to add env file");
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
// failed
|
||||
Log::error("Failed to add env file {$this->envFile} " . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to add env file");
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$ADD_ENV_FILE;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class CloneRepository implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $repoUrl;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $repoUrl, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->repoUrl = $repoUrl;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Cloning repo {$this->repoUrl} into {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Cloning repo {$this->repoUrl}");
|
||||
try {
|
||||
// processing
|
||||
$process = new Process($this->command);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
// completed
|
||||
Log::info("Cloned repo {$this->repoUrl} into {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Cloned repo {$this->repoUrl}");
|
||||
} else {
|
||||
// failed
|
||||
Log::error("Failed to clone repo {$this->repoUrl} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to clone repo {$this->repoUrl}");
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
// failed
|
||||
Log::error("Failed to clone repo {$this->repoUrl} " . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to clone repo {$this->repoUrl}");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$CLONE_REPOSITORY;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class CopyTestsFolder implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $testsDir;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $testsDir, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->testsDir = $testsDir;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Copying tests folder to {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Copying tests folder");
|
||||
try {
|
||||
// processing
|
||||
if (is_dir($this->tempDir . '/tests')) {
|
||||
Log::info("Removing old tests folder from {$this->tempDir}");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}/tests")->run();
|
||||
}
|
||||
mkdir($this->tempDir . '/tests', 0777, true);
|
||||
mkdir($this->tempDir . '/tests/api', 0777, true);
|
||||
mkdir($this->tempDir . '/tests/web', 0777, true);
|
||||
mkdir($this->tempDir . '/tests/web/images', 0777, true);
|
||||
foreach ($this->command as $key => $value) {
|
||||
$process = new Process($value);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info("Copied tests {$value[2]} folder to {$value[3]}");
|
||||
} else {
|
||||
Log::error("Failed to copying tests {$value[2]} folder to {$value[3]}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to copying tests folder");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
throw new \Exception($process->getErrorOutput());
|
||||
}
|
||||
}
|
||||
// completed
|
||||
Log::info("Copied tests folder to {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Copied tests folder");
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Failed to copying tests folder to {$this->tempDir} " . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to copying tests folder");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$COPY_TESTS_FOLDER;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class DeleteTempDirectory implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Deleting folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Deleting folder");
|
||||
try {
|
||||
// processing
|
||||
foreach ($this->command as $key => $value) {
|
||||
$process = new Process($value, null, null, null, 120);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info('Command ' . implode(" ", $value) . ' is successful');
|
||||
} else {
|
||||
Log::error("Failed to delete folder {$this->tempDir} " . $process->getErrorOutput());
|
||||
// $this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to delete folder");
|
||||
}
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
}
|
||||
// completed
|
||||
Log::info("Deleted folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Deleted folder");
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Failed to delete folder {$this->tempDir} " . $th->getMessage());
|
||||
// $this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to delete folder");
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$DELETE_TEMP_DIRECTORY;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class ExamineFolderStructure implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Examining folder structure from {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Examining folder structure");
|
||||
try {
|
||||
// processing
|
||||
$process = new Process($this->command);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
// completed
|
||||
$projectStructure = $submission->project->defaultFileStructure;
|
||||
$defaultStructure = $projectStructure->structure;
|
||||
$excludedFolders = $projectStructure->excluded;
|
||||
$replacementFolders = $projectStructure->replacements;
|
||||
|
||||
$submissionStructure = $this->getDirectoryStructure($this->tempDir, $excludedFolders, $replacementFolders);
|
||||
|
||||
$diff = $this->compare_file_structures($defaultStructure, $submissionStructure);
|
||||
$missingFiles = [];
|
||||
foreach ($diff as $key => $value) {
|
||||
if (gettype($key) == 'integer') {
|
||||
if (!in_array($value, $excludedFolders)) array_push($missingFiles, $value);
|
||||
} else {
|
||||
if (!in_array($key, $excludedFolders)) array_push($missingFiles, [$key => $value]);
|
||||
}
|
||||
}
|
||||
|
||||
Log::info("Finished examining folder structure from {$this->tempDir}");
|
||||
if (empty($missingFiles)) {
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Finished examining folder structure from successfully");
|
||||
} else {
|
||||
Log::error("Failed to examine folder structure from {$this->tempDir} " . json_encode($missingFiles) . " are missing");
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Submitted project is missing the following files " . json_encode($missingFiles));
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
} else {
|
||||
Log::error("Failed to examine folder structure from {$this->tempDir} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to examine folder structure");
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Failed to examine folder structure from {$this->tempDir}" . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to examine folder structure");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function getDirectoryStructure($dirPath, $excludedFolders, $replacementFolders)
|
||||
{
|
||||
$structure = [];
|
||||
$files = glob($dirPath . '/*');
|
||||
foreach ($files as $file) {
|
||||
if (is_dir($file)) {
|
||||
$dirName = basename($file);
|
||||
if (!in_array($dirName, $excludedFolders)) {
|
||||
if (isset($replacementFolders[$dirName])) {
|
||||
$dirName = $replacementFolders[$dirName];
|
||||
}
|
||||
$structure[$dirName] = $this->getDirectoryStructure($file, $excludedFolders, $replacementFolders);
|
||||
}
|
||||
} else {
|
||||
$structure[basename($file)] = '';
|
||||
}
|
||||
}
|
||||
return $structure;
|
||||
}
|
||||
|
||||
private function compare_file_structures($defaultStructure, $submittedStructure)
|
||||
{
|
||||
$diff = [];
|
||||
foreach ($defaultStructure as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
if (!isset($submittedStructure[$key])) {
|
||||
$diff[$key] = $value;
|
||||
} else {
|
||||
$new_diff = $this->compare_file_structures($value, $submittedStructure[$key]);
|
||||
if (!empty($new_diff)) {
|
||||
$diff[$key] = $new_diff;
|
||||
}
|
||||
}
|
||||
} else if (!array_key_exists($key, $submittedStructure) || $submittedStructure[$key] !== $value) {
|
||||
$diff[] = $key;
|
||||
}
|
||||
}
|
||||
return $diff;
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$EXAMINE_FOLDER_STRUCTURE;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
71
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/NpmInstall.php
Normal file
71
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/NpmInstall.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class NpmInstall implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("NPM installing in folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "NPM installing");
|
||||
try {
|
||||
// processing
|
||||
// check if the module is already exist within the assets folder
|
||||
$process = new Process($this->command, $this->tempDir, null, null, 120);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info("NPM installed in folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "NPM installed");
|
||||
} else {
|
||||
Log::error("Failed to NPM install in folder {$this->tempDir} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to NPM install");
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
throw new \Exception($process->getErrorOutput());
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Failed to NPM install in folder {$this->tempDir}" . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to NPM install");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$NPM_INSTALL;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
126
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/NpmRunStart.php
Normal file
126
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/NpmRunStart.php
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Exception\ProcessTimedOutException;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class NpmRunStart
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
$tempDir = $this->tempDir;
|
||||
$command = $this->command;
|
||||
|
||||
|
||||
Log::info("NPM run start is processing in folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "NPM run start is processing");
|
||||
// Change port number in .env file
|
||||
$port = $this->getAvailablePort();
|
||||
if (!$port) {
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to find an available port for the project");
|
||||
return;
|
||||
}
|
||||
$submission->updatePort($port);
|
||||
// Change port number in .env file
|
||||
$envPath = "$tempDir/.env";
|
||||
if (file_exists($envPath)) {
|
||||
$envContent = file_get_contents($envPath);
|
||||
$envContent = preg_replace('/PORT=\d+/', "PORT=$port", $envContent);
|
||||
file_put_contents($envPath, $envContent);
|
||||
}
|
||||
|
||||
// Run NPM start command
|
||||
try {
|
||||
$process = new Process($command, $tempDir, null, null, null);
|
||||
$process->start();
|
||||
$process->waitUntil(function ($type, $output) use ($port) {
|
||||
return strpos($output, "Server started on port $port") !== false || strpos($output, "MongoNetworkError") !== false;
|
||||
}, 60000); // Wait for 60 seconds
|
||||
|
||||
if (strpos($process->getOutput(), "Server started on port $port") !== false) {
|
||||
Log::info("NPM run start is completed in folder {$tempDir} the application is running on port $port");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, $process->getOutput());
|
||||
$process->wait();
|
||||
} else {
|
||||
Log::error("Failed to NPM run start in folder {$tempDir} due to error " . $process->getOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to start application on port $port");
|
||||
Process::fromShellCommandline("npx kill-port $port")->run();
|
||||
}
|
||||
} catch (ProcessTimedOutException $th) {
|
||||
$process->stop();
|
||||
Log::error("Failed to NPM run start in folder {$tempDir} due to timeout " . $process->getOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to start application on port $port due to timeout");
|
||||
Process::fromShellCommandline("npx kill-port $port")->run();
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Failed to NPM run start in folder {$tempDir}" . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to start application on port $port");
|
||||
Process::fromShellCommandline("npx kill-port $port")->run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an available port number.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function getAvailablePort(): ?int
|
||||
{
|
||||
$minPort = 9000;
|
||||
$maxPort = 9999;
|
||||
for ($port = $minPort; $port <= $maxPort; $port++) {
|
||||
$fp = @fsockopen('localhost', $port, $errno, $errstr, 1);
|
||||
if (!$fp && Submission::where('port', $port)->doesntExist()) {
|
||||
return $port;
|
||||
} else {
|
||||
if (is_resource($fp)) {
|
||||
fclose($fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the submission status and result of a specific step.
|
||||
*
|
||||
* @param Submission $submission
|
||||
* @param string $status
|
||||
* @param string $output
|
||||
* @return void
|
||||
*/
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$NPM_RUN_START;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
93
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/NpmRunTests.php
Normal file
93
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/NpmRunTests.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class NpmRunTests implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("NPM running tests in folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "NPM running tests");
|
||||
try {
|
||||
// processing
|
||||
$pass_all = [];
|
||||
$commands = $this->command;
|
||||
foreach ($commands as $key => $command) {
|
||||
$command_string = implode(" ", $command);
|
||||
Log::info("Running {$command_string} in folder {$this->tempDir}");
|
||||
$this->updateSubmissionTestsResultsStatus($command_string, $submission, Submission::$PROCESSING, "Running");
|
||||
usleep(100000);
|
||||
$process = new Process($command, $this->tempDir, null, null, 120);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
$pass_all[$key] = true;
|
||||
Log::info("{$command_string} in folder {$this->tempDir}");
|
||||
$this->updateSubmissionTestsResultsStatus($command_string, $submission, Submission::$COMPLETED, "Completed");
|
||||
} else {
|
||||
$pass_all[$key] = false;
|
||||
Log::error("Failed to NPM run test {$command_string} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionTestsResultsStatus($command_string, $submission, Submission::$FAILED, $process->getErrorOutput());
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
}
|
||||
}
|
||||
if (in_array(false, $pass_all) == false) {
|
||||
Log::info("NPM ran tests in folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "NPM tested");
|
||||
} else {
|
||||
Log::info("NPM failed to run tests in folder {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to run NPM tests");
|
||||
if ($submission->port) Process::fromShellCommandline("npx kill-port $submission->port")->run();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Log::error("Failed to NPM run tests in folder {$this->tempDir} " . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to NPM running tests");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionTestsResultsStatus($testName, Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$NPM_RUN_TESTS;
|
||||
$submission->updateOneTestResult($stepName, $testName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$NPM_RUN_TESTS;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class ReplacePackageJson implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $packageJson;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $packageJson, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->packageJson = $packageJson;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Replacing package.json to {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Replacing package.json");
|
||||
try {
|
||||
// processing
|
||||
$process = new Process($this->command);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
// completed
|
||||
Log::info("Replaced package.json to {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Replaced package.json");
|
||||
} else {
|
||||
// failed
|
||||
Log::error("Failed to replace package.json to {$this->tempDir} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to replace package.json");
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
// failed
|
||||
Log::error("Failed to replace package.json to {$this->tempDir} " . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to replace package.json");
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$REPLACE_PACKAGE_JSON;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
73
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/UnzipZipFiles.php
Normal file
73
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Jobs/NodeJS/UnzipZipFiles.php
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\NodeJS;
|
||||
|
||||
use App\Models\NodeJS\ExecutionStep;
|
||||
use App\Models\NodeJS\Submission;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class UnzipZipFiles implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $submission;
|
||||
public $zipFileDir;
|
||||
public $tempDir;
|
||||
public $command;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct($submission, $zipFileDir, $tempDir, $command)
|
||||
{
|
||||
$this->submission = $submission;
|
||||
$this->zipFileDir = $zipFileDir;
|
||||
$this->tempDir = $tempDir;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$submission = $this->submission;
|
||||
Log::info("Unzipping {$this->zipFileDir} into {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$PROCESSING, "Unzipping submitted folder");
|
||||
try {
|
||||
if (!file_exists($this->tempDir)) mkdir($this->tempDir, 0777, true);
|
||||
// processing
|
||||
$process = new Process($this->command);
|
||||
$process->start();
|
||||
$process_pid = $process->getPid();
|
||||
$process->wait();
|
||||
if ($process->isSuccessful()) {
|
||||
Log::info("Unzipped {$this->zipFileDir} into {$this->tempDir}");
|
||||
$this->updateSubmissionStatus($submission, Submission::$COMPLETED, "Unzipped submitted folder");
|
||||
} else {
|
||||
Log::error("Failed to unzip {$this->zipFileDir} " . $process->getErrorOutput());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed to unzip submitted folder");
|
||||
Process::fromShellCommandline('kill ' . $process_pid)->run();
|
||||
Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
// failed
|
||||
Log::error("Failed to unzip {$this->zipFileDir} " . $th->getMessage());
|
||||
$this->updateSubmissionStatus($submission, Submission::$FAILED, "Failed tp unzip submitted folder");
|
||||
// Process::fromShellCommandline("rm -rf {$this->tempDir}")->run();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateSubmissionStatus(Submission $submission, string $status, string $output): void
|
||||
{
|
||||
$stepName = ExecutionStep::$UNZIP_ZIP_FILES;
|
||||
if ($status != Submission::$PROCESSING) $submission->updateOneResult($stepName, $status, $output);
|
||||
if ($status != Submission::$COMPLETED) $submission->updateStatus($status);
|
||||
}
|
||||
}
|
||||
19
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Enrollment.php
Normal file
19
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Enrollment.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Enrollment extends Model
|
||||
{
|
||||
protected $table = "android_enrollment";
|
||||
protected $fillable = ["android_topic_id", "user_id", "status"];
|
||||
|
||||
|
||||
public function user() : BelongsTo {
|
||||
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
34
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Submits.php
Normal file
34
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Submits.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
use App\Models\Android\Topic;
|
||||
use App\Models\Android\Task;
|
||||
use App\Models\User;
|
||||
use App\Models\Android\SubmitsTestCase;
|
||||
|
||||
class Submits extends Model
|
||||
{
|
||||
protected $table = "android_submits";
|
||||
protected $fillable = ["user_id", "android_topic_id", "android_task_id", "duration", "upload", "comment", "validator"];
|
||||
|
||||
|
||||
public function topic() : BelongsTo {
|
||||
|
||||
return $this->belongsTo(Topic::class, 'android_topic_id', 'id');
|
||||
}
|
||||
|
||||
public function task() : BelongsTo {
|
||||
|
||||
return $this->belongsTo(Task::class, 'android_task_id', 'id');
|
||||
}
|
||||
|
||||
public function user() : BelongsTo {
|
||||
|
||||
return $this->belongsTo(User::class, 'user_id', 'id');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SubmitsFinalSubmission extends Model
|
||||
{
|
||||
protected $table = "android_submits_submission";
|
||||
protected $fillable = ["user_id", "android_topic_id", "tipe", "userfile"];
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use App\Models\Android\Testcase;
|
||||
|
||||
class SubmitsTestCase extends Model
|
||||
{
|
||||
protected $table = "android_submits_testcase";
|
||||
protected $fillable = ["user_id", "android_submit_id", "android_testcase_id", "status", "status_waiting"];
|
||||
|
||||
public function android_testcase() : BelongsTo{
|
||||
|
||||
return $this->belongsTo( Testcase::class, 'android_testcase_id', 'id' );
|
||||
}
|
||||
}
|
||||
26
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Task.php
Normal file
26
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Task.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Task extends Model
|
||||
{
|
||||
protected $table = "android_task";
|
||||
protected $fillable = ['android_topic_id', 'task_no', 'task_name', 'caption', 'material', 'tipe'];
|
||||
|
||||
public $timestamp = true;
|
||||
|
||||
|
||||
|
||||
// create and update at menggunakan timestamp dari server (epoch)
|
||||
public function getCreatedAtAttribute( $value ) {
|
||||
|
||||
return Carbon::parse($value)->timestamp;
|
||||
}
|
||||
public function getUpdatedAtAttribute( $value ) {
|
||||
|
||||
return Carbon::parse($value)->timestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Task_waiting extends Model
|
||||
{
|
||||
protected $table = "android_task_waiting";
|
||||
protected $fillable = ["user_id", "android_topic_id", "android_task_id"];
|
||||
}
|
||||
13
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Testcase.php
Normal file
13
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Testcase.php
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Testcase extends Model
|
||||
{
|
||||
protected $table = "android_testcase";
|
||||
protected $fillable = ["task_id", "case", "score"];
|
||||
|
||||
public $timestamps = false;
|
||||
}
|
||||
26
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Topic.php
Normal file
26
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Android/Topic.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\Android;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Topic extends Model
|
||||
{
|
||||
protected $table = "android_topics";
|
||||
protected $fillable = ['title', 'description', 'folder_path', 'picturePath', 'status'];
|
||||
|
||||
public $timestamp = true;
|
||||
|
||||
|
||||
|
||||
// create and update at menggunakan timestamp dari server (epoch)
|
||||
public function getCreatedAtAttribute( $value ) {
|
||||
|
||||
return Carbon::parse($value)->timestamp;
|
||||
}
|
||||
public function getUpdatedAtAttribute( $value ) {
|
||||
|
||||
return Carbon::parse($value)->timestamp;
|
||||
}
|
||||
}
|
||||
26
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Material.php
Normal file
26
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Material.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Material extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'description',
|
||||
'folder_path',
|
||||
'status',
|
||||
];
|
||||
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
public function tasks()
|
||||
{
|
||||
return $this->hasMany(Task::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ExecutionStep extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'commands',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'commands' => 'array',
|
||||
];
|
||||
|
||||
static $CLONE_REPOSITORY = 'Clone Repository';
|
||||
static $UNZIP_ZIP_FILES = 'Unzip ZIP Files';
|
||||
static $REMOVE_ZIP_FILES = 'Remove ZIP Files';
|
||||
static $EXAMINE_FOLDER_STRUCTURE = 'Examine Folder Structure';
|
||||
static $ADD_ENV_FILE = 'Add .env File';
|
||||
static $REPLACE_PACKAGE_JSON = 'Replace package.json';
|
||||
static $COPY_TESTS_FOLDER = "Copy 'tests' Folder";
|
||||
static $NPM_INSTALL = 'NPM Install';
|
||||
static $NPM_RUN_START = 'NPM Run Start';
|
||||
static $NPM_RUN_TESTS = 'NPM Run Tests';
|
||||
static $DELETE_TEMP_DIRECTORY = 'Delete Temp Directory';
|
||||
|
||||
public function getCommandsAttribute($value)
|
||||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
||||
public function setCommandsAttribute($value)
|
||||
{
|
||||
$this->attributes['commands'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function projectExecutionSteps()
|
||||
{
|
||||
return $this->hasMany(ProjectExecutionStep::class);
|
||||
}
|
||||
}
|
||||
57
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/NodeJS/Project.php
Normal file
57
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/NodeJS/Project.php
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class Project extends Model implements HasMedia
|
||||
{
|
||||
use HasFactory, InteractsWithMedia;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'description',
|
||||
'tech_stack',
|
||||
'github_url',
|
||||
'image',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tech_stack' => 'array',
|
||||
];
|
||||
|
||||
public function defaultFileStructure(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(ProjectsDefaultFileStructure::class);
|
||||
}
|
||||
|
||||
public function submissions(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(Submission::class);
|
||||
}
|
||||
|
||||
public function projectExecutionSteps(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(ProjectExecutionStep::class);
|
||||
}
|
||||
|
||||
public function getTechStackAttribute($value): array
|
||||
{
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
public function setTechStackAttribute($value): void
|
||||
{
|
||||
$this->attributes['tech_stack'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function getImageAttribute(): string
|
||||
{
|
||||
return $this->getFirstMediaUrl('project_images');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ProjectExecutionStep extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
protected $fillable = [
|
||||
'project_id',
|
||||
'execution_step_id',
|
||||
'order',
|
||||
'variables',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'variables' => 'array',
|
||||
];
|
||||
|
||||
public function getVariablesAttribute($value)
|
||||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
||||
public function setVariablesAttribute($value)
|
||||
{
|
||||
$this->attributes['variables'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
|
||||
public function executionStep()
|
||||
{
|
||||
return $this->belongsTo(ExecutionStep::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ProjectsDefaultFileStructure extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
protected $fillable = [
|
||||
'project_id',
|
||||
'structure',
|
||||
'excluded',
|
||||
'replacements',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'structure' => 'array',
|
||||
'excluded' => 'array',
|
||||
'replacements' => 'array',
|
||||
];
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
|
||||
public function getStructureAttribute($value)
|
||||
{
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
public function setStructureAttribute($value)
|
||||
{
|
||||
$this->attributes['structure'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function getExcludedAttribute($value)
|
||||
{
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
public function setExcludedAttribute($value)
|
||||
{
|
||||
$this->attributes['excluded'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function getReplacementsAttribute($value)
|
||||
{
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
public function setReplacementsAttribute($value)
|
||||
{
|
||||
$this->attributes['replacements'] = json_encode($value);
|
||||
}
|
||||
}
|
||||
312
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/NodeJS/Submission.php
Normal file
312
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/NodeJS/Submission.php
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
||||
|
||||
class Submission extends Model implements HasMedia
|
||||
{
|
||||
use HasFactory, InteractsWithMedia;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
static $types = ['file', 'url'];
|
||||
static $statues = ['pending', 'processing', 'completed', 'failed'];
|
||||
static $FILE = 'file';
|
||||
static $URL = 'url';
|
||||
static $PENDING = 'pending';
|
||||
static $PROCESSING = 'processing';
|
||||
static $COMPLETED = 'completed';
|
||||
static $FAILED = 'failed';
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'project_id',
|
||||
'type',
|
||||
'path',
|
||||
'status',
|
||||
'results',
|
||||
'attempts',
|
||||
'port',
|
||||
'start',
|
||||
'end',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'results' => 'array',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
|
||||
public function getResultsAttribute($value)
|
||||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
||||
public function setResultsAttribute($value)
|
||||
{
|
||||
$this->attributes['results'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function getFileAttribute()
|
||||
{
|
||||
return $this->getFirstMediaUrl('public_submissions_files');
|
||||
}
|
||||
|
||||
public function isGithubUrl()
|
||||
{
|
||||
return $this->type == self::$URL;
|
||||
}
|
||||
|
||||
public function history()
|
||||
{
|
||||
return $this->hasMany(SubmissionHistory::class);
|
||||
}
|
||||
|
||||
public function getExecutionSteps()
|
||||
{
|
||||
if ($this->isGithubUrl()) {
|
||||
$steps = $this->project->projectExecutionSteps->filter(function ($step) {
|
||||
return $step->executionStep->name != 'Unzip ZIP Files' && $step->executionStep->name != 'Remove ZIP Files';
|
||||
});
|
||||
} else {
|
||||
$steps = $this->project->projectExecutionSteps->filter(function ($step) {
|
||||
return $step->executionStep->name != 'Clone Repository';
|
||||
});
|
||||
}
|
||||
// order the steps by their order
|
||||
$steps = $steps->sortBy('order');
|
||||
return $steps;
|
||||
}
|
||||
|
||||
public function initializeResults()
|
||||
{
|
||||
$results = [];
|
||||
$steps = $this->getExecutionSteps();
|
||||
foreach ($steps as $step) {
|
||||
if ($step->executionStep->name == ExecutionStep::$NPM_RUN_TESTS) {
|
||||
$tests = $this->project->projectExecutionSteps->where('execution_step_id', $step->executionStep->id)->first()->variables;
|
||||
$testResults = [];
|
||||
$order = 0;
|
||||
foreach ($tests as $testCommandValue) {
|
||||
$order = $order + 1;
|
||||
$command = implode(" ", $step->executionStep->commands);
|
||||
$key = explode("=", $testCommandValue)[0];
|
||||
$value = explode("=", $testCommandValue)[1];
|
||||
$testName = str_replace($key, $value, $command);
|
||||
$testResults[$testName] = [
|
||||
'status' => self::$PENDING,
|
||||
'output' => '',
|
||||
'order' => $order,
|
||||
];
|
||||
}
|
||||
$results[$step->executionStep->name] = [
|
||||
'stepID' => $step->id,
|
||||
'status' => self::$PENDING,
|
||||
'order' => $step->order,
|
||||
'output' => '',
|
||||
'testResults' => $testResults,
|
||||
];
|
||||
} else {
|
||||
$results[$step->executionStep->name] = [
|
||||
'stepID' => $step->id,
|
||||
'status' => self::$PENDING,
|
||||
'order' => $step->order,
|
||||
'output' => '',
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->updateResults($results);
|
||||
}
|
||||
|
||||
public function increaseAttempts()
|
||||
{
|
||||
$this->attempts = $this->attempts + 1;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function updateStatus($status)
|
||||
{
|
||||
$this->status = $status;
|
||||
if ($status == self::$FAILED || self::$COMPLETED) $this->end = now();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function restartTime()
|
||||
{
|
||||
$this->start = now();
|
||||
$this->end = null;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function updateOneResult($step_name, $status, $output)
|
||||
{
|
||||
$results = $this->results;
|
||||
$results->$step_name->status = $status;
|
||||
$results->$step_name->output = $output;
|
||||
$this->updateResults($results);
|
||||
}
|
||||
|
||||
public function updateOneTestResult($step_name, $test_name, $status, $output)
|
||||
{
|
||||
$results = $this->results;
|
||||
$results->$step_name->testResults->$test_name->status = $status;
|
||||
$results->$step_name->testResults->$test_name->output = $output;
|
||||
$this->updateResults($results);
|
||||
}
|
||||
|
||||
public function updateResults($results)
|
||||
{
|
||||
$this->results = $results;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function updatePort($port)
|
||||
{
|
||||
$this->port = $port;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
public function getCurrentExecutionStep($step_id = null)
|
||||
{
|
||||
$steps = $this->getExecutionSteps();
|
||||
if ($step_id) {
|
||||
$current_step = $steps->first(function ($step) use ($step_id) {
|
||||
return $step->id == $step_id;
|
||||
});
|
||||
return $current_step;
|
||||
} else {
|
||||
$results = $this->results;
|
||||
$current_step = null;
|
||||
if (!$results) {
|
||||
return $current_step;
|
||||
}
|
||||
foreach ($steps as $step) {
|
||||
if ($results->{$step->executionStep->name}?->status == self::$PROCESSING || $results->{$step->executionStep->name}?->status == self::$PENDING) {
|
||||
$current_step = $step;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$current_step) {
|
||||
$have_failed_steps = false;
|
||||
foreach ($steps as $step) {
|
||||
if ($results->{$step->executionStep->name}?->status == self::$FAILED) {
|
||||
$have_failed_steps = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($have_failed_steps) {
|
||||
$this->updateStatus(self::$FAILED);
|
||||
} else {
|
||||
$this->updateStatus(self::$COMPLETED);
|
||||
}
|
||||
}
|
||||
|
||||
return $current_step;
|
||||
}
|
||||
}
|
||||
|
||||
public function getNextExecutionStep($step_id = null)
|
||||
{
|
||||
if ($step_id == null) return null;
|
||||
$next_step = null;
|
||||
$steps = $this->getExecutionSteps();
|
||||
$current_step = $this->getCurrentExecutionStep($step_id);
|
||||
if ($current_step) {
|
||||
$current_step_index = $steps->search(function ($step) use ($current_step) {
|
||||
return $step->id == $current_step->id;
|
||||
});
|
||||
if ($current_step_index < $steps->count()) {
|
||||
$next_step = $steps[$current_step_index + 1];
|
||||
}
|
||||
}
|
||||
return $next_step;
|
||||
}
|
||||
|
||||
public function getTotalSteps()
|
||||
{
|
||||
return $this->getExecutionSteps()->count();
|
||||
}
|
||||
|
||||
public function getTotalCompletedSteps()
|
||||
{
|
||||
$results = $this->results;
|
||||
$completed_steps = 0;
|
||||
if ($results != null) {
|
||||
foreach ($results as $result) {
|
||||
if ($result->status == self::$COMPLETED || $result->status == self::$FAILED) {
|
||||
$completed_steps++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $completed_steps;
|
||||
}
|
||||
|
||||
public function restartAfterNpmInstall()
|
||||
{
|
||||
$step = ProjectExecutionStep::where('project_id', $this->project_id)->where('execution_step_id', ExecutionStep::where('name', ExecutionStep::$NPM_INSTALL)->first()->id)->first();
|
||||
$next_step = $this->getNextExecutionStep($step->id ?? null);
|
||||
// update results to pending
|
||||
while ($next_step) {
|
||||
if ($next_step->executionStep->name == ExecutionStep::$NPM_RUN_TESTS) {
|
||||
$tests = $this->project->projectExecutionSteps->where('execution_step_id', $next_step->executionStep->id)->first()->variables;
|
||||
foreach ($tests as $testCommandValue) {
|
||||
$command = implode(" ", $next_step->executionStep->commands);
|
||||
$key = explode("=", $testCommandValue)[0];
|
||||
$value = explode("=", $testCommandValue)[1];
|
||||
$testName = str_replace($key, $value, $command);
|
||||
$this->updateOneTestResult($next_step->executionStep->name, $testName, Submission::$PENDING, " ");
|
||||
}
|
||||
}
|
||||
$this->updateOneResult($next_step->executionStep->name, Submission::$PENDING, " ");
|
||||
$next_step = $this->getNextExecutionStep($next_step->id ?? null);
|
||||
}
|
||||
$this->updateStatus(Submission::$PROCESSING);
|
||||
}
|
||||
|
||||
public function getTotalAttemptsCount()
|
||||
{
|
||||
return $this->history->count() + 1;
|
||||
}
|
||||
|
||||
public function restart()
|
||||
{
|
||||
$this->updateStatus(Submission::$PENDING);
|
||||
$this->increaseAttempts();
|
||||
$this->updatePort(null);
|
||||
$this->restartTime();
|
||||
$this->initializeResults();
|
||||
}
|
||||
|
||||
public function createHistory($description)
|
||||
{
|
||||
// use the method attach to attach the history to the submission
|
||||
$history = $this->history()->create([
|
||||
'user_id' => $this->user_id,
|
||||
'project_id' => $this->project_id,
|
||||
'type' => $this->type,
|
||||
'path' => $this->path,
|
||||
'status' => $this->status,
|
||||
'results' => $this->results,
|
||||
'attempts' => $this->attempts,
|
||||
'port' => $this->port,
|
||||
'start' => $this->start,
|
||||
'end' => $this->end,
|
||||
'description' => $description,
|
||||
]);
|
||||
|
||||
return $history;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class SubmissionHistory extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
static $types = ['file', 'url'];
|
||||
static $statues = ['pending', 'processing', 'completed', 'failed'];
|
||||
static $FILE = 'file';
|
||||
static $URL = 'url';
|
||||
static $PENDING = 'pending';
|
||||
static $PROCESSING = 'processing';
|
||||
static $COMPLETED = 'completed';
|
||||
static $FAILED = 'failed';
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'project_id',
|
||||
'type',
|
||||
'path',
|
||||
'status',
|
||||
'results',
|
||||
'attempts',
|
||||
'port',
|
||||
'start',
|
||||
'end',
|
||||
'description'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'results' => 'array',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function project()
|
||||
{
|
||||
return $this->belongsTo(Project::class);
|
||||
}
|
||||
|
||||
public function getResultsAttribute($value)
|
||||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
||||
public function setResultsAttribute($value)
|
||||
{
|
||||
$this->attributes['results'] = json_encode($value);
|
||||
}
|
||||
|
||||
public function getFileAttribute()
|
||||
{
|
||||
return $this->getFirstMediaUrl('public_submissions_files');
|
||||
}
|
||||
|
||||
public function isGithubUrl()
|
||||
{
|
||||
return $this->type == self::$URL;
|
||||
}
|
||||
|
||||
public function submission()
|
||||
{
|
||||
return $this->belongsTo(SubmissionStatus::class);
|
||||
}
|
||||
|
||||
public function getExecutionSteps()
|
||||
{
|
||||
if ($this->isGithubUrl()) {
|
||||
$steps = $this->project->projectExecutionSteps->filter(function ($step) {
|
||||
return $step->executionStep->name != 'Unzip ZIP Files' && $step->executionStep->name != 'Remove ZIP Files';
|
||||
});
|
||||
} else {
|
||||
$steps = $this->project->projectExecutionSteps->filter(function ($step) {
|
||||
return $step->executionStep->name != 'Clone Repository';
|
||||
});
|
||||
}
|
||||
// order the steps by their order
|
||||
$steps = $steps->sortBy('order');
|
||||
return $steps;
|
||||
}
|
||||
|
||||
public function getTotalSteps()
|
||||
{
|
||||
return $this->getExecutionSteps()->count();
|
||||
}
|
||||
|
||||
public function getTotalCompletedSteps()
|
||||
{
|
||||
$results = $this->results;
|
||||
$completed_steps = 0;
|
||||
if ($results != null) {
|
||||
foreach ($results as $result) {
|
||||
if ($result->status == self::$COMPLETED) {
|
||||
$completed_steps++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $completed_steps;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\NodeJS;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TemporaryFile extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $connection = 'nodejsDB';
|
||||
|
||||
protected $fillable = [
|
||||
'folder_path',
|
||||
'file_name',
|
||||
];
|
||||
}
|
||||
11
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/Topic.php
Normal file
11
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/Topic.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\PHP;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Topic extends Model
|
||||
{
|
||||
protected $table = 'php_topics'; // Sesuaikan dengan nama tabel di database
|
||||
}
|
||||
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/Topic_detail.php
Normal file
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/Topic_detail.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\PHP;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Topic_detail extends Model
|
||||
{
|
||||
protected $table = 'php_topics_detail'; // Sesuaikan dengan nama tabel di database
|
||||
protected $fillable = ['title', 'id_topics' ,'controller', 'description', 'folder_path','file_name'];
|
||||
}
|
||||
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/UserCheck.php
Normal file
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/UserCheck.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\PHP;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserCheck extends Model
|
||||
{
|
||||
protected $table = 'php_user_submits'; // Sesuaikan dengan nama tabel di database
|
||||
protected $fillable = ['userid'];
|
||||
}
|
||||
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/UserLogin.php
Normal file
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/PHP/UserLogin.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models\PHP;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserLogin extends Model
|
||||
{
|
||||
protected $table = 'users'; // Sesuaikan dengan nama tabel di database
|
||||
protected $fillable = ['name','email'];
|
||||
}
|
||||
23
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/StudentSubmission.php
Normal file
23
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/StudentSubmission.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class StudentSubmission extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = ['user_id', 'task_id', 'file_path', 'submission_count', 'test_result'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function task()
|
||||
{
|
||||
return $this->belongsTo(Task::class);
|
||||
}
|
||||
}
|
||||
27
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Task.php
Normal file
27
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/Task.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Task extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'material_id',
|
||||
'task_name',
|
||||
'pdf_path',
|
||||
];
|
||||
|
||||
public function material()
|
||||
{
|
||||
return $this->belongsTo(Material::class);
|
||||
}
|
||||
// Tambahkan relasi ini
|
||||
public function studentSubmissions()
|
||||
{
|
||||
return $this->hasMany(StudentSubmission::class);
|
||||
}
|
||||
}
|
||||
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/TaskSubmission.php
Normal file
12
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/TaskSubmission.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TaskSubmission extends Model
|
||||
{
|
||||
protected $table = 'php_submits_submission'; // Sesuaikan dengan nama tabel di database
|
||||
protected $fillable = ['username','userfile','ket'];
|
||||
}
|
||||
48
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/User.php
Normal file
48
iCLOP-V2-NEW/iCLOP-V2-NEW/app/Models/User.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'google_id',
|
||||
'email',
|
||||
'role',
|
||||
'teacher',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
// use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The model to policy mappings for the application.
|
||||
*
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
require base_path('routes/channels.php');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event to listener mappings for the application.
|
||||
*
|
||||
* @var array<class-string, array<int, class-string>>
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if events and listeners should be automatically discovered.
|
||||
*/
|
||||
public function shouldDiscoverEvents(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to your application's "home" route.
|
||||
*
|
||||
* Typically, users are redirected here after authentication.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/home';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, and other route configuration.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('api')
|
||||
->prefix('api')
|
||||
->group(base_path('routes/api.php'));
|
||||
|
||||
Route::middleware('web')
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/AppLayout.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/AppLayout.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class AppLayout extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.layouts.app');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ApplicationLogo extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.application-logo');
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/Dropdown.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/Dropdown.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class Dropdown extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.dropdown');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class DropdownLink extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.dropdown-link');
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/FailedIcon.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/FailedIcon.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class FailedIcon extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.failed-icon');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components\Forms;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class tinymceEditor extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.forms.tinymce-editor');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class GuestLayout extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.layouts.guest');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components\Head;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class tinymceConfig extends Component
|
||||
{
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*/
|
||||
public function render(): View|Closure|string
|
||||
{
|
||||
return view('components.head.tinymce-config');
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/InputError.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/InputError.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class InputError extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.input-error');
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/InputLabel.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/InputLabel.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class InputLabel extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.input-label');
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/NavLink.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/NavLink.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class NavLink extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.nav-link');
|
||||
}
|
||||
}
|
||||
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/NotFound.php
Normal file
17
iCLOP-V2-NEW/iCLOP-V2-NEW/app/View/Components/NotFound.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class NotFound extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.not-found');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PendingIcon extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.pending-icon');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PrimaryButton extends Component
|
||||
{
|
||||
/**
|
||||
* Get the view / contents that represents the component.
|
||||
*/
|
||||
public function render(): View
|
||||
{
|
||||
return view('nodejs.components.primary-button');
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user