o
    ;Qh                     @   s  d dl mZ d dlmZ d dlmZ d dlmZ d dlm	Z	 d dl
Z
d dlZd dlmZmZmZ d dlmZ eG d	d
 d
ZG dd deZG dd deZG dd dZdd Zdd Zdd Zedkr
ed eddZed ed ed dZdd d!d"d#d!d$d%d!d&d'd!d(d)d!d*d+d!d,d-d!gZd.Zd/Zed0 ed1e  ed2e  ed3e  ed4e  ej eeeed5Z!d+Z"ej#ee"eed6Z$ed7 ee$ ed8 d9d: e$d; D Z%ed< ee% d=d>d?d@dAdBdCdDdEdFdG
Z&eee%e&Z'edH dS dS )I    )OllamaPromptTemplate)BaseOutputParser)LLMChain)
ChatOllamaN)DictListOptional)	dataclassc                   @   s&   e Zd ZU eed< eed< eed< dS )	SkillDatanamecountlevelN)__name__
__module____qualname__str__annotations__int r   r   H/var/www/eduai.edurigo.com/doc_train/edurigo_ai/TNA/testing/tna_final.pyr      s   
 r   c                   @   4   e Zd ZdZdedefddZdedefddZdS )	DepartmentOutputParserz-Parser for Step 1 - Department Identificationtextreturnc              
   C   sl   zt d|t j}|rt| W S | |W S  ty5 } zdddt| dW  Y d }~S d }~ww )N\{.*?\}UnknownlowzParse error: identified_department
confidence	reasoning)	researchDOTALLjsonloadsgroup_extract_department_from_text	Exceptionr   )selfr   
json_matcher   r   r   parse   s   zDepartmentOutputParser.parsec                 C   s   ddddS )Nr   mediumz#Could not parse structured responser   r   )r+   r   r   r   r   r)   #   s   z4DepartmentOutputParser._extract_department_from_textN)r   r   r   __doc__r   r   r.   r)   r   r   r   r   r      s    r   c                   @   r   )	SkillAnalysisOutputParserz+Parser for Step 2 - Skill Priority Analysisr   r   c              
   C   s   z?t d|t j}|rt| }d|dW S t d|t j}|r:t| }dt|tr5|gdW S |dW S | |W S  t	yY } zdt
|g dW  Y d }~S d }~ww )Nz\[.*?\]T)successskill_recommendationsr   Fr2   errorr3   )r#   r$   r%   r&   r'   r(   
isinstancedict_extract_skills_from_textr*   r   )r+   r   r,   skills_data
skill_datar-   r   r   r   r.   -   s.   zSkillAnalysisOutputParser.parsec           
         s  g }|  d}i }|D ]  rdrqtd}|r8|r.d|v r.|| d|d  i}qtfdddD rc|rPd|v rP|| i }td	d
