wiki:UseCaseRealisations

==UseCaseRealisations

ИД 1

Откако корисникот ќе се најави оди да си одбере предмети. Студентот клика на "Предмети"

  • GET барање кон /subjects рута
  • SubjectController@index() акција се активира

Си бира година, како и кариерна патека и смер.

Серверот прикажува листа на сите предмети

// SubjectController.php
public function index(Request $request)
{
    $subjects = Subject::with('prerequisites')
        ->orderBy('code')
        ->paginate(15);
    
    return view('admin.subjects.index', [
        'subjects' => $subjects
    ]);
}

Откако ќе одбере му дава листа на предмети популирани од база, за неговиот смер и година. Сортирани по зимски и летен семестар.

ИД 2

Серверот филтрира по пребарување

// SubjectController.php
public function index(Request $request)
{
    $query = Subject::query();
    
    // Филтрирање по пребарување
    if ($request->filled('search')) {
        $search = $request->input('search');
        $query->where('code', 'LIKE', "%{$search}%")
              ->orWhere('name', 'LIKE', "%{$search}%")
              ->orWhere('name_mk', 'LIKE', "%{$search}%");
    }
    
    // Филтрирање по година
    if ($request->filled('year')) {
        $query->where('year', $request->input('year'));
    }
    
    // Филтрирање по семестар
    if ($request->filled('semester')) {
        $query->where('semester_type', $request->input('semester'));
    }
    
    // Филтрирање по тип
    if ($request->filled('type')) {
        $query->where('subject_type', $request->input('type'));
    }
    
    $subjects = $query->with('prerequisites')
                     ->orderBy('code')
                     ->paginate(15);
    
    return view('admin.subjects.index', ['subjects' => $subjects]);
}

Откако тој ќе си ги пополни си оди директно на Генерирај роадмап копчето.

ИД 3

  1. Студентот кликне на "Создај мој план/Roadmap"
    • GET барање кон /roadmap/create
    • RoadmapController@create() се активира

Тука е даден целиот опис на академскиот роадмап и кои се препорачани предмети според избирачката (опционално) кариера.

Серверот враќа форма за креирање

// RoadmapController.php
public function create()
{
    $studyPrograms = StudyProgram::all();
    $careerPaths = CareerPath::all();
    
    return view('roadmap.create', [
        'studyPrograms' => $studyPrograms,
        'careerPaths' => $careerPaths
    ]);
}

Апликацијата прикажува ЧЕКОР 1: Избор програма и кариера

@csrf

JavaScriptAJAX барање за предмети

// views/roadmap/create.blade.php
document.getElementById('programSelect').addEventListener('change', function() {
    const programId = this.value;
    
    if (!programId) return;
    
    // AJAX fetch за да добиeме предмети од оваа програма
    fetch(`/api/study-program/${programId}/subjects`, {
        method: 'GET',
        headers: {
            'Accept': 'application/json'
        }
    })
    .then(response => response.json())
    .then(data => {
        displaySubjectsByYear(data.subjects);
    })
    .catch(error => console.error('Error:', error));
});

Серверот враќа JSON со предмети

// RoadmapController.php
public function getSubjectsByProgram($programId)
{
    $program = StudyProgram::findOrFail($programId);
    
    // Вчитај сите предмети од програмата со pivot info
    $subjects = $program->subjects()
        ->with('prerequisites')
        ->get()
        ->map(function($subject) {
            return [
                'id' => $subject->id,
                'code' => $subject->code,
                'name_mk' => $subject->name_mk,
                'name_en' => $subject->name_en,
                'year' => $subject->pivot->year,
                'semester_type' => $subject->pivot->semester_type,
                'type' => $subject->pivot->type,
                'credits' => $subject->credits,
                'prerequisites' => $subject->prerequisites->pluck('id')
            ];
        });
    
    return response()->json(['subjects' => $subjects]);
}


JavaScript приказует ЧЕКОР 2: Избор завршени предмети

