Spaces:
Sleeping
Sleeping
Mahdiyar
commited on
Commit
Β·
9a255bf
1
Parent(s):
1f38061
initial version
Browse files- README.md +95 -1
- hackathon_organizer/__init__.py β __init__.py +0 -0
- hackathon_organizer/app.py β app.py +0 -0
- app_simple.py +146 -0
- hackathon_organizer/database.py β database.py +0 -0
- generate_participants.py +0 -372
- hackathon_organizer/README.md +0 -95
- hackathon_organizer/hackathon_participants.db +0 -0
- hackathon_organizer/requirements.txt +0 -7
- hackathon_participants.db +0 -0
- main.py +0 -52
- hackathon_organizer/matching_agent.py β matching_agent.py +0 -0
- notebook.ipynb +112 -0
- product_manager/organizer.md +0 -37
- requirements.txt +2 -1
- {hackathon_organizer/tests β tests}/__init__.py +0 -0
- tests/test_database.py +101 -0
- tests/test_matching_agent.py +71 -0
README.md
CHANGED
|
@@ -11,4 +11,98 @@ license: apache-2.0
|
|
| 11 |
short_description: Instantly match hackathon participants into ideal teams!
|
| 12 |
---
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
short_description: Instantly match hackathon participants into ideal teams!
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# π€ AI-Powered HackBuddyAI
|
| 15 |
+
|
| 16 |
+
This project is a web application that helps hackathon organizers automatically form balanced teams from a list of participants. It uses a `TinyCodeAgent` to analyze participant skills and goals, creating optimal teams based on criteria defined by the organizer.
|
| 17 |
+
|
| 18 |
+
The application features a two-tab interface:
|
| 19 |
+
1. **Participant Registration**: A form for participants to submit their profile, including skills, background, and what they want to achieve during the hackathon.
|
| 20 |
+
2. **Organizer Dashboard**: A view for the organizer to see all registered participants, define matching criteria, and run the AI-powered team formation process.
|
| 21 |
+
|
| 22 |
+
## Tech Stack
|
| 23 |
+
- **Backend**: Python
|
| 24 |
+
- **AI Agent Framework**: `TinyCodeAgent`
|
| 25 |
+
- **Web UI**: Gradio
|
| 26 |
+
- **Data Handling**: Pandas
|
| 27 |
+
- **Database**: SQLite
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
## Setup and Installation
|
| 32 |
+
|
| 33 |
+
### 1. Prerequisites
|
| 34 |
+
- Python 3.8+
|
| 35 |
+
- An OpenAI API key
|
| 36 |
+
|
| 37 |
+
### 2. Installation
|
| 38 |
+
1. **Clone the repository** (if applicable) or ensure you have the project files in a directory.
|
| 39 |
+
|
| 40 |
+
2. **Navigate to the project directory**:
|
| 41 |
+
```bash
|
| 42 |
+
cd path/to/project
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
3. **Create a virtual environment** (recommended):
|
| 46 |
+
```bash
|
| 47 |
+
python -m venv venv
|
| 48 |
+
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
4. **Install dependencies**:
|
| 52 |
+
The project relies on the `tinyagent` library. Assuming it is available in your environment, install the other required packages:
|
| 53 |
+
```bash
|
| 54 |
+
pip install -r hackathon_organizer/requirements.txt
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
### 3. Configure Environment Variables
|
| 58 |
+
You must set your OpenAI API key as an environment variable. The application will not run without it.
|
| 59 |
+
|
| 60 |
+
- **On macOS/Linux**:
|
| 61 |
+
```bash
|
| 62 |
+
export OPENAI_API_KEY="your_api_key_here"
|
| 63 |
+
```
|
| 64 |
+
- **On Windows (Command Prompt)**:
|
| 65 |
+
```bash
|
| 66 |
+
set OPENAI_API_KEY="your_api_key_here"
|
| 67 |
+
```
|
| 68 |
+
|
| 69 |
+
---
|
| 70 |
+
|
| 71 |
+
## π Running the Application
|
| 72 |
+
|
| 73 |
+
Once the setup is complete, you can launch the Gradio web application using one of the following methods:
|
| 74 |
+
|
| 75 |
+
### Method 1: Using the main script (recommended)
|
| 76 |
+
|
| 77 |
+
```bash
|
| 78 |
+
python main.py
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
This script handles all the necessary path configurations and launches the application.
|
| 82 |
+
|
| 83 |
+
### Method 2: Running app.py directly
|
| 84 |
+
|
| 85 |
+
```bash
|
| 86 |
+
cd hackathon_organizer
|
| 87 |
+
python app.py
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
Either method will start a local web server, and you can access the application at the URL provided in the console (usually `http://127.0.0.1:7860`).
|
| 91 |
+
|
| 92 |
+
The application will create a `hackathon_participants.db` file in the working directory to store the participant data.
|
| 93 |
+
|
| 94 |
+
---
|
| 95 |
+
|
| 96 |
+
## π§ͺ Running Tests
|
| 97 |
+
|
| 98 |
+
The project includes unit tests for the database and the agent setup logic. The tests are located in the `tests/` directory.
|
| 99 |
+
|
| 100 |
+
To run the tests:
|
| 101 |
+
|
| 102 |
+
```bash
|
| 103 |
+
# From the project root directory
|
| 104 |
+
cd hackathon_organizer
|
| 105 |
+
python -m unittest discover tests
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
The tests are designed to run without needing an active internet connection or a valid API key. They use mocks to simulate agent behavior and an in-memory database for testing database operations.
|
hackathon_organizer/__init__.py β __init__.py
RENAMED
|
File without changes
|
hackathon_organizer/app.py β app.py
RENAMED
|
File without changes
|
app_simple.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import logging
|
| 4 |
+
import sys
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
from database import initialize_database, add_participant, get_participants_dataframe
|
| 8 |
+
|
| 9 |
+
# --- Logging Setup ---
|
| 10 |
+
logging.basicConfig(
|
| 11 |
+
level=logging.INFO,
|
| 12 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
| 13 |
+
handlers=[logging.StreamHandler(sys.stdout)]
|
| 14 |
+
)
|
| 15 |
+
logger = logging.getLogger('app_simple')
|
| 16 |
+
|
| 17 |
+
# --- Initial Setup ---
|
| 18 |
+
logger.info("Initializing database...")
|
| 19 |
+
initialize_database()
|
| 20 |
+
|
| 21 |
+
# --- Gradio UI Functions ---
|
| 22 |
+
|
| 23 |
+
def register_participant(name, email, linkedin, background, goals):
|
| 24 |
+
"""Callback function to register a new participant."""
|
| 25 |
+
if not all([name, email]):
|
| 26 |
+
return "Please provide at least a name and email.", get_participants_dataframe()
|
| 27 |
+
|
| 28 |
+
participant_data = {
|
| 29 |
+
"name": name,
|
| 30 |
+
"email": email,
|
| 31 |
+
"linkedin_profile": linkedin,
|
| 32 |
+
"background": background,
|
| 33 |
+
"goals": goals
|
| 34 |
+
}
|
| 35 |
+
try:
|
| 36 |
+
add_participant(participant_data)
|
| 37 |
+
feedback = f"β
Success! Participant '{name}' registered."
|
| 38 |
+
logger.info(f"Registered new participant: {email}")
|
| 39 |
+
except Exception as e:
|
| 40 |
+
feedback = f"β Error! Could not register participant. Reason: {e}"
|
| 41 |
+
logger.error(f"Failed to register participant {email}: {e}")
|
| 42 |
+
|
| 43 |
+
return feedback, get_participants_dataframe()
|
| 44 |
+
|
| 45 |
+
def refresh_participants_list():
|
| 46 |
+
"""Callback to reload the participant data from the database."""
|
| 47 |
+
return get_participants_dataframe()
|
| 48 |
+
|
| 49 |
+
def mock_matching_process(organizer_criteria):
|
| 50 |
+
"""Mock function for the matching process (without using TinyCodeAgent)."""
|
| 51 |
+
participants_df = get_participants_dataframe()
|
| 52 |
+
if len(participants_df) < 2:
|
| 53 |
+
logger.warning("Matching process aborted: not enough participants.")
|
| 54 |
+
return "Cannot run matching with fewer than 2 participants."
|
| 55 |
+
|
| 56 |
+
# Create a simple mock output
|
| 57 |
+
result = f"""
|
| 58 |
+
## Team Matching Results
|
| 59 |
+
|
| 60 |
+
**Criteria used**: {organizer_criteria}
|
| 61 |
+
|
| 62 |
+
### Team 1
|
| 63 |
+
* **{participants_df['name'].iloc[0] if len(participants_df) > 0 else 'No participants'}**
|
| 64 |
+
* **{participants_df['name'].iloc[1] if len(participants_df) > 1 else 'No participants'}**
|
| 65 |
+
|
| 66 |
+
**Justification**: This is a mock team created for demonstration purposes.
|
| 67 |
+
|
| 68 |
+
### Team 2
|
| 69 |
+
* **{participants_df['name'].iloc[2] if len(participants_df) > 2 else 'No participants'}**
|
| 70 |
+
* **{participants_df['name'].iloc[3] if len(participants_df) > 3 else 'No participants'}**
|
| 71 |
+
|
| 72 |
+
**Justification**: This is another mock team created for demonstration purposes.
|
| 73 |
+
|
| 74 |
+
*Note: This is a simplified version without the AI matching. The full version would use TinyCodeAgent to create optimized teams.*
|
| 75 |
+
"""
|
| 76 |
+
return result
|
| 77 |
+
|
| 78 |
+
# --- Gradio App Definition ---
|
| 79 |
+
|
| 80 |
+
with gr.Blocks(theme=gr.themes.Soft(), title="HackBuddyAI (Simple)") as app:
|
| 81 |
+
gr.Markdown("# π€ HackBuddyAI (Simple Version)")
|
| 82 |
+
gr.Markdown("*This is a simplified version without the AI matching component.*")
|
| 83 |
+
|
| 84 |
+
with gr.Tabs():
|
| 85 |
+
with gr.TabItem("π€ Participant Registration"):
|
| 86 |
+
gr.Markdown("## Welcome, Participant!")
|
| 87 |
+
gr.Markdown("Fill out the form below to register for the hackathon.")
|
| 88 |
+
with gr.Row():
|
| 89 |
+
with gr.Column():
|
| 90 |
+
name_in = gr.Textbox(label="Full Name")
|
| 91 |
+
email_in = gr.Textbox(label="Email Address")
|
| 92 |
+
linkedin_in = gr.Textbox(label="LinkedIn Profile URL", placeholder="Optional")
|
| 93 |
+
with gr.Column():
|
| 94 |
+
background_in = gr.Textbox(label="Your Background & Skills", lines=5, placeholder="e.g., Python developer with 3 years of experience, specializing in Django and REST APIs...")
|
| 95 |
+
goals_in = gr.Textbox(label="Your Goals for this Hackathon", lines=5, placeholder="e.g., I want to learn about machine learning and work on a cool data visualization project...")
|
| 96 |
+
|
| 97 |
+
submit_button = gr.Button("Register", variant="primary")
|
| 98 |
+
registration_feedback = gr.Markdown()
|
| 99 |
+
|
| 100 |
+
with gr.TabItem("π Organizer Dashboard"):
|
| 101 |
+
gr.Markdown("## Welcome, Organizer!")
|
| 102 |
+
gr.Markdown("Here you can view registered participants and run the team matching process.")
|
| 103 |
+
|
| 104 |
+
with gr.Accordion("View Registered Participants", open=False):
|
| 105 |
+
refresh_button = gr.Button("π Refresh List")
|
| 106 |
+
participants_df_out = gr.DataFrame(value=get_participants_dataframe, interactive=False)
|
| 107 |
+
|
| 108 |
+
gr.Markdown("### Run Matching")
|
| 109 |
+
organizer_criteria_in = gr.Textbox(
|
| 110 |
+
label="Matching Criteria",
|
| 111 |
+
lines=4,
|
| 112 |
+
value="Create teams of 3. Try to balance skills in each team (e.g., frontend, backend, data).",
|
| 113 |
+
placeholder="Describe your ideal team composition..."
|
| 114 |
+
)
|
| 115 |
+
run_button = gr.Button("π Run Matching", variant="primary")
|
| 116 |
+
|
| 117 |
+
gr.Markdown("### π€ Matched Teams")
|
| 118 |
+
matching_results_out = gr.Markdown("Matching has not been run yet.")
|
| 119 |
+
|
| 120 |
+
# --- Event Handlers ---
|
| 121 |
+
submit_button.click(
|
| 122 |
+
fn=register_participant,
|
| 123 |
+
inputs=[name_in, email_in, linkedin_in, background_in, goals_in],
|
| 124 |
+
outputs=[registration_feedback, participants_df_out]
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
refresh_button.click(
|
| 128 |
+
fn=refresh_participants_list,
|
| 129 |
+
inputs=[],
|
| 130 |
+
outputs=[participants_df_out]
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
run_button.click(
|
| 134 |
+
fn=mock_matching_process,
|
| 135 |
+
inputs=[organizer_criteria_in],
|
| 136 |
+
outputs=[matching_results_out]
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
# --- Launching the App ---
|
| 140 |
+
if __name__ == "__main__":
|
| 141 |
+
try:
|
| 142 |
+
logger.info("Launching Gradio app (simple version)...")
|
| 143 |
+
# queue() is important for handling multiple users
|
| 144 |
+
app.queue().launch(share=False)
|
| 145 |
+
except KeyboardInterrupt:
|
| 146 |
+
logger.info("Gradio app shutting down.")
|
hackathon_organizer/database.py β database.py
RENAMED
|
File without changes
|
generate_participants.py
DELETED
|
@@ -1,372 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Generate Synthetic Hackathon Participants
|
| 4 |
-
|
| 5 |
-
This script creates 100 diverse hackathon participants and saves them to the database.
|
| 6 |
-
The participants represent various personas that might attend a hackathon focused on
|
| 7 |
-
"Connected Experiences" and AI agents.
|
| 8 |
-
"""
|
| 9 |
-
import random
|
| 10 |
-
import sys
|
| 11 |
-
import os
|
| 12 |
-
from faker import Faker
|
| 13 |
-
from hackathon_organizer.database import initialize_database, add_participant, get_participants_dataframe
|
| 14 |
-
|
| 15 |
-
# Initialize Faker for generating realistic names and emails
|
| 16 |
-
fake = Faker()
|
| 17 |
-
|
| 18 |
-
# Define various participant personas and attributes
|
| 19 |
-
TECHNICAL_BACKGROUNDS = [
|
| 20 |
-
# Developers
|
| 21 |
-
"Full-stack developer with {years} years of experience in {stack}. {additional}",
|
| 22 |
-
"Backend engineer specializing in {backend_tech}. {additional}",
|
| 23 |
-
"Frontend developer focused on {frontend_tech}. {additional}",
|
| 24 |
-
"Mobile developer with expertise in {mobile_tech}. {additional}",
|
| 25 |
-
"DevOps engineer with experience in {devops_tech}. {additional}",
|
| 26 |
-
"Data scientist working with {ds_tech}. {additional}",
|
| 27 |
-
"Machine learning engineer focused on {ml_tech}. {additional}",
|
| 28 |
-
"AI researcher specializing in {ai_tech}. {additional}",
|
| 29 |
-
"Game developer using {game_tech}. {additional}",
|
| 30 |
-
"Embedded systems engineer working with {embedded_tech}. {additional}",
|
| 31 |
-
|
| 32 |
-
# Technical but not coding-focused
|
| 33 |
-
"UX/UI designer with {years} years of experience. {additional}",
|
| 34 |
-
"Product manager for {product_type} products. {additional}",
|
| 35 |
-
"QA engineer with expertise in {qa_tech}. {additional}",
|
| 36 |
-
"Technical writer specializing in {writing_focus}. {additional}",
|
| 37 |
-
"Solution architect with background in {arch_focus}. {additional}",
|
| 38 |
-
]
|
| 39 |
-
|
| 40 |
-
NON_TECHNICAL_BACKGROUNDS = [
|
| 41 |
-
"Marketing professional with experience in {marketing_focus}. {additional}",
|
| 42 |
-
"Business development specialist in the {industry} industry. {additional}",
|
| 43 |
-
"Project manager with {years} years of experience in {pm_focus}. {additional}",
|
| 44 |
-
"Entrepreneur and founder of a {startup_type} startup. {additional}",
|
| 45 |
-
"Student studying {field} at {university}. {additional}",
|
| 46 |
-
"Design thinking facilitator and innovation consultant. {additional}",
|
| 47 |
-
"Content creator focusing on {content_focus}. {additional}",
|
| 48 |
-
"Sales professional in the {sales_industry} sector. {additional}",
|
| 49 |
-
"HR specialist interested in tech talent and culture. {additional}",
|
| 50 |
-
"Non-profit professional looking to leverage technology for social impact. {additional}",
|
| 51 |
-
]
|
| 52 |
-
|
| 53 |
-
GOALS = [
|
| 54 |
-
# Learning-focused
|
| 55 |
-
"I want to learn about AI and how it can enhance user experiences at events.",
|
| 56 |
-
"I'm here to understand how to build AI agents and apply them to real-world problems.",
|
| 57 |
-
"I hope to gain practical experience with AI technologies and expand my technical skills.",
|
| 58 |
-
"I want to learn from experienced developers and improve my coding abilities.",
|
| 59 |
-
"I'm looking to understand how AI can create more meaningful human connections.",
|
| 60 |
-
|
| 61 |
-
# Project-focused
|
| 62 |
-
"I want to build a prototype that demonstrates the power of AI in connecting people.",
|
| 63 |
-
"I'm hoping to create an innovative solution that addresses the challenges of virtual events.",
|
| 64 |
-
"My goal is to develop an AI agent that enhances real-world social interactions.",
|
| 65 |
-
"I want to build something impressive for my portfolio that showcases my skills.",
|
| 66 |
-
"I'm aiming to create a practical tool that event organizers can actually use.",
|
| 67 |
-
|
| 68 |
-
# Networking-focused
|
| 69 |
-
"I'm primarily here to network with other professionals in the AI and event space.",
|
| 70 |
-
"I want to meet potential co-founders for a startup idea I've been developing.",
|
| 71 |
-
"I'm looking to connect with mentors who can guide my career in tech.",
|
| 72 |
-
"I hope to find collaborators for future projects beyond this hackathon.",
|
| 73 |
-
"I want to expand my professional network in the Toronto tech community.",
|
| 74 |
-
|
| 75 |
-
# Career-focused
|
| 76 |
-
"I'm exploring career opportunities in AI development and looking to showcase my skills.",
|
| 77 |
-
"I want to transition from my current role to a more tech-focused position.",
|
| 78 |
-
"I'm hoping this experience will help me land a job at an innovative tech company.",
|
| 79 |
-
"I want to demonstrate my abilities to potential employers or clients.",
|
| 80 |
-
"I'm building skills that will help me advance in my current organization.",
|
| 81 |
-
|
| 82 |
-
# Fun/Experience-focused
|
| 83 |
-
"I'm here for the creative experience and the thrill of building something in 24 hours.",
|
| 84 |
-
"I want to have fun while challenging myself technically.",
|
| 85 |
-
"I'm curious about hackathons and wanted to experience one firsthand.",
|
| 86 |
-
"I enjoy the collaborative atmosphere of hackathons and the energy they generate.",
|
| 87 |
-
"I'm looking for a break from my routine and a chance to work on something different.",
|
| 88 |
-
]
|
| 89 |
-
|
| 90 |
-
# Technical stack components
|
| 91 |
-
STACK_COMPONENTS = {
|
| 92 |
-
"years": [str(i) for i in range(1, 16)],
|
| 93 |
-
"stack": [
|
| 94 |
-
"JavaScript/TypeScript and Python", "MERN stack", "MEAN stack", "Ruby on Rails",
|
| 95 |
-
"Django and React", "Vue.js and Node.js", "PHP and Laravel", "Java Spring Boot",
|
| 96 |
-
".NET and Angular", "Go and React", "Python Flask and Vue.js"
|
| 97 |
-
],
|
| 98 |
-
"backend_tech": [
|
| 99 |
-
"Node.js and Express", "Django and PostgreSQL", "Ruby on Rails", "Java Spring Boot",
|
| 100 |
-
"ASP.NET Core", "PHP and Laravel", "Go microservices", "Python FastAPI",
|
| 101 |
-
"GraphQL APIs", "Serverless architectures on AWS"
|
| 102 |
-
],
|
| 103 |
-
"frontend_tech": [
|
| 104 |
-
"React and Redux", "Angular and RxJS", "Vue.js and Vuex", "Svelte and SvelteKit",
|
| 105 |
-
"Next.js", "Gatsby", "React Native", "Flutter", "TypeScript and Material UI",
|
| 106 |
-
"Tailwind CSS and Alpine.js"
|
| 107 |
-
],
|
| 108 |
-
"mobile_tech": [
|
| 109 |
-
"React Native", "Flutter", "Swift for iOS", "Kotlin for Android",
|
| 110 |
-
"Xamarin", "Ionic", "PWAs", "Unity for mobile games", "NativeScript",
|
| 111 |
-
"Mobile AR/VR applications"
|
| 112 |
-
],
|
| 113 |
-
"devops_tech": [
|
| 114 |
-
"Kubernetes and Docker", "AWS infrastructure", "Azure DevOps", "Google Cloud Platform",
|
| 115 |
-
"CI/CD pipelines", "Terraform and infrastructure as code", "Jenkins and GitLab CI",
|
| 116 |
-
"Monitoring and observability tools", "Site Reliability Engineering practices",
|
| 117 |
-
"Security automation"
|
| 118 |
-
],
|
| 119 |
-
"ds_tech": [
|
| 120 |
-
"Python, Pandas, and scikit-learn", "R and Tidyverse", "SQL and data warehousing",
|
| 121 |
-
"Tableau and data visualization", "Big data technologies like Spark",
|
| 122 |
-
"ETL pipelines", "Statistical analysis", "A/B testing methodologies",
|
| 123 |
-
"Natural Language Processing", "Computer Vision"
|
| 124 |
-
],
|
| 125 |
-
"ml_tech": [
|
| 126 |
-
"TensorFlow and Keras", "PyTorch", "scikit-learn", "deep learning models",
|
| 127 |
-
"MLOps and model deployment", "reinforcement learning", "computer vision algorithms",
|
| 128 |
-
"NLP models", "recommendation systems", "time series forecasting"
|
| 129 |
-
],
|
| 130 |
-
"ai_tech": [
|
| 131 |
-
"large language models", "generative AI", "conversational agents", "computer vision systems",
|
| 132 |
-
"reinforcement learning", "multimodal AI", "AI ethics and responsible AI",
|
| 133 |
-
"autonomous systems", "AI for social good", "explainable AI"
|
| 134 |
-
],
|
| 135 |
-
"game_tech": [
|
| 136 |
-
"Unity", "Unreal Engine", "Godot", "WebGL", "AR/VR development",
|
| 137 |
-
"mobile game development", "game AI", "procedural generation",
|
| 138 |
-
"multiplayer networking", "game physics"
|
| 139 |
-
],
|
| 140 |
-
"embedded_tech": [
|
| 141 |
-
"Arduino", "Raspberry Pi", "IoT devices", "embedded Linux",
|
| 142 |
-
"RTOS", "C/C++ for microcontrollers", "sensor networks",
|
| 143 |
-
"firmware development", "hardware interfaces", "low-power systems"
|
| 144 |
-
],
|
| 145 |
-
"product_type": [
|
| 146 |
-
"SaaS", "mobile", "enterprise", "consumer", "AI-powered",
|
| 147 |
-
"IoT", "fintech", "healthtech", "edtech", "e-commerce"
|
| 148 |
-
],
|
| 149 |
-
"qa_tech": [
|
| 150 |
-
"automated testing", "Selenium and Cypress", "performance testing",
|
| 151 |
-
"security testing", "mobile app testing", "API testing",
|
| 152 |
-
"test-driven development", "behavior-driven development",
|
| 153 |
-
"continuous integration testing", "accessibility testing"
|
| 154 |
-
],
|
| 155 |
-
"writing_focus": [
|
| 156 |
-
"API documentation", "user guides", "developer tutorials",
|
| 157 |
-
"knowledge bases", "technical blogs", "software requirements",
|
| 158 |
-
"open source documentation", "technical specifications",
|
| 159 |
-
"UX writing", "compliance documentation"
|
| 160 |
-
],
|
| 161 |
-
"arch_focus": [
|
| 162 |
-
"cloud architectures", "microservices", "serverless",
|
| 163 |
-
"enterprise systems", "distributed systems", "API design",
|
| 164 |
-
"security architectures", "data platforms", "IoT systems",
|
| 165 |
-
"mobile and web applications"
|
| 166 |
-
],
|
| 167 |
-
"additional": [
|
| 168 |
-
"I enjoy working in collaborative environments.",
|
| 169 |
-
"I'm passionate about creating accessible technology.",
|
| 170 |
-
"I've contributed to several open source projects.",
|
| 171 |
-
"I'm interested in ethical technology and responsible innovation.",
|
| 172 |
-
"I enjoy mentoring junior developers.",
|
| 173 |
-
"I have a background in design thinking.",
|
| 174 |
-
"I've worked in startups and enterprise environments.",
|
| 175 |
-
"I'm particularly interested in AI ethics.",
|
| 176 |
-
"I love solving complex algorithmic problems.",
|
| 177 |
-
"I focus on creating user-centered solutions.",
|
| 178 |
-
"I have experience leading small technical teams.",
|
| 179 |
-
"I'm self-taught and constantly learning new technologies.",
|
| 180 |
-
"I have a computer science degree but learned most of my skills on the job.",
|
| 181 |
-
"I'm currently transitioning careers into tech.",
|
| 182 |
-
"I'm an advocate for diversity in tech.",
|
| 183 |
-
"I've organized tech meetups and community events.",
|
| 184 |
-
"I'm interested in the intersection of technology and sustainability.",
|
| 185 |
-
"I have experience in both technical and business roles.",
|
| 186 |
-
"I'm passionate about making technology more accessible to everyone.",
|
| 187 |
-
"I enjoy the challenges of working with legacy systems.",
|
| 188 |
-
"", # Empty for some participants
|
| 189 |
-
]
|
| 190 |
-
}
|
| 191 |
-
|
| 192 |
-
# Non-technical components
|
| 193 |
-
NON_TECH_COMPONENTS = {
|
| 194 |
-
"marketing_focus": [
|
| 195 |
-
"digital marketing", "content strategy", "brand development",
|
| 196 |
-
"social media campaigns", "event promotion", "growth hacking",
|
| 197 |
-
"community building", "influencer partnerships", "SEO/SEM",
|
| 198 |
-
"product marketing"
|
| 199 |
-
],
|
| 200 |
-
"industry": [
|
| 201 |
-
"technology", "healthcare", "finance", "education", "retail",
|
| 202 |
-
"entertainment", "manufacturing", "non-profit", "government",
|
| 203 |
-
"hospitality"
|
| 204 |
-
],
|
| 205 |
-
"years": [str(i) for i in range(1, 16)],
|
| 206 |
-
"pm_focus": [
|
| 207 |
-
"agile methodologies", "waterfall approaches", "hybrid frameworks",
|
| 208 |
-
"technical projects", "creative initiatives", "product launches",
|
| 209 |
-
"organizational change", "international teams", "startup environments",
|
| 210 |
-
"enterprise transformations"
|
| 211 |
-
],
|
| 212 |
-
"startup_type": [
|
| 213 |
-
"tech", "social impact", "e-commerce", "healthcare", "education",
|
| 214 |
-
"fintech", "sustainability", "B2B SaaS", "consumer app", "AI/ML"
|
| 215 |
-
],
|
| 216 |
-
"field": [
|
| 217 |
-
"Computer Science", "Business Administration", "Design", "Marketing",
|
| 218 |
-
"Engineering", "Data Science", "Psychology", "Communications",
|
| 219 |
-
"Information Technology", "Entrepreneurship"
|
| 220 |
-
],
|
| 221 |
-
"university": [
|
| 222 |
-
"University of Toronto", "York University", "Ryerson University",
|
| 223 |
-
"Seneca College", "Humber College", "OCAD University",
|
| 224 |
-
"George Brown College", "McMaster University", "Waterloo University",
|
| 225 |
-
"Queen's University"
|
| 226 |
-
],
|
| 227 |
-
"content_focus": [
|
| 228 |
-
"tech tutorials", "industry trends", "career development",
|
| 229 |
-
"product reviews", "educational content", "lifestyle and tech",
|
| 230 |
-
"startup stories", "coding challenges", "design inspiration",
|
| 231 |
-
"thought leadership"
|
| 232 |
-
],
|
| 233 |
-
"sales_industry": [
|
| 234 |
-
"SaaS", "hardware", "consulting services", "enterprise solutions",
|
| 235 |
-
"consumer tech", "B2B technology", "telecommunications",
|
| 236 |
-
"cybersecurity", "cloud services", "digital transformation"
|
| 237 |
-
],
|
| 238 |
-
"additional": [
|
| 239 |
-
"I'm excited to learn more about technology and how it can solve real problems.",
|
| 240 |
-
"I bring a unique perspective from my non-technical background.",
|
| 241 |
-
"I'm interested in the human aspects of technology.",
|
| 242 |
-
"I'm looking to collaborate with technical team members and contribute my skills.",
|
| 243 |
-
"I have strong communication and presentation skills.",
|
| 244 |
-
"I excel at understanding user needs and translating them into requirements.",
|
| 245 |
-
"I'm good at explaining complex concepts to diverse audiences.",
|
| 246 |
-
"I have experience managing stakeholder expectations.",
|
| 247 |
-
"I'm skilled at identifying market opportunities.",
|
| 248 |
-
"I enjoy bridging the gap between technical and non-technical teams.",
|
| 249 |
-
"I have a creative approach to problem-solving.",
|
| 250 |
-
"I'm passionate about user experience and accessibility.",
|
| 251 |
-
"I have a network of industry connections that could be valuable.",
|
| 252 |
-
"I'm experienced in gathering and synthesizing user feedback.",
|
| 253 |
-
"I'm interested in how technology can create social impact.",
|
| 254 |
-
"I have experience in project coordination and team organization.",
|
| 255 |
-
"I'm good at creating compelling narratives around technical products.",
|
| 256 |
-
"I'm curious about AI and its potential applications.",
|
| 257 |
-
"I have a background in psychology and understand human behavior.",
|
| 258 |
-
"I'm skilled at facilitating workshops and brainstorming sessions.",
|
| 259 |
-
"", # Empty for some participants
|
| 260 |
-
]
|
| 261 |
-
}
|
| 262 |
-
|
| 263 |
-
def generate_background(is_technical=True):
|
| 264 |
-
"""Generate a realistic background for a participant."""
|
| 265 |
-
if is_technical:
|
| 266 |
-
template = random.choice(TECHNICAL_BACKGROUNDS)
|
| 267 |
-
components = STACK_COMPONENTS
|
| 268 |
-
else:
|
| 269 |
-
template = random.choice(NON_TECHNICAL_BACKGROUNDS)
|
| 270 |
-
components = NON_TECH_COMPONENTS
|
| 271 |
-
|
| 272 |
-
# Fill in the template with random components
|
| 273 |
-
for key in components:
|
| 274 |
-
if "{" + key + "}" in template:
|
| 275 |
-
template = template.replace("{" + key + "}", random.choice(components[key]))
|
| 276 |
-
|
| 277 |
-
return template
|
| 278 |
-
|
| 279 |
-
def generate_linkedin_profile(name):
|
| 280 |
-
"""Generate a realistic LinkedIn profile URL based on the name."""
|
| 281 |
-
# Remove spaces and special characters, convert to lowercase
|
| 282 |
-
name_part = ''.join(c for c in name if c.isalnum()).lower()
|
| 283 |
-
|
| 284 |
-
# Add some randomness to ensure uniqueness
|
| 285 |
-
if random.random() < 0.3:
|
| 286 |
-
# Some people use just their name
|
| 287 |
-
profile = name_part
|
| 288 |
-
elif random.random() < 0.6:
|
| 289 |
-
# Some add a random number
|
| 290 |
-
profile = f"{name_part}{random.randint(1, 999)}"
|
| 291 |
-
else:
|
| 292 |
-
# Some add their profession or location
|
| 293 |
-
suffixes = ["dev", "tech", "to", "canada", "design", "pm", "product", "marketing", "ai"]
|
| 294 |
-
profile = f"{name_part}-{random.choice(suffixes)}"
|
| 295 |
-
|
| 296 |
-
return f"linkedin.com/in/{profile}"
|
| 297 |
-
|
| 298 |
-
def generate_participants(count=100):
|
| 299 |
-
"""Generate a specified number of diverse hackathon participants."""
|
| 300 |
-
participants = []
|
| 301 |
-
|
| 302 |
-
# Define the distribution of technical vs non-technical participants
|
| 303 |
-
# For a hackathon, we'll have more technical participants but still a good mix
|
| 304 |
-
technical_count = int(count * 0.7) # 70% technical
|
| 305 |
-
non_technical_count = count - technical_count # 30% non-technical
|
| 306 |
-
|
| 307 |
-
# Generate technical participants
|
| 308 |
-
for _ in range(technical_count):
|
| 309 |
-
name = fake.name()
|
| 310 |
-
email = fake.email()
|
| 311 |
-
linkedin = generate_linkedin_profile(name)
|
| 312 |
-
background = generate_background(is_technical=True)
|
| 313 |
-
goals = random.choice(GOALS)
|
| 314 |
-
|
| 315 |
-
participants.append({
|
| 316 |
-
"email": email,
|
| 317 |
-
"name": name,
|
| 318 |
-
"linkedin_profile": linkedin,
|
| 319 |
-
"background": background,
|
| 320 |
-
"goals": goals
|
| 321 |
-
})
|
| 322 |
-
|
| 323 |
-
# Generate non-technical participants
|
| 324 |
-
for _ in range(non_technical_count):
|
| 325 |
-
name = fake.name()
|
| 326 |
-
email = fake.email()
|
| 327 |
-
linkedin = generate_linkedin_profile(name)
|
| 328 |
-
background = generate_background(is_technical=False)
|
| 329 |
-
goals = random.choice(GOALS)
|
| 330 |
-
|
| 331 |
-
participants.append({
|
| 332 |
-
"email": email,
|
| 333 |
-
"name": name,
|
| 334 |
-
"linkedin_profile": linkedin,
|
| 335 |
-
"background": background,
|
| 336 |
-
"goals": goals
|
| 337 |
-
})
|
| 338 |
-
|
| 339 |
-
# Shuffle the participants to mix technical and non-technical
|
| 340 |
-
random.shuffle(participants)
|
| 341 |
-
return participants
|
| 342 |
-
|
| 343 |
-
def main():
|
| 344 |
-
"""Main function to generate participants and save them to the database."""
|
| 345 |
-
print("Initializing database...")
|
| 346 |
-
initialize_database()
|
| 347 |
-
|
| 348 |
-
print("Generating 100 diverse hackathon participants...")
|
| 349 |
-
participants = generate_participants(100)
|
| 350 |
-
|
| 351 |
-
print("Adding participants to the database...")
|
| 352 |
-
for p in participants:
|
| 353 |
-
add_participant(p)
|
| 354 |
-
|
| 355 |
-
print("Participants added successfully.")
|
| 356 |
-
|
| 357 |
-
# Get and display a sample of the participants
|
| 358 |
-
df = get_participants_dataframe()
|
| 359 |
-
print(f"\nTotal participants in database: {len(df)}")
|
| 360 |
-
print("\nSample of participants:")
|
| 361 |
-
print(df.sample(5))
|
| 362 |
-
|
| 363 |
-
if __name__ == "__main__":
|
| 364 |
-
# Check if Faker is installed
|
| 365 |
-
try:
|
| 366 |
-
import faker
|
| 367 |
-
except ImportError:
|
| 368 |
-
print("The 'faker' package is required but not installed.")
|
| 369 |
-
print("Please install it using: pip install faker")
|
| 370 |
-
sys.exit(1)
|
| 371 |
-
|
| 372 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hackathon_organizer/README.md
DELETED
|
@@ -1,95 +0,0 @@
|
|
| 1 |
-
# π€ AI-Powered HackBuddyAI
|
| 2 |
-
|
| 3 |
-
This project is a web application that helps hackathon organizers automatically form balanced teams from a list of participants. It uses a `TinyCodeAgent` to analyze participant skills and goals, creating optimal teams based on criteria defined by the organizer.
|
| 4 |
-
|
| 5 |
-
The application features a two-tab interface:
|
| 6 |
-
1. **Participant Registration**: A form for participants to submit their profile, including skills, background, and what they want to achieve during the hackathon.
|
| 7 |
-
2. **Organizer Dashboard**: A view for the organizer to see all registered participants, define matching criteria, and run the AI-powered team formation process.
|
| 8 |
-
|
| 9 |
-
## Tech Stack
|
| 10 |
-
- **Backend**: Python
|
| 11 |
-
- **AI Agent Framework**: `TinyCodeAgent`
|
| 12 |
-
- **Web UI**: Gradio
|
| 13 |
-
- **Data Handling**: Pandas
|
| 14 |
-
- **Database**: SQLite
|
| 15 |
-
|
| 16 |
-
---
|
| 17 |
-
|
| 18 |
-
## Setup and Installation
|
| 19 |
-
|
| 20 |
-
### 1. Prerequisites
|
| 21 |
-
- Python 3.8+
|
| 22 |
-
- An OpenAI API key
|
| 23 |
-
|
| 24 |
-
### 2. Installation
|
| 25 |
-
1. **Clone the repository** (if applicable) or ensure you have the project files in a directory.
|
| 26 |
-
|
| 27 |
-
2. **Navigate to the project directory**:
|
| 28 |
-
```bash
|
| 29 |
-
cd path/to/project
|
| 30 |
-
```
|
| 31 |
-
|
| 32 |
-
3. **Create a virtual environment** (recommended):
|
| 33 |
-
```bash
|
| 34 |
-
python -m venv venv
|
| 35 |
-
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
|
| 36 |
-
```
|
| 37 |
-
|
| 38 |
-
4. **Install dependencies**:
|
| 39 |
-
The project relies on the `tinyagent` library. Assuming it is available in your environment, install the other required packages:
|
| 40 |
-
```bash
|
| 41 |
-
pip install -r hackathon_organizer/requirements.txt
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
### 3. Configure Environment Variables
|
| 45 |
-
You must set your OpenAI API key as an environment variable. The application will not run without it.
|
| 46 |
-
|
| 47 |
-
- **On macOS/Linux**:
|
| 48 |
-
```bash
|
| 49 |
-
export OPENAI_API_KEY="your_api_key_here"
|
| 50 |
-
```
|
| 51 |
-
- **On Windows (Command Prompt)**:
|
| 52 |
-
```bash
|
| 53 |
-
set OPENAI_API_KEY="your_api_key_here"
|
| 54 |
-
```
|
| 55 |
-
|
| 56 |
-
---
|
| 57 |
-
|
| 58 |
-
## π Running the Application
|
| 59 |
-
|
| 60 |
-
Once the setup is complete, you can launch the Gradio web application using one of the following methods:
|
| 61 |
-
|
| 62 |
-
### Method 1: Using the main script (recommended)
|
| 63 |
-
|
| 64 |
-
```bash
|
| 65 |
-
python main.py
|
| 66 |
-
```
|
| 67 |
-
|
| 68 |
-
This script handles all the necessary path configurations and launches the application.
|
| 69 |
-
|
| 70 |
-
### Method 2: Running app.py directly
|
| 71 |
-
|
| 72 |
-
```bash
|
| 73 |
-
cd hackathon_organizer
|
| 74 |
-
python app.py
|
| 75 |
-
```
|
| 76 |
-
|
| 77 |
-
Either method will start a local web server, and you can access the application at the URL provided in the console (usually `http://127.0.0.1:7860`).
|
| 78 |
-
|
| 79 |
-
The application will create a `hackathon_participants.db` file in the working directory to store the participant data.
|
| 80 |
-
|
| 81 |
-
---
|
| 82 |
-
|
| 83 |
-
## π§ͺ Running Tests
|
| 84 |
-
|
| 85 |
-
The project includes unit tests for the database and the agent setup logic. The tests are located in the `tests/` directory.
|
| 86 |
-
|
| 87 |
-
To run the tests:
|
| 88 |
-
|
| 89 |
-
```bash
|
| 90 |
-
# From the project root directory
|
| 91 |
-
cd hackathon_organizer
|
| 92 |
-
python -m unittest discover tests
|
| 93 |
-
```
|
| 94 |
-
|
| 95 |
-
The tests are designed to run without needing an active internet connection or a valid API key. They use mocks to simulate agent behavior and an in-memory database for testing database operations.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hackathon_organizer/hackathon_participants.db
DELETED
|
Binary file (41 kB)
|
|
|
hackathon_organizer/requirements.txt
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
tinyagent-py[all]==0.0.12
|
| 2 |
-
cloudpickle
|
| 3 |
-
modal
|
| 4 |
-
jinja2
|
| 5 |
-
pyyaml
|
| 6 |
-
gradio[mcp]
|
| 7 |
-
pandas
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hackathon_participants.db
CHANGED
|
Binary files a/hackathon_participants.db and b/hackathon_participants.db differ
|
|
|
main.py
DELETED
|
@@ -1,52 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Hackathon Team Organizer - Main Entry Point
|
| 4 |
-
|
| 5 |
-
This script launches the Hackathon Team Organizer application.
|
| 6 |
-
It ensures the working directory is set correctly before running the app.
|
| 7 |
-
"""
|
| 8 |
-
import os
|
| 9 |
-
import sys
|
| 10 |
-
import subprocess
|
| 11 |
-
|
| 12 |
-
def main():
|
| 13 |
-
# Get the directory where this script is located
|
| 14 |
-
script_dir = os.path.dirname(os.path.abspath(__file__))
|
| 15 |
-
|
| 16 |
-
# Change to the script directory to ensure relative paths work
|
| 17 |
-
os.chdir(script_dir)
|
| 18 |
-
|
| 19 |
-
# Add the current directory to the Python path
|
| 20 |
-
sys.path.insert(0, script_dir)
|
| 21 |
-
|
| 22 |
-
print("Starting Hackathon Team Organizer...")
|
| 23 |
-
print(f"Working directory: {os.getcwd()}")
|
| 24 |
-
|
| 25 |
-
# Check for OpenAI API key
|
| 26 |
-
if not os.environ.get("OPENAI_API_KEY"):
|
| 27 |
-
print("\nβ οΈ WARNING: OPENAI_API_KEY environment variable is not set!")
|
| 28 |
-
print("The application requires an OpenAI API key to function properly.")
|
| 29 |
-
print("Please set it using:")
|
| 30 |
-
print(" export OPENAI_API_KEY='your-api-key' (macOS/Linux)")
|
| 31 |
-
print(" set OPENAI_API_KEY='your-api-key' (Windows)\n")
|
| 32 |
-
print("Running simplified version without AI matching...")
|
| 33 |
-
app_script = "hackathon_organizer/app_simple.py"
|
| 34 |
-
else:
|
| 35 |
-
print("OpenAI API key found. Running full version with AI matching...")
|
| 36 |
-
app_script = "hackathon_organizer/app.py"
|
| 37 |
-
|
| 38 |
-
# Run the application
|
| 39 |
-
try:
|
| 40 |
-
print(f"Launching application: {app_script}")
|
| 41 |
-
subprocess.run([sys.executable, app_script], check=True)
|
| 42 |
-
except KeyboardInterrupt:
|
| 43 |
-
print("\nApplication stopped by user.")
|
| 44 |
-
except subprocess.CalledProcessError as e:
|
| 45 |
-
print(f"\nError running the application: {e}")
|
| 46 |
-
sys.exit(1)
|
| 47 |
-
except Exception as e:
|
| 48 |
-
print(f"\nUnexpected error: {e}")
|
| 49 |
-
sys.exit(1)
|
| 50 |
-
|
| 51 |
-
if __name__ == "__main__":
|
| 52 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hackathon_organizer/matching_agent.py β matching_agent.py
RENAMED
|
File without changes
|
notebook.ipynb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": null,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [],
|
| 8 |
+
"source": []
|
| 9 |
+
},
|
| 10 |
+
{
|
| 11 |
+
"cell_type": "code",
|
| 12 |
+
"execution_count": 1,
|
| 13 |
+
"metadata": {},
|
| 14 |
+
"outputs": [],
|
| 15 |
+
"source": [
|
| 16 |
+
"%load_ext autoreload\n",
|
| 17 |
+
"%autoreload 2\n"
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"cell_type": "code",
|
| 22 |
+
"execution_count": 2,
|
| 23 |
+
"metadata": {},
|
| 24 |
+
"outputs": [
|
| 25 |
+
{
|
| 26 |
+
"data": {
|
| 27 |
+
"text/plain": [
|
| 28 |
+
"True"
|
| 29 |
+
]
|
| 30 |
+
},
|
| 31 |
+
"execution_count": 2,
|
| 32 |
+
"metadata": {},
|
| 33 |
+
"output_type": "execute_result"
|
| 34 |
+
}
|
| 35 |
+
],
|
| 36 |
+
"source": [
|
| 37 |
+
"import json\n",
|
| 38 |
+
"\n",
|
| 39 |
+
"import os\n",
|
| 40 |
+
"from dotenv import load_dotenv\n",
|
| 41 |
+
"import nest_asyncio\n",
|
| 42 |
+
"nest_asyncio.apply()\n",
|
| 43 |
+
"\n",
|
| 44 |
+
"# Load environment variables from a .env file\n",
|
| 45 |
+
"load_dotenv()"
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"cell_type": "code",
|
| 50 |
+
"execution_count": 3,
|
| 51 |
+
"metadata": {},
|
| 52 |
+
"outputs": [],
|
| 53 |
+
"source": [
|
| 54 |
+
"import tinyagent\n",
|
| 55 |
+
"from textwrap import dedent\n",
|
| 56 |
+
"import tinyagent.hooks\n",
|
| 57 |
+
"import tinyagent.hooks.rich_ui_callback\n",
|
| 58 |
+
"import tinyagent.mcp_client\n",
|
| 59 |
+
"import tinyagent.storage.json_file_storage\n",
|
| 60 |
+
"import tinyagent.storage.postgres_storage\n",
|
| 61 |
+
"import tinyagent.storage.sqlite_storage\n",
|
| 62 |
+
"from tinyagent import tool"
|
| 63 |
+
]
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"cell_type": "code",
|
| 67 |
+
"execution_count": null,
|
| 68 |
+
"metadata": {},
|
| 69 |
+
"outputs": [
|
| 70 |
+
{
|
| 71 |
+
"ename": "SyntaxError",
|
| 72 |
+
"evalue": "invalid syntax (1561902338.py, line 1)",
|
| 73 |
+
"output_type": "error",
|
| 74 |
+
"traceback": [
|
| 75 |
+
" \u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[31m \u001b[39m\u001b[31mimport .create_matching_agent\u001b[39m\n ^\n\u001b[31mSyntaxError\u001b[39m\u001b[31m:\u001b[39m invalid syntax\n"
|
| 76 |
+
]
|
| 77 |
+
}
|
| 78 |
+
],
|
| 79 |
+
"source": [
|
| 80 |
+
"import create_matching_agent"
|
| 81 |
+
]
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"cell_type": "code",
|
| 85 |
+
"execution_count": null,
|
| 86 |
+
"metadata": {},
|
| 87 |
+
"outputs": [],
|
| 88 |
+
"source": []
|
| 89 |
+
}
|
| 90 |
+
],
|
| 91 |
+
"metadata": {
|
| 92 |
+
"kernelspec": {
|
| 93 |
+
"display_name": "Python 3.11 (askdev_p311)",
|
| 94 |
+
"language": "python",
|
| 95 |
+
"name": "askdev_p311"
|
| 96 |
+
},
|
| 97 |
+
"language_info": {
|
| 98 |
+
"codemirror_mode": {
|
| 99 |
+
"name": "ipython",
|
| 100 |
+
"version": 3
|
| 101 |
+
},
|
| 102 |
+
"file_extension": ".py",
|
| 103 |
+
"mimetype": "text/x-python",
|
| 104 |
+
"name": "python",
|
| 105 |
+
"nbconvert_exporter": "python",
|
| 106 |
+
"pygments_lexer": "ipython3",
|
| 107 |
+
"version": "3.11.11"
|
| 108 |
+
}
|
| 109 |
+
},
|
| 110 |
+
"nbformat": 4,
|
| 111 |
+
"nbformat_minor": 2
|
| 112 |
+
}
|
product_manager/organizer.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
#HackBuddyAI
|
| 3 |
-
|
| 4 |
-
Gradio APP for Hackathon Organizer
|
| 5 |
-
|
| 6 |
-
## Problem
|
| 7 |
-
Many people join hackathons individually, and they need to be matched with other individuals to form a team. If the formed team is not suitable, non-of the team members will benefit from the hackathon, they will churn and not participate in the next hackathon.
|
| 8 |
-
|
| 9 |
-
- Help individuals find the right team
|
| 10 |
-
- If a selected team is not suitable, or some of participants didn't show up, the organizer could re-run the matching process to find a new team in no time.
|
| 11 |
-
|
| 12 |
-
## Solution
|
| 13 |
-
1. Web application for participants to fill their profile and goals, e_mail field to be used as a unique identifier, link to their linkedin profile, text field (multilined) for their background, skills, why they like to join the hackathon, etc.
|
| 14 |
-
|
| 15 |
-
### 2. On form submission, the data will be stored in a database/ Google Sheet / SQLite
|
| 16 |
-
|
| 17 |
-
### 3. Organizer part:
|
| 18 |
-
Organizer could define custom system prompt, to navigate the system for matching the participants, for example group size, why organizer looks to have in each group, etc.
|
| 19 |
-
|
| 20 |
-
### 4. Matching Proccess,
|
| 21 |
-
AI Agent, will have access to pandas dataframe, including the participants data and organizer prefrences for matching.
|
| 22 |
-
It use its python code execution tool to actually interact with the data, and match the participants based on defined criteria.
|
| 23 |
-
|
| 24 |
-
## End result
|
| 25 |
-
List of teams with their members, and a short description why they are a good match. in Markdown format.
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
## Tech Stack
|
| 29 |
-
- Gradio
|
| 30 |
-
- Tiny Agent
|
| 31 |
-
- Tiny Code Agent
|
| 32 |
-
- Pandas
|
| 33 |
-
|
| 34 |
-
for developing the agent part we use tiny agent library, you have access to the documentation in the LLM_API_DOC.md file.
|
| 35 |
-
|
| 36 |
-
For the frontend we use gradio, gradio has an integration with tiny agent as well.
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -3,4 +3,5 @@ cloudpickle
|
|
| 3 |
modal
|
| 4 |
jinja2
|
| 5 |
pyyaml
|
| 6 |
-
gradio[mcp]
|
|
|
|
|
|
| 3 |
modal
|
| 4 |
jinja2
|
| 5 |
pyyaml
|
| 6 |
+
gradio[mcp]
|
| 7 |
+
pandas
|
{hackathon_organizer/tests β tests}/__init__.py
RENAMED
|
File without changes
|
tests/test_database.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
import os
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import sys
|
| 5 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 6 |
+
from database import initialize_database, add_participant, get_participants_dataframe
|
| 7 |
+
|
| 8 |
+
# Use a separate test database
|
| 9 |
+
TEST_DB_PATH = "test_hackathon.db"
|
| 10 |
+
|
| 11 |
+
class TestDatabase(unittest.TestCase):
|
| 12 |
+
|
| 13 |
+
@classmethod
|
| 14 |
+
def setUpClass(cls):
|
| 15 |
+
"""Set up a test database before any tests run."""
|
| 16 |
+
cls.db_path = TEST_DB_PATH
|
| 17 |
+
# Point the database module to the test DB
|
| 18 |
+
import database
|
| 19 |
+
database.DB_PATH = cls.db_path
|
| 20 |
+
|
| 21 |
+
def setUp(self):
|
| 22 |
+
"""Initialize a clean database before each test."""
|
| 23 |
+
if os.path.exists(self.db_path):
|
| 24 |
+
os.remove(self.db_path)
|
| 25 |
+
initialize_database()
|
| 26 |
+
|
| 27 |
+
def tearDown(self):
|
| 28 |
+
"""Remove the database file after each test."""
|
| 29 |
+
if os.path.exists(self.db_path):
|
| 30 |
+
os.remove(self.db_path)
|
| 31 |
+
|
| 32 |
+
def test_01_initialize_database(self):
|
| 33 |
+
"""Test that the database and table are created."""
|
| 34 |
+
self.assertTrue(os.path.exists(self.db_path))
|
| 35 |
+
df = get_participants_dataframe()
|
| 36 |
+
self.assertIsInstance(df, pd.DataFrame)
|
| 37 |
+
self.assertEqual(len(df), 0)
|
| 38 |
+
|
| 39 |
+
def test_02_add_and_get_participant(self):
|
| 40 |
+
"""Test adding a single participant and retrieving it."""
|
| 41 |
+
participant = {
|
| 42 |
+
"email": "[email protected]",
|
| 43 |
+
"name": "Test User",
|
| 44 |
+
"linkedin_profile": "linkedin.com/in/test",
|
| 45 |
+
"background": "Testing background.",
|
| 46 |
+
"goals": "Testing goals."
|
| 47 |
+
}
|
| 48 |
+
add_participant(participant)
|
| 49 |
+
|
| 50 |
+
df = get_participants_dataframe()
|
| 51 |
+
self.assertEqual(len(df), 1)
|
| 52 |
+
|
| 53 |
+
retrieved = df.iloc[0]
|
| 54 |
+
self.assertEqual(retrieved["email"], participant["email"])
|
| 55 |
+
self.assertEqual(retrieved["name"], participant["name"])
|
| 56 |
+
|
| 57 |
+
def test_03_add_multiple_participants(self):
|
| 58 |
+
"""Test adding multiple participants."""
|
| 59 |
+
participants_to_add = [
|
| 60 |
+
{"email": "[email protected]", "name": "User One", "linkedin_profile": "", "background": "", "goals": ""},
|
| 61 |
+
{"email": "[email protected]", "name": "User Two", "linkedin_profile": "", "background": "", "goals": ""},
|
| 62 |
+
]
|
| 63 |
+
for p in participants_to_add:
|
| 64 |
+
add_participant(p)
|
| 65 |
+
|
| 66 |
+
df = get_participants_dataframe()
|
| 67 |
+
self.assertEqual(len(df), 2)
|
| 68 |
+
self.assertIn("[email protected]", df["email"].values)
|
| 69 |
+
self.assertIn("[email protected]", df["email"].values)
|
| 70 |
+
|
| 71 |
+
def test_04_replace_participant(self):
|
| 72 |
+
"""Test that adding a participant with an existing email updates the record."""
|
| 73 |
+
original_participant = {
|
| 74 |
+
"email": "[email protected]",
|
| 75 |
+
"name": "Original Name",
|
| 76 |
+
"linkedin_profile": "original_linkedin",
|
| 77 |
+
"background": "original_background",
|
| 78 |
+
"goals": "original_goals"
|
| 79 |
+
}
|
| 80 |
+
add_participant(original_participant)
|
| 81 |
+
|
| 82 |
+
df_before = get_participants_dataframe()
|
| 83 |
+
self.assertEqual(len(df_before), 1)
|
| 84 |
+
self.assertEqual(df_before.iloc[0]["name"], "Original Name")
|
| 85 |
+
|
| 86 |
+
updated_participant = {
|
| 87 |
+
"email": "[email protected]",
|
| 88 |
+
"name": "Updated Name",
|
| 89 |
+
"linkedin_profile": "updated_linkedin",
|
| 90 |
+
"background": "updated_background",
|
| 91 |
+
"goals": "updated_goals"
|
| 92 |
+
}
|
| 93 |
+
add_participant(updated_participant)
|
| 94 |
+
|
| 95 |
+
df_after = get_participants_dataframe()
|
| 96 |
+
self.assertEqual(len(df_after), 1)
|
| 97 |
+
self.assertEqual(df_after.iloc[0]["name"], "Updated Name")
|
| 98 |
+
self.assertEqual(df_after.iloc[0]["linkedin_profile"], "updated_linkedin")
|
| 99 |
+
|
| 100 |
+
if __name__ == '__main__':
|
| 101 |
+
unittest.main()
|
tests/test_matching_agent.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
from unittest.mock import patch, MagicMock, AsyncMock
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 7 |
+
from tinyagent import TinyCodeAgent
|
| 8 |
+
from matching_agent import create_matching_agent, run_matching
|
| 9 |
+
|
| 10 |
+
class TestMatchingAgent(unittest.TestCase):
|
| 11 |
+
|
| 12 |
+
@patch.dict(os.environ, {"OPENAI_API_KEY": "test_key"})
|
| 13 |
+
def test_01_create_matching_agent(self):
|
| 14 |
+
"""
|
| 15 |
+
Test the creation and configuration of the matching agent.
|
| 16 |
+
"""
|
| 17 |
+
agent = create_matching_agent()
|
| 18 |
+
|
| 19 |
+
self.assertIsInstance(agent, TinyCodeAgent)
|
| 20 |
+
self.assertEqual(agent.model, "gpt-4.1-mini")
|
| 21 |
+
self.assertIn("You are a brilliant hackathon team-matching AI.", agent.system_prompt)
|
| 22 |
+
self.assertIn("participants_df", agent.system_prompt)
|
| 23 |
+
self.assertIn("organizer_criteria", agent.system_prompt)
|
| 24 |
+
|
| 25 |
+
# Check code execution environment settings
|
| 26 |
+
self.assertIn("pandas", agent.pip_packages)
|
| 27 |
+
self.assertIn("scikit-learn", agent.pip_packages)
|
| 28 |
+
self.assertIn("pandas", agent.authorized_imports)
|
| 29 |
+
|
| 30 |
+
@patch.dict(os.environ, {"OPENAI_API_KEY": "test_key"})
|
| 31 |
+
def test_02_run_matching(self):
|
| 32 |
+
"""
|
| 33 |
+
Test the run_matching function to ensure it configures the agent
|
| 34 |
+
and calls the run method correctly.
|
| 35 |
+
"""
|
| 36 |
+
# Create a mock agent to isolate the test from the actual TinyCodeAgent implementation
|
| 37 |
+
mock_agent = MagicMock(spec=TinyCodeAgent)
|
| 38 |
+
mock_agent.run = AsyncMock(return_value="## Mocked Team Report")
|
| 39 |
+
|
| 40 |
+
# Sample data
|
| 41 |
+
participants_df = pd.DataFrame({
|
| 42 |
+
"name": ["Alice", "Bob"],
|
| 43 |
+
"skills": ["Frontend", "Backend"]
|
| 44 |
+
})
|
| 45 |
+
organizer_criteria = "Create teams of 2."
|
| 46 |
+
|
| 47 |
+
# Define an async test function to run the coroutine
|
| 48 |
+
async def do_run_matching():
|
| 49 |
+
result = await run_matching(mock_agent, participants_df, organizer_criteria)
|
| 50 |
+
|
| 51 |
+
# --- Assertions ---
|
| 52 |
+
# 1. Check that user_variables were set correctly on the agent
|
| 53 |
+
self.assertIn("participants_df", mock_agent.user_variables)
|
| 54 |
+
self.assertIn("organizer_criteria", mock_agent.user_variables)
|
| 55 |
+
pd.testing.assert_frame_equal(mock_agent.user_variables["participants_df"], participants_df)
|
| 56 |
+
self.assertEqual(mock_agent.user_variables["organizer_criteria"], organizer_criteria)
|
| 57 |
+
|
| 58 |
+
# 2. Check that agent.run was called correctly
|
| 59 |
+
mock_agent.run.assert_called_once()
|
| 60 |
+
# The first argument to run should be the user prompt
|
| 61 |
+
self.assertEqual(mock_agent.run.call_args[0][0], "Form the teams based on the provided data and criteria.")
|
| 62 |
+
|
| 63 |
+
# 3. Check the result
|
| 64 |
+
self.assertEqual(result, "## Mocked Team Report")
|
| 65 |
+
|
| 66 |
+
# Run the async test function
|
| 67 |
+
import asyncio
|
| 68 |
+
asyncio.run(do_run_matching())
|
| 69 |
+
|
| 70 |
+
if __name__ == '__main__':
|
| 71 |
+
unittest.main()
|