dd   }||d< qd	 v r{t
d}|rzt|d|d< qtfdddD rdd    t fdddD r |d< qd	 v rt
d}|rt|d|d< q|rd|v r|| |D ]&}	|	dd |	dd |	dd |	dd |	dd |	dd q|rd nd!|t|d"S )#z,Enhanced text extraction for multiple skills
#z^\d+\.\s*(.+)
skill_name   c                 3       | ]	}|   v V  qd S Nlower.0keywordliner   r   	<genexpr>`       zFSkillAnalysisOutputParser._extract_skills_from_text.<locals>.<genexpr>)zskill:zskill_name:- z^[\-\*\d\.\s]* :priorityz(\d+(?:\.\d+)?)priority_percentagec                 3   r?   r@   rA   rC   rF   r   r   rH   m   rI   )zlevel:requiredc                 3   r?   r@   rA   )rD   r   )
level_textr   r   rH   o   rI   )beginnerintermediateexpertrequired_levelcoveragetarget_coverageg      I@Intermediatecurrent_coverageg        g      N@gap_typezMissing Skillr"   z)Standard skill requirement for departmentTF)r2   r3   extracted_count)stripsplit
startswithr#   matchappendr(   anysubrB   r$   float
setdefaultlen)
r+   r   skillslinescurrent_skillnumbered_matchr=   priority_matchcoverage_matchskillr   )rQ   rG   r   r8   J   s^   




z3SkillAnalysisOutputParser._extract_skills_from_textN)r   r   r   r0   r   r   r.   r8   r   r   r   r   r1   *   s    r1   c                
   @   sH  e Zd Zd-dedefddZdefdd	Zdefd
dZdedee	 de	fddZ
dedee	 dee	 fddZdededee de	fddZdedee	 dedede	f
ddZdedededede	f
ddZdee dedefd d!Zdedee fd"d#Zd$edefd%d&Zd'ee	 dedee	 fd(d)Zd'ee	 de	fd*d+Zd,S ).TwoStepTNASystem
gemma3:12bhttp://localhost:11434
model_namebase_urlc                 C   sf   || _ || _t||ddd| _t | _t | _t| j| 	 | jdd| _
t| j|  | jdd| _dS )z
        Initialize Two-Step TNA System
        
        Args:
            model_name: Ollama model name
            base_url: Ollama server URL
        g?i  )modelrq   temperaturenum_predictF)llmpromptoutput_parserverboseN)rp   rq   r   ru   r   dept_parserr1   skill_parserr   _create_department_prompt
dept_chain_create_skill_analysis_promptskill_chain)r+   rp   rq   r   r   r   __init__   s,   zTwoStepTNASystem.__init__r   c                 C   s   d}t ddg|dS )z=Create prompt template for Step 1 - Department Identificationas  
You are an expert at identifying organizational departments based on user descriptions.

TASK: Analyze the user prompt and identify which specific department they are referring to from the organization's department list.

ORGANIZATION DEPARTMENTS: {organization_departments}

USER PROMPT: "{user_prompt}"

INSTRUCTIONS:
1. Carefully analyze keywords, responsibilities, and context in the user prompt
2. Match the description to the most appropriate department from the provided list
3. Consider typical department functions and responsibilities
4. Choose the best match from the given department list
5. Provide confidence level and clear reasoning

RESPOND IN THIS EXACT JSON FORMAT:
{{
    "identified_department": "exact department name from the list",
    "confidence": "high/medium/low",
    "reasoning": "brief explanation of why this department was chosen"
}}

JSON Response:user_promptorganization_departmentsinput_variablestemplater   r+   r   r   r   r   r{      s
   z*TwoStepTNASystem._create_department_promptc                 C   s   d}t g d|dS )z;Create prompt template for Step 2 - Skill Priority Analysisa	  
You are an expert Training Need Analysis (TNA) consultant specializing in skill gap analysis.

TASK: Analyze the current skills for the {department} department and recommend skill priorities that need to be addressed.

DEPARTMENT: {department}
TOTAL EMPLOYEES: {employee_count}
CURRENT SKILLS: {current_skills}

ANALYSIS REQUIREMENTS:

1. COVERAGE ANALYSIS:
   - Calculate current skill coverage percentages
   - Identify skills with insufficient coverage
   - Standard target: 50-70% coverage for most skills, 70%+ for critical skills

2. LEVEL ANALYSIS:
   - Assess if current skill levels are appropriate for the department
   - Consider if Beginner/Intermediate/Expert levels match job requirements

3. MISSING SKILLS:
   - Identify essential skills that are completely missing
   - Consider both technical and soft skills needed for this department

4. PRIORITY CALCULATION:
   - Base priority on: coverage gaps, level gaps, business impact, urgency
   - Priority scale: 90-100% (Critical), 70-89% (Important), 50-69% (Moderate), 30-49% (Nice-to-have), 10-29% (Low)

5. DEPARTMENT-SPECIFIC FOCUS:
   - Consider the core functions and responsibilities of {department}
   - Include industry-standard skills for this department type
   - Think about both current needs and future requirements

IMPORTANT: You MUST provide EXACTLY 10-15 skill recommendations. Include:
- Analysis of existing skills that need improvement
- Essential missing skills for this department
- Both technical and soft skills
- Skills at different priority levels

RESPOND WITH JSON ARRAY OF EXACTLY 10-15 SKILL RECOMMENDATIONS:

[
  {{
    "skill_name": "exact skill name",
    "required_level": "Beginner/Intermediate/Expert",
    "priority_percentage": 85.5,
    "current_coverage": 25.0,
    "target_coverage": 70.0,
    "gap_type": "Coverage Gap/Level Gap/Missing Skill/Enhancement",
    "reasoning": "detailed explanation of why this skill needs attention and the assigned priority"
  }},
  {{
    "skill_name": "another skill name",
    "required_level": "Intermediate",
    "priority_percentage": 78.0,
    "current_coverage": 0.0,
    "target_coverage": 60.0,
    "gap_type": "Missing Skill",
    "reasoning": "explanation for this skill priority"
  }}
]

CRITICAL: Provide MINIMUM 10 skills, MAXIMUM 15 skills. Do not provide fewer than 10 recommendations.
Sort by priority percentage (highest first).
Include a variety of skills: existing skills needing improvement + completely missing essential skills.

JSON Response:
departmentemployee_countcurrent_skillsr   r   r   r   r   r   r}      s
   Dz.TwoStepTNASystem._create_skill_analysis_promptr   r   c           	   
   C   sP  zt d t d|  t dtdd |D   dd |D }|d|d}| j|}t|tr|d	d
}| ||}|rn|d |d	< |d |d< ||d< d|d< t d|d  d|d  d|dd d |W S d|d< d| d|d< d|d	< d|d< |W S dddddW S  t	y } zdt|dddW  Y d}~S d}~ww )a  
        STEP 1: Use LLM to identify department from user prompt
        
        Args:
            user_prompt: User's description/prompt about the department
            organization_departments: List of department dictionaries with 'id' and 'name' keys
                                     Example: [{'id': 1, 'name': 'HR'}, {'id': 2, 'name': 'Engineering'}]
            
        Returns:
            Dict with identified department info, confidence, and reasoning
        u2   🔄 STEP 1: Identifying department from prompt...z   Prompt: z   Available Departments: c                 S   s$   g | ]}|d   d|d  dqS )r    (ID: id)r   rD   deptr   r   r   
<listcomp>/  s   $ z>TwoStepTNASystem.step1_identify_department.<locals>.<listcomp>c                 S      g | ]}|d  qS )r   r   r   r   r   r   r   5      , )r   r   r    rK   r   r   department_iddepartment_infoTr2   u!   ✅ STEP 1 Complete: Identified 'z' (ID: z) with r!   unknownz confidenceFzDepartment 'z ' not found in organization listr5   Nz2Failed to parse department identification response)r2   r5   r    r   )
printr   joinr|   runr6   r7   get_match_department_to_listr*   )	r+   r   r   department_nameschain_inputresultidentified_dept_namematched_dept_infor-   r   r   r   step1_identify_department   sJ   
,z*TwoStepTNASystem.step1_identify_departmentidentified_dept	dept_listc                 C   sp   |sdS |   }|D ]}|d    |kr|  S q|D ]}|d    }||v s1||v r5|  S qdS )aU  
        Match identified department with organization's department list
        
        Args:
            identified_dept: Department name identified by LLM
            dept_list: List of department dictionaries with 'id' and 'name' keys
            
        Returns:
            Dict with department info if found, None otherwise
        Nr   )rB   r\   )r+   r   r   identified_lowerr   dept_name_lowerr   r   r   r   b  s   z*TwoStepTNASystem._match_department_to_listr   r   r   c                 C   s  zt d| d t d|  t dt| d | ||}|||d}| j|}t dt|  t dt|d	d
  d t|tr|	drt|	dg }t d| d | 
|d |}d||t||| |t|j|t|dd}	t dt| d |	W S t d t|}
| j|
}|	dr|	dr| 
|d |}t dt| d d||t||| |d|	ddddW S ddg dtt|dkrt|d	d d nt|idW S  ty } zdt|g d W  Y d	}~S d	}~ww )!a  
        STEP 2: Use LLM to analyze skills for the identified department
        
        Args:
            department: Department identified from Step 1
            employee_count: Total number of employees in department
            current_skills: List of current skills with counts and levels
            
        Returns:
            Dict with skill priority recommendations
        u"   🔄 STEP 2: Analyzing skills for z department...   Employee Count:    Current Skills: z skills providedr   u    🔍 Debug - LLM Response type: u   🔍 Debug - Response preview: N   z...r2   r3   u   🔍 Debug - Parsed z skills from JSON responseT)raw_response_typeraw_skill_countprocessed_skill_count)r2   r   r   total_recommendationsr3   analysis_summary
debug_infou   ✅ STEP 2 Complete: Generated z skill recommendationsuY   ⚠️  Warning: Received non-dict or unsuccessful response, attempting manual parsing...u0   ✅ STEP 2 Complete (manual parsing): Generated manual_extractionr[   r   )parsing_methodr[   FzIFailed to parse skill analysis response with both JSON and manual methodsraw_responsei  )r2   r5   r3   r   r4   )r   re   _format_skills_for_llmr~   r   typer   r6   r7   r   _post_process_recommendations_generate_summaryr   rz   r8   r*   )r+   r   r   r   skills_descriptionr   r   skill_countprocessed_recsfinal_resultraw_textmanual_resultr-   r   r   r   step2_analyze_skills  s|   
,	z%TwoStepTNASystem.step2_analyze_skillsskills_inputc                 C   s   t d t d | ||}|dsdd|dd|dS |d	 }|d
 }|d }t d| d| d t d | |}	| |||	}
|
dsVdd|
dd||
dS d||
|
d |
d dS )a"  
        Complete two-step TNA analysis
        
        Args:
            user_prompt: User's description of department/team
            organization_departments: List of department dictionaries with 'id' and 'name' keys
                                    Example: [{'id': 1, 'name': 'HR'}, {'id': 2, 'name': 'Engineering'}]
            employee_count: Total employees in the department
            skills_input: Current skills data (formatted string)
            
        Returns:
            Complete analysis results from both steps
        u&   🚀 Starting Complete TNA Analysis...zP================================================================================r2   Fr>   r5   z Department identification failed)r2   stepr5   step1_resultr    r   r   u   ✅ Department Identified: r   r      Skill analysis failed)r2   r   r5   r   step2_resultTr3   r   )r2   r   r   final_recommendationsr   )r   r   r   _parse_skill_inputr   )r+   r   r   r   r   r   r    r   r   r   r   r   r   r   complete_tna_analysis  s:   





z&TwoStepTNASystem.complete_tna_analysisr    c                 C   sN   |  |}| |||}|dsdd|dd|dS d||d |d	 d
S )Nr2   Fr   r5   r   )r2   r   r5   r   Tr3   r   )r2   r   r   r   )r   r   r   )r+   r   r    r   r   r   r   r   r   r   tna_skill_analysis_use"  s   


	z'TwoStepTNASystem.tna_skill_analysis_userf   c                 C   s`   |sdS g }|D ]"}|j | d }|d|j d|j  d| d|dd|j d	 qd