function displaySubjectsByYear(subjects) {
    // Организирај по години
    const subjectsByYear = {};
    
    subjects.forEach(subject => {
        if (!subjectsByYear[subject.year]) {
            subjectsByYear[subject.year] = [];
        }
        subjectsByYear[subject.year].push(subject);
    });
    
    // Креирај HTML за секоја година
    let html = '<div class="subjects-grid">';
    
    Object.keys(subjectsByYear).sort().forEach(year => {
        html += `<div class="year-section">
            <h3>Година ${year}</h3>
            ${['winter', 'summer'].map(semester => {
                const semesterSubjects = subjectsByYear[year]
                    .filter(s => s.semester_type === semester);
                
                return `<div class="semester">
                    <h4>${semester === 'winter' ? 'Зимски' : 'Летен'}</h4>
                    ${semesterSubjects.map(subject => `
                        <div class="subject-card">
                            <input type="checkbox" name="completed_subjects[]" 
                                   value="${subject.id}" 
                                   class="subject-checkbox">
                            <strong>${subject.code}</strong> - ${subject.name_mk}
                            <span class="credits">(${subject.credits} ECTS)</span>
                            <span class="type badge-${subject.type}">
                                ${subject.type === 'mandatory' ? 'Задолжително' : 'Изборно'}
                            </span>
                        </div>
                    `).join('')}
                </div>`;
            }).join('')}
        </div>`;
    });
    
    html += '</div>';
    
    document.getElementById('subjectsContainer').innerHTML = html;
    document.getElementById('step2').style.display = 'block';
}

Студентот кликне "ГЕНЕРИРАЈ МОЈ ПЛАН"

  • JavaScript собира податоци од checkbox-ите


JavaScript ја повикува POST барање

document.getElementById('roadmapForm').addEventListener('submit', function(e) {
    e.preventDefault();
    
    const formData = new FormData(this);
    
    // Конвертирај во JSON
    const data = {
        study_program_id: formData.get('study_program_id'),
        career_path_id: formData.get('career_path_id'),
        completed_subjects: Array.from(document.querySelectorAll(
            'input[name="completed_subjects[]"]:checked'
        )).map(cb => cb.value),
        in_progress_subjects: Array.from(document.querySelectorAll(
            'input[name="in_progress_subjects[]"]:checked'
        )).map(cb => cb.value)
    };
    
    fetch('/roadmap', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
        },
        body: JSON.stringify(data)
    })
    .then(response => response.text())
    .then(html => {
        document.body.innerHTML = html;
    })
    .catch(error => console.error('Error:', error));
});

Серверот прима POST барање и генерира план

// RoadmapController.php
public function store(Request $request)
{
    $validated = $request->validate([
        'study_program_id' => 'required|exists:study_programs,id',
        'career_path_id' => 'nullable|exists:career_paths,id',
        'completed_subjects' => 'array',
        'in_progress_subjects' => 'array'
    ]);
    
    $user = Auth::user();
    $studyProgram = StudyProgram::find($validated['study_program_id']);
    
    // Избриши стари записи
    UserProgress::where('user_id', $user->id)
        ->where('study_program_id', $studyProgram->id)
        ->delete();
    
    // Создај нови за завршени
    foreach ($validated['completed_subjects'] as $subjectId) {
        UserProgress::create([
            'user_id' => $user->id,
            'subject_id' => $subjectId,
            'study_program_id' => $studyProgram->id,
            'career_path_id' => $validated['career_path_id'] ?? null,
            'status' => 'completed',
            'completed_at' => now()
        ]);
    }
    
    // Создај нови за во-прогрес
    foreach ($validated['in_progress_subjects'] as $subjectId) {
        UserProgress::create([
            'user_id' => $user->id,
            'subject_id' => $subjectId,
            'study_program_id' => $studyProgram->id,
            'career_path_id' => $validated['career_path_id'] ?? null,
            'status' => 'in_progress'
        ]);
    }
    
    // Генерирај три-слојна препорака
    $roadmap = $this->generateRoadmap($user, $studyProgram, $validated);
    $semesterRoadmap = $this->generateSemesterRoadmap($roadmap);
    
    return view('roadmap.show', [
        'roadmap' => $roadmap,
        'semesterRoadmap' => $semesterRoadmap,
        'studyProgram' => $studyProgram,
        'careerPath' => $validated['career_path_id'] 
            ? CareerPath::find($validated['career_path_id']) 
            : null
    ]);
}

