Update Terakhir Iclop Codeception

This commit is contained in:
Amal Udjir 2025-04-22 10:10:07 +07:00
commit de7bd2798b
3615 changed files with 1248622 additions and 0 deletions

18
.editorconfig Normal file
View 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
.env.example Normal file
View 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
.gitattributes vendored Normal file
View 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
.gitignore vendored Normal file
View 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
README.md Normal file
View 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).

27
app/Console/Kernel.php Normal file
View 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');
}
}

View 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) {
//
});
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\Android23;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AssignController extends Controller
{
//
}

View File

@ -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');
}
}
}

View File

@ -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'));
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,96 @@
<?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()->teacher == "admin" ) {
return redirect('welcome');
} else if ( Auth::user()->teacher == "teacher" ) {
return redirect('php/teacher/topics');
} else {
// student
echo Auth::user()->teacher;
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'
]);
$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 logout_teacher(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',
];
$newUser = User::firstOrCreate(['email' => $data['email']],$data);
$newUser = User::firstOrCreate(['role' => $data['role']],$data);
auth('web')->login($newUser);
session()->regenerate();
return redirect()->route('dashboard-student');
}
auth('web')->login($userDatabase);
session()->regenerate();
return redirect()->route('dashboard-student');
}
}

View File

@ -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;
}

View File

@ -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'));
}
}

View File

@ -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'));
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\NodeJS\Student;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ExecutionStepController extends Controller
{
//
}

View File

@ -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('/');
}
}

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\NodeJS\Student;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ProjectExecutionStepController extends Controller
{
//
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\NodeJS\Student;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ProjectsDefaultFileStructureController extends Controller
{
//
}

View File

@ -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);
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Http\Controllers\NodeJS\Student;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class SubmissionHistoryController extends Controller
{
//
}

View File

@ -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');
}
}

View File