|S )z#Format current skills for LLM inputz No current skills data provided.d   rJ   z: /z employees (z.1fz% coverage) at z levelr;   )r   r`   r   r   r   )r+   rf   r   	formattedrl   rV   r   r   r   r   9  s   *
z'TwoStepTNASystem._format_skills_for_llmc           	      C   s   g }|  s|S td|  }|D ]-}td|  }|r?|d  }t|d}| |d  }|t||| q|S )z/Parse skill input string into SkillData objectsz,\s*z(.+?)-(\d+)\s*\((.+?)\)r>   r      )	r\   r#   r]   r_   r(   r   _normalize_skill_levelr`   r   )	r+   r   rf   skill_partspartr_   r=   r   r   r   r   r   r   G  s   z#TwoStepTNASystem._parse_skill_inputr   c                 C   sH   |  }|drdS |dsd|v rdS |ds |dr"dS dS )	z(Normalize skill level to standard formatbeginBeginnerinterr/   rX   rT   advExpert)rB   r^   )r+   r   level_lowerr   r   r   r   Z  s   
z'TwoStepTNASystem._normalize_skill_levelrecommendationsc                 C   s   g }|D ]g}zZ| dd | | ddtdtdt| ddtdtdt| d	dtdtdt| d
d| dd | dd d}t|d
 d | |d< || W q tt	fyk   Y qw |j