// ФИЛТРИРАЊЕ: Три слоја
private function generateRoadmap($user, $studyProgram, $validated)
{
    $roadmap = [];
    $completedIds = collect($validated['completed_subjects'])->map(fn($id) => (int)$id);
    $inProgressIds = collect($validated['in_progress_subjects'])->map(fn($id) => (int)$id);
    
    $allSubjects = $studyProgram->subjects()->with('prerequisites')->get();
    
    foreach ($allSubjects as $subject) {
        // СЛОЈ 1: Проверка на година
        if ($subject->year > $studyProgram->duration_years) {
            continue; // Прескочи Year 5+ во 4-годишна програма
        }
        
        // СЛОЈ 2: Веќе завршено?
        if ($completedIds->contains($subject->id) || $inProgressIds->contains($subject->id)) {
            continue; // Не препорачувај што вече го взел
        }
        
        // СЛОЈ 3: Каријерна патека?
        if ($validated['career_path_id']) {
            $careerPath = CareerPath::find($validated['career_path_id']);
            $isElective = $subject->pivot->type === 'elective';
            $isInPath = $careerPath->subjects->contains($subject->id);
            
            if ($isElective && !$isInPath) {
                continue; // Елективи не во патеката не се препорачуваат
            }
        }
        
        // Проверка на предуслови
        $prerequisites = $subject->prerequisites()->get();
        $ready = true;
        $blockedBy = [];
        
        foreach ($prerequisites as $prereq) {
            if (!$completedIds->contains($prereq->id)) {
                $ready = false;
                $blockedBy[] = $prereq;
            }
        }
        
        // Додај во roadmap
        $roadmap[] = [
            'subject' => $subject,
            'ready' => $ready,
            'blocked_by' => $blockedBy,
            'year' => $subject->pivot->year,
            'semester' => $subject->pivot->semester_type
        ];
    }
    
    // Сортирај: Ready first, потоа по година, потоа по ред
    usort($roadmap, function($a, $b) {
        if ($a['ready'] !== $b['ready']) {
            return $a['ready'] ? -1 : 1; // Ready прво
        }
        if ($a['year'] !== $b['year']) {
            return $a['year'] - $b['year']; // После по година
        }
        return 0;
    });
    
    return $roadmap;
}

private function generateSemesterRoadmap($roadmap)
{
    $semesterRoadmap = [];
    
    foreach ($roadmap as $item) {
        $year = $item['year'];
        $semester = $item['semester'];
        
        if (!isset($semesterRoadmap[$year])) {
            $semesterRoadmap[$year] = [
                'winter' => [],
                'summer' => []
            ];
        }
        
        $semesterRoadmap[$year][$semester][] = $item;
    }
    
    return $semesterRoadmap;
}


Студентот може да ги види препораките

  • Зелени: Готови за запишување оваа година
  • Црвени: Блокирани - мора прво да завршат [Предуслови]

Апликацијата прикажува РЕЗУЛТАТ

  • Резиме на напредок (X завршени, Y во-прогрес)
  • ECTS прогресна лента
  • Семестар-по-семестар план:
    • Година 1 Зимски: F23L3W001, F23L3W002...
    • Година 1 Летен: F23L3S003...
  • Препорачани следни чекори (зелено: готови, црвено: блокирани)


### Исклучоци:

  • Нелогиран корисник: Редирекција кон /login
  • Невалидна програма: 404 грешка
  • Нема избрани предмети: Покажи порака да избере барем еден
  • Конфликт на години: Предмет е во повеќе години (обично задолжително)

ИД 4

Одржување на информации за предмети (Админ)

Овдека остануваат повеќето crud функционалности

No image "admin1.png" attached to UseCaseRealisations CRUD за предмети

No image "admin2.png" attached to UseCaseRealisations Уредување на предмет како и додавање на предуслови и кариера.

No image "admin3.png" attached to UseCaseRealisations CRUD за студиски програми

No image "admin4.png" attached to UseCaseRealisations Тука уредувачот може да додаде предмети на дадени студиски програми.

Last modified 10 months ago Last modified on 12/29/25 14:23:48
Note: See TracWiki for help on using the wiki.