@ -0,0 +1,858 @@
<?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::where('type', '1')->get();
$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'] : '';
$type = isset($_GET['type']) ? $_GET['type'] : '';
$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::where('type', $type)->get();
$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'] : '';
$type = isset($_GET['type']) ? $_GET['type'] : '';
$task_db = DB::table('php_task')
->where('id_topics', $phpid)
->where('task_no', $task)
->first();
if($task_db->task_no == $task){
if($type == 1){
$html_start = $this->html_task();
}else if($type == 2){
$html_start = $this->html_task_codeception();
}
}else{
$html_start = "";
}
$pdf_reader = 0;
$topics = Topic::where('type', $type)->get();;
$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_codeception(){
return view('php.student.task.form_submission_task_codeception',[]);
}
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){
$phpid = $_POST['phpid'];
$type = $_POST['type'];
$task_id = $_POST['testing_number_one'];
$file = $request->file('file');
//$file_name = Auth::user()->name.'_'.$file->getClientOriginalName();
if($phpid == 12 and $task_id == 1){
$file_name = 'form_insert.php';
}elseif($phpid == 13 and $task_id == 1){
$file_name = 'koneksi.php';
}elseif($phpid == 13 and $task_id == 2){
$file_name = 'index.php';
}elseif($phpid == 13 and $task_id == 3){
$file_name = 'insert.php';
}elseif($phpid == 13 and $task_id == 4){
$file_name = 'update.php';
}elseif($phpid == 13 and $task_id == 5){
$file_name = 'delete.php';
}else{
$file_name = $file->getClientOriginalName();
}
Storage::disk('local')->makeDirectory('public/uploads/'.Auth::user()->name);
$file->storeAs('/uploads/'.Auth::user()->name, $file_name, 'public');
$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/public/uploads/$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')");
$fileContent = file_get_contents($path);
if($_POST['testing_number_one'] == 1){
$testing_number_one = "testing_number_one";
}elseif($_POST['testing_number_one'] == 2){
$testing_number_one = "testing_number_two";
}elseif($_POST['testing_number_one'] == 3){
$testing_number_one = "testing_number_three";
}elseif($_POST['testing_number_one'] == 4){
$testing_number_one = "testing_number_four";
}elseif($_POST['testing_number_one'] == 5){
$testing_number_one = "testing_number_five";
}
$fileContent = str_replace("'", '"', $fileContent);
DB::select("insert into php_submits_submission(task_id,task_topics,task_no,testing_type,username,userfile,ket) values ('$task_id','$phpid','$type','$testing_number_one','$val','$file_name','$fileContent')");
return redirect("/php/send-task?phpid=$phpid&task=$task_id&type=$type")->with('status', 'File Successfully Uploaded!');
}
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')");
$fileContent = file_get_contents($path);
$task_id = $_POST['testing_number_one'];
$phpid = $_POST['phpid'];
if($_POST['testing_number_one'] == 1){
$testing_number_one = "testing_number_one";
}elseif($_POST['testing_number_one'] == 2){
$testing_number_one = "testing_number_two";
}elseif($_POST['testing_number_one'] == 3){
$testing_number_one = "testing_number_three";
}elseif($_POST['testing_number_one'] == 4){
$testing_number_one = "testing_number_four";
}elseif($_POST['testing_number_one'] == 5){
$testing_number_one = "testing_number_five";
}
DB::select("insert into php_submits_submission(task_id,testing_type,username,userfile,ket) values ('$task_id','$testing_number_one','$val','$file_name','$fileContent')");
return redirect("/php/send-task?phpid=$phpid&task=$task_id")->with('status', 'File Successfully Uploaded!');
}
function unittesting(){
$val = session('key');
DB::select("TRUNCATE TABLE php_user_submits");
DB::insert("insert into php_user_submits(userid) values ('$val')");
$kode = $_POST['kode'];
if($kode == '1'){
$unitesting_run = 'testLatihanNumberOne';
$testing_number_one = "testing_number_one";
}elseif($kode == '2'){
$unitesting_run = 'testLatihanNumberTwo';
$testing_number_one = "testing_number_two";
}elseif($kode == '3'){
$unitesting_run = 'testLatihanNumberThree';
$testing_number_one = "testing_number_three";
}elseif($kode == '4'){
$unitesting_run = 'testLatihanNumberFour';
$testing_number_one = "testing_number_four";
}elseif($kode == '5'){
$unitesting_run = 'testLatihanNumberFive';
$testing_number_one = "testing_number_five";
}
$command = "cd " . base_path() . " && php codecept.phar run Functional LoginTest --filter testLatihanNumberOne";
//$command = "cd " . base_path() . " && php codecept.phar run Unit PHPBasicTest --filter testLatihanNumberOne";
$output = shell_exec($command);
//echo "<pre>$command</pre>";
$sql = DB::select("SELECT * FROM php_testing_rule WHERE testing_name = '$testing_number_one'");
$row = $sql[0];
$html = $row->testing_rule;
$test = str_replace(array("\r\n","\r","\n"," "),"",$html);
$result_test = htmlspecialchars($test);
$string = json_encode($output);
$text = str_replace("\n", ' ', $output);
$cleaned = preg_replace('/\e\[[;?\d]*m/', '', $string);
// Pola regex untuk mengekstrak versi Codeception
$pattern = '/Codeception PHP Testing Framework v([\d.]+)/';
// Define patterns to extract relevant information
$pattern_phpunit_version = '/Codeception\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+)/';
$pattern_path = 'C:\\xampp\\htdocs\\iclop\\phpunit.xml';
$pattern_time = '/Time:\s+([0-9:.]+)/';
$pattern_memory = '/Memory:\s+([\d.]+)\s+(MB|KB|GB)/';
$pattern_ok = '/OK\s+\((\d+)\s+test(?:s)?,\s+(\d+)\s+assertion(?:s)?\)/';
$pattern_failures = '/Tests:\s+(\d+),\s+Assertions:\s+(\d+),\s+Failures:\s+(\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);
preg_match($pattern_ok, $text, $matches_ok);
preg_match($pattern_time, $text, $matches_time);
preg_match($pattern_memory, $text, $matches_memory);
preg_match($pattern_failures, $text, $matches_failures);
preg_match($pattern, $cleaned, $matches);
// Extracted information
$phpunit_version = isset($matches[1]) ? $matches[1] : "Not founds";
$phpunit_stat = isset($matches_ok[0]) ? $matches_ok[0] : "Not found";
$phpunit_time = isset($matches_time[1]) ? $matches_time[1] : "Not found";
$phpunit_memory = isset($matches_memory[0]) ? $matches_memory[0] : "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";
if (!empty($matches_failures)) {
$tests_assertions_failures = "FAILURES!<br /> Tests: " . $matches_failures[1] . ", Assertions: " . $matches_failures[2] . ", Failures: " . $matches_failures[3];
}
if($num_failures == 1){
$username = Session::get('user_name');
$sql = DB::select("SELECT
*
FROM
php_submits_submission a
LEFT JOIN php_testing_rule b ON a.testing_type = b.testing_name
WHERE
a.username COLLATE utf8mb4_general_ci = '$username'
ORDER BY
a.id DESC
LIMIT 1;
");
$row = $sql[0];
$html1 = htmlspecialchars($row->testing_rule);
$html2 = htmlspecialchars($row->ket);
$diff = "";
$diff_err = "";
// Memecah string HTML menjadi array per baris
$html1_lines = explode("\n", $html1);
$html2_lines = explode("\n", $html2);
$result_content = str_replace(array("\r\n","\r"," "),"", $html1_lines);
$result_content2 = str_replace(array("\r\n","\r"," "),"", $html2_lines);
// Membandingkan setiap baris
for ($i = 0; $i < count($result_content) && $i < count($result_content2); $i++) {
if ($result_content[$i] != $result_content2[$i]) {
$diff .= $html2_lines[$i] . "\n";
$diff .= "\n <br />"; // Menambahkan baris kosong setelah setiap perbedaan
}
}
echo "<br />";
echo "
<table class='table'>
<tr>
<td width='200px'>Codeception version</td>
<td width='10px'>:</td>
<td>$phpunit_version</td>
</tr>
<tr>
<td>Execution time</td>
<td>:</td>
<td>$phpunit_time</td>
</tr>
<tr>
<td>Number of failures</td>
<td>:</td>
<td>$num_failures</td>
</tr>
<tr>
<td>Failure location</td>
<td>:</td>
<td>".$diff."</td>
</tr>
<tr>
<td>Tests Status</td>
<td>:</td>
<td><b>$tests_assertions_failures</b></td>
</tr>
</table>
";
}else{
echo "
<table class='table'>
<tr>
<td width='200px'>Codeception version</td>
<td width='10px'>:</td>
<td>$phpunit_version</td>
</tr>
<tr>
<td>Execution time</td>
<td>:</td>
<td>$phpunit_time</td>
</tr>
<tr>
<td>Memory usage</td>
<td>:</td>
<td>$phpunit_memory</td>
</tr>
<tr>
<td>Tests Run</td>
<td>:</td>
<td><b>$phpunit_stat</b></td>
</tr>
</table>
";
}
}
function open_terminal(){
$val = session('key');
DB::select("TRUNCATE TABLE php_user_submits");
DB::insert("insert into php_user_submits(userid) values ('$val')");
$kode = 9;
if($kode == '1'){
$unitesting_run = 'testLatihanNumberOne';
}elseif($kode == '2'){
$unitesting_run = 'testLatihanNumberTwo';
}elseif($kode == '3'){
$unitesting_run = 'testLatihanNumberThree';
}elseif($kode == '4'){
$unitesting_run = 'testLatihanNumberFour';
}elseif($kode == '5'){
$unitesting_run = 'testLatihanNumberFive';
}elseif($kode == '6'){
$unitesting_run = 'testPHPStructure';
}elseif($kode == '7'){
$unitesting_run = 'testConditions';
}elseif($kode == '8'){
$unitesting_run = 'testLoops';
}elseif($kode == '9'){
$unitesting_run = 'testArray';
}
$path_test = base_path("phpunit.xml");
$pa = "vendor\bin\phpunit -c $path_test --filter $unitesting_run";
$path = base_path($pa);
$output = shell_exec($path);
echo dd($output);
//echo json_encode($output);
}
function session_progress(){
session(['params' => $_POST['params']]);
}
function flutter(){
// Path to the Flutter testing directory
$flutterTestingDir = 'C:\\xampp\\htdocs\\iclop\\public\\flutter_testing';
// Change directory to the Flutter testing directory
chdir($flutterTestingDir);
// Command to execute Flutter test
$command = 'flutter test';
// Execute the command and capture output
$output = shell_exec($command);
// Print the output
echo "<pre>$output</pre>";
}
function codeception_start(){
$actual = "";
$topics = Topic::where('type', '2')->get();
$topicsCount = count($topics);
return view('php.student.material.index',[
'result_up' => $actual,
'topics' => $topics,
'topicsCount' => $topicsCount,
]);
}
function unittesting_codeception(){
$val = session('key');
$kode = $_POST['kode'];
$type = $_POST['type'];
$phpid = $_POST['phpid'];
DB::select("TRUNCATE TABLE php_user_submits");
DB::insert("insert into php_user_submits(userid,phpid,task,type) values ('$val','$phpid','$kode','$type')");
$testing = DB::SELECT("SELECT material FROM php_task WHERE id_topics = '$phpid' and task_no = '$kode'");
$rt = $testing[0];
$testing_number_one = $rt->material;
$command = "cd " . base_path() . " && php codecept.phar run Functional FormMethodTest --filter $testing_number_one -v";
$output = shell_exec($command);
$string = json_encode($output);
// Hapus karakter ANSI escape
$clean_output = preg_replace('/\\x1b\[[0-9;]*m/', '', $string);
$clean_output = str_replace('\n', PHP_EOL, $clean_output);
if (preg_match('/(Pemeriksaan File pengelolaan form dengan Method POST Berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1]. PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File pengelolaan form dengan Method GET terdapat kesalahan.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(Pemeriksaan File pengelolaan form dengan Method POST terdapat kesalahan.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(Pemeriksaan File pengelolaan form dengan Method GET berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(File operasi CREATE Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(Pemeriksaan File Koneksi PHP berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File Koneksi PHP Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(Pemeriksaan File Koneksi Periksa variable PHP Anda, Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(Pemeriksaan File Insert PHP berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} elseif (preg_match('/(File operasi UPDATE Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(File operasi DELETE Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(File operasi READ Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File Select PHP berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File Delete PHP berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File Update PHP berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File Session Terdapat Kesalahan*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Pemeriksaan File Session PHP berhasil.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Input type text untuk nama tidak ditemukan.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Input type email tidak ditemukan.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Select untuk prodi tidak ditemukan.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
}elseif (preg_match('/(Tombol submit tidak ditemukan.*)/', $clean_output, $matches)) {
$hal = $matches[1] . PHP_EOL;
} else {
$hal = "Testing Tidak ditemukan." . PHP_EOL;
}
$string = json_encode($output);
$text = str_replace("\n", ' ', $output);
$cleaned = preg_replace('/\\e\[[0-9;]*m/', '', $output);
$firstLine = strtok($cleaned, "\n");
$firstLine = explode(" https://", $firstLine)[0];
preg_match('/Time:\s([\d:.]+),\sMemory:\s([\d.]+ MB)/', $cleaned, $matches);
$time = $matches[1];
$memory = $matches[2];
preg_match('/Time:\s([\d:.]+),\sMemory:\s([\d.]+ MB)/', $cleaned, $matches);
$time = $matches[1] ?? 'Not Found';
$memory = $matches[2] ?? 'Not Found';
preg_match('/(Pemeriksaan File Koneksi PHP Terdapat Kesalahan)/', $cleaned, $errorMatches);
$errorMessage = $errorMatches[1] ?? 'No Error Message';
preg_match('/(FAILURES!)/', $cleaned, $failureMatches);
$failureMessage = $failureMatches[1] ?? 'No Failures';
preg_match('/Tests:\s\d+,\sAssertions:\s\d+,\sFailures:\s\d+/', $cleaned, $summaryMatches);
$testSummary = $summaryMatches[0] ?? 'No Test Summary';
preg_match('/OK\s\(\d+\stest,\s\d+\sassertions?\)/', $cleaned, $okMatches);
$okResult = $okMatches[0] ?? 'No OK Result';
if($testSummary == 'No Test Summary'){
$hasil = $okResult;
}else{
$hasil = $testSummary;
}
echo "
<table class='table'>
<tr>
<td width='200px'>Codeception version</td>
<td width='10px'>:</td>
<td>$firstLine</td>
</tr>
<tr>
<td>Execution time</td>
<td>:</td>
<td>$time</td>
</tr>
<tr>
<td>Memory usage</td>
<td>:</td>
<td>$memory</td>
</tr>
<tr>
<td>Error Message</td>
<td>:</td>
<td>$hal</td>
</tr>
<tr>
<td>Failure Status</td>
<td>:</td>
<td>$failureMessage</td>
</tr>
<tr>
<td>Tests Run</td>
<td>:</td>
<td><b>$hasil</b></td>
</tr>
</table>
";
}
function work_results() {
$username = session('key');
$results = DB::select("SELECT * FROM php_submits_submission WHERE username = ? AND task_topics IN (12,13,14) GROUP BY task_topics", [$username]);
if (!empty($results)) {
$output = "";
$filePath = storage_path("app/public/uploads/$username/index.php");
if (file_exists($filePath)) {
try {
ob_start();
include $filePath;
$content = ob_get_clean();
$output = "<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: auto;
background-color: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 10px;
}
.content {
font-size: 16px;
color: #333;
}
</style>
<div class='container'>
<h2>Work Results, Generated by iCLOP</h2>
<div class='content'>$content</div>
</div>";
} catch (\Throwable $e) {
$output .= "
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: auto;
background-color: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 10px;
}
.content {
font-size: 16px;
color: #333;
}
</style>
<div class='container'><p>Error: " . $e->getMessage() . "</p></div>";
}
} else {
$output .= "<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 20px;
}
.container {
max-width: 800px;
margin: auto;
background-color: #fff;
padding: 20px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
border-radius: 10px;
}
.content {
font-size: 16px;
color: #333;
}
</style>
<div class='container'><p>File tidak ditemukan: " . $filePath . "</p></div>";
}
if (!empty(session('output'))) {
echo session('output');
}
return response($output);
}
return response("<div class='container'><p>Tidak ada file untuk diproses</p></div>", 404);
}
function form_insert_by_student() {
$username = session('key');
$results = DB::select("SELECT * FROM php_submits_submission WHERE username = ? AND task_topics IN (12,13,14) GROUP BY task_topics", [$username]);
if (!empty($results)) {
$output = ""; // Simpan hasil eksekusi semua file di sini
$filePath = storage_path("app/public/uploads/$username/form_insert.php");
if (file_exists($filePath)) {
try {
ob_start();
include $filePath; // Eksekusi file
$output .= ob_get_clean(); // Simpan hasilnya
} catch (\Throwable $e) {
$output .= "Error: " . $e->getMessage();
}
} else {
$output .= "File tidak ditemukan: " . $filePath . "<br>";
}
return response($output);
}
return response("Tidak ada file untuk diproses", 404);
}
function save_insert() {
$username = session('key');
$results = DB::select("SELECT * FROM php_submits_submission WHERE username = ? AND task_topics IN (12,13,14) GROUP BY task_topics", [$username]);
if (!empty($results)) {
$output = ""; // Simpan hasil eksekusi semua file di sini
$filePath = storage_path("app/public/uploads/$username/insert.php");
if (file_exists($filePath)) {
try {
ob_start();
include $filePath; // Eksekusi file
$output .= ob_get_clean(); // Simpan hasilnya
} catch (\Throwable $e) {
$output .= "Error: " . $e->getMessage();
}
} else {
$output .= "File tidak ditemukan: " . $filePath . "<br>";
}
$result = DB::connection('mysql2')
->select("SELECT * FROM users order by id desc limit 1");
$id = $result[0]->id;
$user_name = session('key');
DB::connection('mysql2')
->select("UPDATE users SET username = '$user_name' WHERE id = $id");
return redirect('/php/work-results')->with('output', $output);
}
return response("Tidak ada file untuk diproses", 404);
}
function delete_student() {
$username = session('key');
$results = DB::select("SELECT * FROM php_submits_submission WHERE username = ? AND task_topics IN (12,13,14) GROUP BY task_topics", [$username]);
if (!empty($results)) {
$output = ""; // Simpan hasil eksekusi semua file di sini
$filePath = storage_path("app/public/uploads/$username/delete.php");
if (file_exists($filePath)) {
try {
ob_start();
include $filePath; // Eksekusi file
$output .= ob_get_clean(); // Simpan hasilnya
} catch (\Throwable $e) {
$output .= "Error: " . $e->getMessage();
}
} else {
$output .= "File tidak ditemukan: " . $filePath . "<br>";
}
return redirect('/php/work-results')->with('output', $output);
}
return response("Tidak ada file untuk diproses", 404);
}
function index_student(){
return redirect('/php/work-results');
}
function edit_student() {
$username = session('key');
$results = DB::select("SELECT * FROM php_submits_submission WHERE username = ? AND task_topics IN (12,13,14) GROUP BY task_topics", [$username]);
if (!empty($results)) {
$output = ""; // Simpan hasil eksekusi semua file di sini
$filePath = storage_path("app/public/uploads/$username/update.php");
if (file_exists($filePath)) {
try {
ob_start();
include $filePath; // Eksekusi file
$output .= ob_get_clean(); // Simpan hasilnya
} catch (\Throwable $e) {
$output .= "Error: " . $e->getMessage();
}
} else {
$output .= "File tidak ditemukan: " . $filePath . "<br>";
}
return response($output);
}
return response("Tidak ada file untuk diproses", 404);
}
}

View File

@ -0,0 +1,79 @@
<?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 delete_topics( Request $request, $id ){
$results = DB::select('DELETE FROM php_topics_detail WHERE id = ?', [$id]);
return redirect()->back()->with('message', 'Data Berhasil Dihapus.');
}
function get_file(){
$kode = $_POST['kode'];
return view('php.teacher.topics_file_detail',[
'results' => $kode
]);
}
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');
}
}