dd dd |S )z)Post-process and validate recommendationsr=   r   rU   rX   r   r   rO   2   rY   rW   rZ   r"   zNo reasoning provided)r=   rU   rO   rY   rW   rZ   r"   target_employee_countc                 S   s   | d S )NrO   r   )xr   r   r   <lambda>  s    z@TwoStepTNASystem._post_process_recommendations.<locals>.<lambda>T)keyreverse)r   r\   r   maxminrc   r   r`   
ValueError	TypeErrorsort)r+   r   r   	processedrecprocessed_recr   r   r   r   e  s(   z.TwoStepTNASystem._post_process_recommendationsc                 C   s   |sddiS t dd |D }t dd |D }t dd |D }tdd |D t | }t ||||d	t|d
|rD|d d dS ddS )zGenerate analysis summarymessagezNo recommendations generatedc                 S   s   g | ]
}|d  dkr|qS )rO   P   r   rD   rr   r   r   r         z6TwoStepTNASystem._generate_summary.<locals>.<listcomp>c                 S   s,   g | ]}d |d   krdk rn n|qS )r   rO   r   r   r   r   r   r   r     s   , c                 S   s   g | ]
}|d  dk r|qS )rO   r   r   r   r   r   r   r     r   c                 s   s    | ]}|d  V  qdS )rO   Nr   r   r   r   r   rH     s    z5TwoStepTNASystem._generate_summary.<locals>.<genexpr>)high_prioritymedium_prioritylow_priorityr>   r   r=   N)total_skillspriority_distributionaverage_prioritytop_priority_skill)re   sumround)r+   r   r   r   r   avg_priorityr   r   r   r     s    z"TwoStepTNASystem._generate_summaryN)rn   ro   )r   r   r   r   r   r   r{   r}   r	   r   r   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rm      s<    '!LB

f

=
rm   c                 C   sn  | r|st dt|}|d }|d }tddgdd}tg dd	d}td
dd}| }	||B }
|	|d}||B }|	||d}||}|
|}|j}|j}|dd }|dd }t|}t	d t	| t	d t|}|
dg }|
dd}|
di }|
dd}|
dg }|p|}dd |D }d|||||di}t	tj|dd |S )N0Goal/problem and missing skills must be providedformatted_stringskills_with_levelsgoalmissing_skills_stra,  
You are a professional learning and development advisor.

Given the user's business goal, and their identified missing skills WITH REQUIRED LEVELS, output the recommendation report in **STRICT JSON FORMAT**.

### JSON Output Format:

{{
"Goal":{{
  "goalTitle": "string",
  "course_recommendations_online": [
    {{
      "platform": "Platform name",
      "course_name": "Course title",
      "level": "Beginner / Intermediate / Advanced",
      "target_skill": "Skill name being addressed",
      "required_level": "Required proficiency level for this skill",
      "why_recommended": "Reason why the course is useful for achieving the required skill level",
      "relevance_score": integer (0-100),
      "link": "Course URL or 'Available on [Platform]'"
    }}
  ]
  }}
}}

**Rules:**
- Only return valid JSON, no markdown, no headings.
- Use double quotes `"` around all keys and string values.
- Ensure the JSON is syntactically correct (ready for `json.loads()`).
- If a field is missing, use an empty string "" or empty array [].
- Only give those courses which relevance_score more than 75
- Consider the required proficiency level when recommending courses
- Match course difficulty to the required skill level

