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 2 weeks ago Last modified on 12/29/25 14:23:48
Note: See TracWiki for help on using the wiki.