View File

@ -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();
}
}

View File

@ -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');
}
}

View File

@ -0,0 +1,78 @@
<?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_add(){
$a = 2;
$b = 3;
echo $result = $a * $b;
}
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);
$sql = DB::select("SELECT
*
FROM
php_submits_submission a
LEFT JOIN php_testing_rule b ON a.testing_type = b.testing_name
WHERE
a.username COLLATE utf8mb4_general_ci = '$username'
ORDER BY
a.id DESC
LIMIT 1;
");
$row = $sql[0];
$html2 = htmlspecialchars($row->ket);
$test = str_replace(array("\r\n","\r","\n"," "),"",$html2);
$result_content2 = str_replace(array("\r\n","\r"," "),"", $test);
/* $actual = storage_path("app/private/{$username}/{$userfile}");
$php_output = shell_exec("PHP $actual 2>&1");
$test = str_replace(array("\r\n","\r","\n"," "),"",highlight_string($php_output));
*/
return view('php.student.task.result_submssion_task',[
'result_up' => $html2,
]);
// echo "$value == $sess_name";
}
function get_session(){
if (Auth::check()) {
$value = Auth::user()->name;
return $value;
}
}
}

View File

@ -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());
}
}

View File

@ -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)],
];
}
}

70
app/Http/Kernel.php Normal file
View 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
];
}