---

### Inputs:

Goal / Problem:
{goal}

Missing Skills with Required Levels:
{missing_skills_str}
r   r   r   course_data_jsona  
You are a professional learning and development advisor.

You are given:
- A business goal
- A list of missing skills WITH REQUIRED LEVELS
- A database of available courses (with name and description)

### Business Goal:
{goal}

### Missing Skills with Required Levels:
{missing_skills_str}

### Available Courses:
{course_data_json}

Your task is to:
1. Evaluate each course for how well it helps achieve the goal and address the missing skills at the required proficiency levels.
2. Select the most relevant courses (maximum 5).
3. For each course, return ONLY the following fields:
   - "course_name": string
   - "course_id": string
   - "target_skill": string (which skill this course addresses)
   - "required_level": string (required proficiency level for the skill)
   - "reason_to_recommend": string
   - "relevance_score": integer (0-100)

Don't give any extra text. Just return JSON Output.

### JSON Output Format:

{{
"Goal":{{
  "goaltitle": "string",
  "course_recommendations_offline": [
    {{
      "course_name": "Course title",
      "course_id": "Course ID from course_data_json",
      "target_skill": "Skill name being addressed",
      "required_level": "Required proficiency level for this skill",
      "why_recommended": "Reason why the course is useful for achieving the required skill level",
      "relevance_score": integer (0-100),
      "link": "Course URL or 'Available on [Platform]'"
    }}
  ],
  "suggested_order": [
    "Course 1 name",
    "Course 2 name",
    "Course 3 name"
  ]
  }}
}}

