diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..108964a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,33 @@ +{ + "name": "Python 3", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye", + "customizations": { + "codespaces": { + "openFiles": [ + "README.md", + "app_full.py" + ] + }, + "vscode": { + "settings": {}, + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance" + ] + } + }, + "updateContentCommand": "[ -f packages.txt ] && sudo apt update && sudo apt upgrade -y && sudo xargs apt install -y + Project Logo + + +LEAP is a web application that generates a personalized learning path based on your educational background, skills, and goals. The app uses an AI model to create a customized plan and allows you to download it as a `.docx` file. Built using Python, Streamlit, and the GROQ LLM API, the application generates comprehensive learning paths with key concepts, curated resources, and estimated completion timelines. Features include automated document generation for learning plans, and intelligent resource recommendations from trusted platforms. The tool helps users efficiently plan their learning journey by breaking down complex career transitions into structured, actionable steps with specific time estimates and progress tracking capabilities. # Features๐ŸŒŸ - Generates a customized learning path based on user input. @@ -8,6 +13,9 @@ This is a web application that generates a personalized learning path based on y - Allows users to download the learning path as a `.docx` file. - Securely handles sensitive information like API keys using environment variables. +# Streamlit App๐Ÿ”— +Link: https://leap-learning-path-generator.streamlit.app/ + # Prerequisitesโš™๏ธ Before running the application, ensure you have the following installed: @@ -19,7 +27,7 @@ Before running the application, ensure you have the following installed: # Setting Up the Environment # Step 1: Clone the Repository -git clone https://github.com/dasaribhumika/learning-path-generator +git clone https://github.com/gudashashank/LEAP cd learning-path-generator @@ -63,3 +71,5 @@ Fork the repository Create a new branch Submit a pull request Your contributions are always welcome! + +**Made with ๐Ÿ’™ by [Shashank Guda](https://shashankguda.me)** diff --git a/__pycache__/LEAP (Wide).webp b/__pycache__/LEAP (Wide).webp new file mode 100644 index 0000000..9bbfb94 Binary files /dev/null and b/__pycache__/LEAP (Wide).webp differ diff --git a/__pycache__/Leap Logo.png b/__pycache__/Leap Logo.png new file mode 100644 index 0000000..242e2eb Binary files /dev/null and b/__pycache__/Leap Logo.png differ diff --git a/app.py b/app.py deleted file mode 100644 index a9ae122..0000000 --- a/app.py +++ /dev/null @@ -1,188 +0,0 @@ -import streamlit as st -from chains import Chain -from streamlit_lottie import st_lottie -import requests -from docx import Document -import io - -# Initialize Chain class -chain = Chain() - -# Load Lottie animation -def load_lottieurl(url): - r = requests.get(url) - if r.status_code != 200: - return None - return r.json() - -# Lottie animation URL -lottie_url = "https://assets9.lottiefiles.com/packages/lf20_tfb3estd.json" -lottie_animation = load_lottieurl(lottie_url) - - -# Function to generate .docx from the learning path -def generate_docx(learning_path): - doc = Document() - doc.add_heading('Personalized Learning Path', 0) - - for course in learning_path.split('\n'): - doc.add_paragraph(course) - - # Save document to a BytesIO object - doc_io = io.BytesIO() - doc.save(doc_io) - doc_io.seek(0) - - return doc_io - -# Enhanced Custom CSS for full page styling and animation -st.markdown( - """ - - """, unsafe_allow_html=True -) - -# UI layout -st.title("๐Ÿ“˜ Personalized Learning Path Generator") -st.write("Enter your educational background, skills, and goals to generate a customized learning path.") - -# Place Lottie animation in the main content area -if lottie_animation: - st.markdown('
', unsafe_allow_html=True) - st_lottie(lottie_animation, height=200, key="motivation") - st.markdown('
', unsafe_allow_html=True) - -# User inputs with descriptions and placeholders -educational_background = st.text_area( - "๐Ÿ“˜ Educational Background", - "", # No default value, just an empty field - height=100, - placeholder="Enter details (e.g., degree, major, relevant courses):" -) -skills = st.text_area( - "๐Ÿ”ง Skills", - "", # No default value, just an empty field - height=100, - placeholder="Enter details (e.g., programming languages, technical skills, tools):" -) -goals = st.text_area( - "๐ŸŽฏ Goals", - "", # No default value, just an empty field - height=100, - placeholder="Enter details (e.g., career objectives, learning goals):" -) - -# Generate learning path button -if st.button("Generate Learning Path"): - if educational_background and skills and goals: - with st.spinner("Generating your personalized learning path..."): - try: - # Call the generate_learning_path method - learning_path = chain.generate_learning_path(educational_background, skills, goals) - - # Store learning path in session state - st.session_state.learning_path = learning_path - - # Display the learning path in a readable format - st.subheader("Your Customized Learning Path:") - - # Timeline-style display - st.markdown("
", unsafe_allow_html=True) - for i, module in enumerate(learning_path.split('\n\n'), 1): - st.write(f"{module}") - st.markdown("
", unsafe_allow_html=True) - - # Generate the .docx file - docx_file = generate_docx(learning_path) - - # Provide a download link - st.download_button( - label="Download Learning Path as .docx", - data=docx_file.getvalue(), - file_name="learning_path.docx", - mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document" - ) - except Exception as e: - st.error(f"An error occurred: {e}") - else: - st.warning("Please provide educational background, skills, and goals.") diff --git a/app_full.py b/app_full.py new file mode 100644 index 0000000..e8ae875 --- /dev/null +++ b/app_full.py @@ -0,0 +1,414 @@ +import os +import streamlit as st +from streamlit_lottie import st_lottie +import requests +from docx import Document +from docx.shared import Pt +import io +from langchain_groq import ChatGroq +from langchain_core.prompts import PromptTemplate +from langchain_core.exceptions import OutputParserException + +# Modified environment setup to work with both local and deployment +def get_api_key(): + # First try to get from Streamlit Secrets + try: + api_key = st.secrets["GROQ_API_KEY"] + except: + # If not found in Streamlit Secrets, try local .env file + api_key = 'Not Found' + + if api_key is None: + st.error("No API key found. Please set up your GROQ API key in the environment variables.") + st.stop() + + return api_key + +class Chain: + def __init__(self): + api_key = get_api_key() + self.llm = ChatGroq( + temperature=0, + groq_api_key=api_key, + model_name="llama-3.3-70b-versatile" + ) + + def generate_learning_path(self, educational_background, skills, goals): + prompt_path = PromptTemplate.from_template( + """ + ### EDUCATIONAL BACKGROUND: + {educational_background} + + ### SKILLS: + {skills} + + ### GOALS: + {goals} + + ### INSTRUCTION: + Based on the user's educational background, skills, and specific goals, generate a personalized learning path. For each learning topic, provide the following: + + 1. The topic name + 2. Key concepts that will be covered in this topic (5-7 main concepts) + 3. A list of recommended resources categorized as: + - Books: List 2-3 highly recommended books for the topic (title and author only) + - Courses: List 2-3 recommended courses or tutorials (include official URLs only for well-known platforms like Coursera, edX, Udemy, LinkedIn Learning) + - Resources: List 2-3 recommended websites or learning platforms (include official URLs only for well-known platforms) + 4. An estimated time for learning the topic + + IMPORTANT GUIDELINES: + - Only include URLs for official, well-known educational platforms and resources + - If you're not completely confident about a URL, provide just the resource name without the link + - For books, only provide title and author (no links) + - Each topic must include 5-7 key concepts that will be covered + + Structure your response in the following way: + - **Topic**: [name of the topic] + - **Estimated Time**: [time estimate] + - **Key Concepts**: + - [Concept 1] + - [Concept 2] + - [Concept 3] + - [Concept 4] + - [Concept 5] + - **Books**: + - [Book Title] by [Author] + - [Book Title] by [Author] + - **Courses**: + - [Course Title] - [Platform] ([URL if confident]) + - [Course Title] - [Platform] + - **Resources**: + - [Resource Name] ([URL if confident]) + - [Resource Name] + + Remember to maintain high confidence when including URLs and focus on major educational platforms only. + """ + ) + chain_path = prompt_path | self.llm + res = chain_path.invoke(input={"educational_background": educational_background, "skills": skills, "goals": goals}) + + try: + response = res.content + except OutputParserException: + raise OutputParserException("Context too large or output could not be parsed.") + + return response + +def load_lottieurl(url): + r = requests.get(url) + if r.status_code != 200: + return None + return r.json() + +def generate_docx(learning_path): + doc = Document() + doc.add_heading('Personalized Learning Path', 0) + doc.styles['Normal'].font.size = Pt(12) + + for line in learning_path.split('\n'): + if line.startswith("- **Topic**"): + topic = line.split(": ")[1] + doc.add_paragraph(topic, style="Heading 2") + elif line.startswith("- **Estimated Time**"): + time = line.split(": ")[1] + doc.add_paragraph(f"Estimated Time: {time}") + elif line.startswith("- **Key Concepts**"): + doc.add_paragraph("Key Concepts:", style="Heading 3") + elif line.startswith("- **Books**"): + doc.add_paragraph("Books:", style="Heading 3") + elif line.startswith("- **Courses**"): + doc.add_paragraph("Courses:", style="Heading 3") + elif line.startswith("- **Resources**"): + doc.add_paragraph("Resources:", style="Heading 3") + elif line.startswith(" - "): + text = line[4:] + # Create a paragraph with the link if it exists + if "(" in text and ")" in text and "http" in text: + name = text[:text.find("(")].strip() + url = text[text.find("(")+1:text.find(")")].strip() + p = doc.add_paragraph() + p.add_run(name + " - ") + p.add_run(url) + else: + doc.add_paragraph(text) + else: + doc.add_paragraph(line) + + doc_io = io.BytesIO() + doc.save(doc_io) + doc_io.seek(0) + return doc_io + +# Lottie animation URL +lottie_url = "https://assets2.lottiefiles.com/packages/lf20_DMgKk1.json" + +# Page config and title +st.set_page_config( + page_title="Learning Path Generator", + page_icon="๐Ÿ“˜", + layout="wide" +) + +# [Previous styling code remains the same] +st.markdown( + """ + + """, unsafe_allow_html=True +) + +# Sidebar with instructions +with st.sidebar: + st.markdown(""" + # ๐Ÿ“š How to Use + + Welcome to the Learning Path Generator! Follow these steps to get your personalized learning path: + + ### 1. Educational Background ๐Ÿ“˜ + * Enter your current education level + * Include your major/field of study + * List relevant courses you've taken + * Mention any certifications + + ### 2. Skills ๐Ÿ”ง + * List your technical skills + * Include programming languages + * Add tools you're familiar with + * Mention soft skills + + ### 3. Goals ๐ŸŽฏ + * Specify your career objectives + * Mention target roles/positions + * Include time frame for goals + * List specific areas you want to learn + + ### 4. Generate & Download ๐Ÿ“ฅ + * Click "Generate Learning Path" + * Review the customized path + * Download as a Word document + * Follow the suggested resources + + ### Tips for Best Results โœจ + * Be specific in your descriptions + * Include skill levels where relevant + * Clearly state your timeline + * Mention any preferences for learning style + + Need help? Contact: shguda@syr.edu (or) bdasari@syr.edu + """) + +# Main content +st.title("๐Ÿ“˜ LEAP - Learning Enhancement And Progression: Personalized Learning Path Generator") +st.write("Enter your educational background, skills, and goals to generate a customized learning path.") + +# Initialize Chain +chain = Chain() + +# Lottie animation display +lottie_animation = load_lottieurl(lottie_url) +if lottie_animation: + st.markdown('
', unsafe_allow_html=True) + st_lottie( + lottie_animation, + height=200, + key="motivation", + quality="high", + speed=1 + ) + st.markdown('
', unsafe_allow_html=True) + +# User inputs +educational_background = st.text_area("๐Ÿ“˜ Educational Background", "", height=100, placeholder="Enter details (e.g., degree, major, relevant courses):") +skills = st.text_area("๐Ÿ”ง Skills", "", height=100, placeholder="Enter details (e.g., programming languages, technical skills, tools):") +goals = st.text_area("๐ŸŽฏ Goals", "", height=100, placeholder="Enter details (e.g., career objectives, learning goals):") + +# Generate learning path button +if st.button("Generate Learning Path"): + if educational_background and skills and goals: + with st.spinner("Generating your personalized learning path..."): + try: + learning_path = chain.generate_learning_path(educational_background, skills, goals) + st.session_state.learning_path = learning_path + st.subheader("Your Customized Learning Path:") + st.markdown("
", unsafe_allow_html=True) + for module in learning_path.split('\n\n'): + st.write(module) + st.markdown("
", unsafe_allow_html=True) + docx_file = generate_docx(learning_path) + st.download_button( + label="Download Learning Path as .docx", + data=docx_file.getvalue(), + file_name="learning_path.docx", + mime="application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ) + except Exception as e: + st.error(f"An error occurred: {e}") + else: + st.warning("Please provide educational background, skills, and goals.") + +# Footer +st.markdown("", unsafe_allow_html=True) \ No newline at end of file diff --git a/chains.py b/chains.py deleted file mode 100644 index 2a69ba1..0000000 --- a/chains.py +++ /dev/null @@ -1,67 +0,0 @@ -import os -from langchain_groq import ChatGroq -from langchain_core.prompts import PromptTemplate -from langchain_core.output_parsers import JsonOutputParser -from langchain_core.exceptions import OutputParserException -from dotenv import load_dotenv - -load_dotenv() - -class Chain: - def __init__(self): - # Initialize ChatGroq with your API key and model - self.llm = ChatGroq(temperature=0, groq_api_key=os.getenv("GROQ_API_KEY"), model_name="llama-3.1-70b-versatile") - - def generate_learning_path(self, educational_background, skills, goals): - # Define a prompt for generating a learning path based on educational background and skills - prompt_path = PromptTemplate.from_template( - """ - ### EDUCATIONAL BACKGROUND: - {educational_background} - - ### SKILLS: - {skills} - - ### GOALS: - {goals} - - ### INSTRUCTION: - Based on the user's educational background, skills, and specific goals, generate a personalized learning path. For each learning topic, provide the following: - 1. The topic name. - 2. A list of recommended resources categorized as: - - **Books**: List a few books for the topic, ordered from beginner to advanced. - - **Courses**: List a few online courses or tutorials for the topic, ordered from beginner to advanced. - - **Blogs**: List a few blogs or articles for the topic, ordered from beginner to advanced. - 3. An estimated time for learning the topic. - - Please ensure that the output is aligned with the user's background and goals, and that resources are categorized properly. - - Structure your response in the following way: - - **Topic**: (name of the topic) - - **Estimated Time**: (time estimate) - - **Books**: - - [Book 1](URL) - - [Book 2](URL) - - **Courses**: - - [Course 1](URL) - - [Course 2](URL) - - **Blogs**: - - [Blog 1](URL) - - [Blog 2](URL) - """ - ) - # Execute the prompt with ChatGroq - chain_path = prompt_path | self.llm - res = chain_path.invoke(input={"educational_background": educational_background, "skills": skills, "goals": goals}) - - # Parse JSON response - try: - # Directly use the model response without needing to parse into JSON - response = res.content - except OutputParserException: - raise OutputParserException("Context too large or output could not be parsed.") - - return response - -if __name__ == "__main__": - print(os.getenv("GROQ_API_KEY")) diff --git a/requirements.txt b/requirements.txt index 317aa32..a922575 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -streamlit==1.20.0 -requests==2.28.2 -python-dotenv==1.0.0 -python-docx==0.8.11 \ No newline at end of file +streamlit +streamlit-lottie +python-docx +requests +langchain-groq