View File

@ -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('generated::VZkQ7NuBzZw9YG9l');
}
}

View File

@ -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 = [
//
];
}

View File

@ -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 = [
//
];
}

View File

@ -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);
}
}

View File

@ -0,0 +1,27 @@
<?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);
}
}

View File

@ -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);
}
}

View File

@ -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',
];
}

View 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(),
];
}
}

View File

@ -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;
}

View File

@ -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',
];
}

View File

@ -0,0 +1,22 @@
<?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 = [
'php/save_insert.php',
'php/edit.php',
'php/index.php',
'php/delete.php',
'php/work-results',
];
}

View 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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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 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);
}
}

View File

@ -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);
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View File

@ -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);
}
}

View 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);
}
}

View 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);
}
}

View 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');
}
}

View File

@ -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"];
}

View File

@ -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' );
}
}

View 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;
}
}

View File

@ -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"];
}

View 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;
}

View 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;
}
}

View File

@ -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);
}
}

View 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');
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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;
}
}

View File

@ -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;
}
}

View File

@ -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',
];
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Models\PHP;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Php_testing_rule extends Model
{
protected $table = 'php_testing_rule'; // Sesuaikan dengan nama tabel di database
}

11
app/Models/PHP/Topic.php Normal file
View 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';
}

View 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'];
}

View 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'];
}

View 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'];
}

View 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
app/Models/User.php Normal file
View 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',
];
}

View File

@ -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
{
//
}
}

View File

@ -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
{
//
}
}

View File

@ -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');
}
}

View File

@ -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;
}
}

View File

@ -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'));
});
}
}

View 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');
}
}

View File

@ -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');
}
}

View 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');
}
}

View File

@ -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');
}
}

View 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');
}
}

View File

@ -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');
}
}

View File

@ -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');
}
}

View File

@ -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');
}
}

View 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');
}
}

View 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');
}
}

View 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');
}
}

View 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');
}
}

View File

@ -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');
}
}

View File

@ -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');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class RefreshIcon extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('nodejs.components.refresh-icon');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class ResponsiveNavLink extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('nodejs.components.responsive-nav-link');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class SuccessIcon extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('nodejs.components.success-icon');
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class TextInput extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('nodejs.components.text-input');
}
}

53
artisan Normal file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any of our classes manually. It's great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);

55
bootstrap/app.php Normal file
View File

@ -0,0 +1,55 @@
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;

Some files were not shown because too many files have changed in this diff Show More