**Rules:**
- Only return valid JSON, no markdown, no headings.
- Use double quotes `"` around all keys and string values.
- Ensure the JSON is syntactically correct (ready for `json.loads()`).
- If a field is missing, use an empty string "" or empty array [].
- Only give those courses which relevance_score more than 75
- Consider the required proficiency level when recommending courses
rn   r   rr   rs   r   r   ```json```z`=============================================================================Offline Goal Start z]=============================================================================Offline Goal Endcourse_recommendations_offline	goaltitlerK   Goal	goalTitlecourse_recommendations_onlinec                 S   r   )r=   r   )rD   
skill_infor   r   r   r   L  r   z=recommend_online_offline_courses_goal_try.<locals>.<listcomp>)r   missing_skillsmissing_skills_with_levelsr   r   r   indent)r   "process_missing_skills_with_levelsr   r   invokecontentr\   r&   r'   r   r   dumps)r   r  courses_availableprocessed_skills_infor   r   course_recommendation_prompt2'course_recommendation_prompt_simplifiedru   goal_stronline_course_extraction_chaininput_data_onlineoffline_course_extraction_chaininput_data_offlinerecommendations_offlinerecommendations_onlinerecommendations_offline_data1recommendations_online_data1recommendations_offline_data2recommendations_online_data2recommendations_offline_datarecommendations_online_datar   goal_title_offlinegoal_data_onlinegoal_title_onliner   final_goal_titlemissing_skills_listfinal_outputr   r   r   )recommend_online_offline_courses_goal_try  sn   0B



r!  c                 C   s   g }t | trdd | dD }dd |D }n8t | trQ| D ]0}t |tr0||dd q t |trF||dd|d	dd q |t|dd q g }|D ]}||d  d
|d	  d qUd|}||dS )z
    Process missing skills input to handle different formats:
    - List of skill names: ["CRM Tools", "Communication"]
    - List of skill objects: [{"skill_name": "CRM Tools", "required_level": "Intermediate"}]
    - Mixed format
    c                 S      g | ]}|  qS r   r\   rD   rl   r   r   r   r   h  r   z6process_missing_skills_with_levels.<locals>.<listcomp>,c                 S   s   g | ]}|d dqS )rX   r=   rU   r   )rD   r   r   r   r   r   i  s    rX   r&  r=   rK   rU   z (Required Level: r   r   )r   r   )r6   r   r]   listr`   r7   r   r   )r  r   skill_namesrl   formatted_skillsr  r   r   r   r   r  ]  s6   








 
r  c                 C   s  | r|st dtddgdd}tg ddd}tdd	d
}| }|}||B }||d}	||B }
|||d}|
|}||	}|j}|j}|dd }|dd }t|}t|}|dg }|dd}|di }|dd}|dg }|p|}t	|t
rdd |dD }nt	|trtdd |D rdd |D }n|}td t| td t| |||d}ttj|dd |S )Nr   r   r   a  
You are a professional learning and development advisor.

Given the user's business goal , and their identified missing skills, output the recommendation report in **STRICT JSON FORMAT**.

### JSON Output Format:

{{
Goal:{{
  "goalTitle": "string",
  "course_recommendations_online": [
    {{
      "platform": "Platform name",
      "course_name": "Course title",
      "level": "Beginner / Intermediate / Advanced",
      "why_recommended": "Reason why the course is useful",
      "relevance_score": integer (0-100),
      "link": "Course URL or 'Available on [Platform]'"
    }}
  ]
  
  }}
}}

**Rules:**
- Only return valid JSON, no markdown, no headings.
- Use double quotes `"` around all keys and string values.
- Ensure the JSON is syntactically correct (ready for `json.loads()`).
- If a field is missing, use an empty string "" or empty array [].
- Only give those courses which relevance_score more than 75

---

### Inputs:

Goal / Problem:
{goal}

Missing Skills:
{missing_skills_str}
r   r   ag  
You are a professional learning and development advisor.

You are given:
- A business goal
- A list of missing skills
- A database of available courses (with name and description)

### Business Goal:
{goal}

### Missing Skills:
{missing_skills_str}

### Available Courses:
{course_data_json}

Your task is to:
1. Evaluate each course for how well it helps achieve the goal and address the missing skills.
2. Select the most relevant courses (maximum 5).
3. For each course, return ONLY the following fields:
   - "course_name": string
   - "course_id" : string
   - "reason_to_recommend": string
   - "relevance_score": integer (0-100)

Don't give any extra text . Just return JSON Output.
Here is the JSON class to validate the output that you give. Make sure the check always passes.

class SimpleOfflineCourseRecommendation(BaseModel):
    
    ### JSON Output Format:


Goal:{{
  "goaltitle": "string",
  "course_recommendations_offline": [
    {{
      
      "course_name": "Course title",
      "course_id" : Course ID from course_data_json,
      "why_recommended": "Reason why the course is useful",
      "relevance_score": integer (0-100),
      "link": "Course URL or 'Available on [Platform]'"
    }}
  ],
  "suggested_order": [
    "Course 1 name",
    "Course 2 name",
    "Course 3 name"
  ]
  }}


**Rules:**
- Only return valid JSON, no markdown, no headings.
- Use double quotes `"` around all keys and string values.
- Ensure the JSON is syntactically correct (ready for `json.loads()`).
- If a field is missing, use an empty string "" or empty array [].
- Only give those courses which relevance_score more than 75


rn   r   r   r   r   r   r   r   rK   r   r   r   c                 S   r"  r   r#  r$  r   r   r   r   <  r   z9recommend_online_offline_courses_goal.<locals>.<listcomp>r%  c                 s   s    | ]}t |tV  qd S r@   )r6   r'  )rD   itemr   r   r   rH   =  s    z8recommend_online_offline_courses_goal.<locals>.<genexpr>c                 S   s   g | ]	}|D ]}|qqS r   r   )rD   sublistrl   r   r   r   r   ?  s    !course_recommendations_offline234 course_recommendations_online234)r  r   r   r   r  )r   r   r   r  r  r\   r&   r'   r   r6   r   r]   r'  allr   r	  )r   r  r
  r  r  ru   r  r   r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r   r  r  r   r   r   r   %recommend_online_offline_courses_goal  sl   -C




r/  __main__z#Initializing Two-Step TNA System...rn   )rp   ze
====================================================================================================z'EXAMPLE: COMPLETE TWO-STEP TNA ANALYSISzd====================================================================================================zZWe need training for our team that handles customer complaints and manages support ticketsr>   Sales)r   r   r   	Marketingr   zHuman Resources   zInformation Technology   Finance   zCustomer Service   
Operations   zxCommunication-12(Intermediate), Active Listening-6(Intermediate), CRM Tools-5(Beginner), Problem Solving-8(Intermediate)u   📝 Input Data:z   User Prompt: z   Organization Departments: r   r   )r   r   r   r   )r   r    r   r   z8--------=-=-=-=-=-=---------------------================zO++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++=c                 C   s$   g | ]}|d  |d |d dqS )r=   rU   r"   )r=   rU   r"   r   r$  r   r   r   r     s    r   r   r   zoLearn the fundamentals of Python programming, including variables, loops, functions, and basic data structures.zqCovers the basics of data analysis, statistics, and visualization using tools like Pandas, NumPy, and Matplotlib.zhBuild and evaluate models using regression, classification, and clustering techniques with Scikit-Learn.zWExplore neural networks, CNNs, and RNNs using TensorFlow to build deep learning models.zgUnderstand text preprocessing, embeddings, and LLMs using libraries like NLTK, spaCy, and Hugging Face.zpMaster SQL queries, joins, subqueries, and window functions for effective data analysis on relational databases.z`Create and deploy AI-driven RESTful APIs using FastAPI and integrate them into web applications.z]Learn to build Retrieval-Augmented Generation pipelines using LangChain, FAISS, and ChromaDB.zaUnderstand core Azure services, AI tools, and deployment techniques for scalable AI applications.zeLearn graph theory concepts and build intelligent applications using Neo4j and Cypher query language.)
zPython for BeginnerszData Science Essentialsz"Machine Learning with Scikit-LearnzDeep Learning with TensorFlowzNatural Language ProcessingzSQL for Data Analysisz AI-Powered Web Apps with FastAPIzLangChain and RAG Pipelinesz Cloud Fundamentals with Azure AIzGraph Databases with Neo4jcourses_online_offline)(langchain_community.llmsr   langchain.promptsr   langchain.schemar   langchain.chainsr   langchain_ollamar   r&   r#   typingr   r	   r
   dataclassesr   r   r   r1   rm   r!  r  r/  r   r   
tna_systemr   r   r   r   r   r   r    r   result1r   course_availabler:  r   r   r   r   <module>   s    a     E/ 
L
	