Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions yanfr-lab-open-hr/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
HRFLOW_API_KEY=your_api_key_here
HRFLOW_USER_EMAIL=your_email_here
HRFLOW_SOURCE_KEY=your_source_key_here
HRFLOW_BOARD_KEY=your_board_key_here
59 changes: 59 additions & 0 deletions yanfr-lab-open-hr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Open HR avec HrFlow.ai

> Intelligent candidate sourcing with auto-generated questionnaires and HrFlow.ai-powered scoring.

## What it does

Open HR is an AI sourcing agent built for recruiters. Given a job title and requirements, it:

1. **Auto-generates a weighted questionnaire** from the job description (skills, experience, education, stability)
2. **Indexes the job** via HrFlow.ai Job Indexing API
3. **Scores all candidates** in the source using HrFlow.ai Scoring API with a custom algorithm key
4. **Combines AI scores with questionnaire weights** to produce precise decimal rankings
5. **Allows refinement** via natural language feedback loop

## HrFlow.ai APIs used

- `POST /v1/job/indexing` — Index job description to enable scoring
- `GET /v1/profiles/scoring` — Score all profiles against the job using algorithm key
- `GET /v1/profiles/searching` — Fallback profile search
- `POST /v1/profile/indexing` — Index candidate profiles

## How to run

### Prerequisites

- Python 3.11+

### Setup

```bash
# Install dependencies
pip install -r requirements.txt

# Copy environment variables
cp .env.example .env
# Fill in your HrFlow API keys in .env

# Start the app
uvicorn web_app_fr_v2:app --host 0.0.0.0 --port 8002
```

### Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `HRFLOW_API_KEY` | Yes | HrFlow.ai API secret key |
| `HRFLOW_USER_EMAIL` | Yes | HrFlow.ai account email |
| `HRFLOW_SOURCE_KEY` | Yes | HrFlow.ai source key |
| `HRFLOW_BOARD_KEY` | Yes | HrFlow.ai board key |

## Screenshots

![Preview](./assets/preview.png)

## Team

- **Yan** — Full-stack development & HrFlow.ai integration
- **Leo** — HrFlow.ai API integration & scoring algorithm
- **Aike** — Development & HrFlow.ai integration
16 changes: 16 additions & 0 deletions yanfr-lab-open-hr/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "../../schemas/app.schema.json",
"name": "Open HR avec HrFlow.ai",
"description": "AI agent that auto-generates a weighted questionnaire from any job description, scores and ranks candidates using HrFlow.ai Scoring API, and refines results through a natural language feedback loop.",
"credentials": {
"source_keys": ["c1039b004a836e42427b9c89309ad99cf9b6a73c"],
"board_keys": ["c99d70c3a062c2f99ca78fce23b89fb4f53119ef"],
"algorithm_key": "b1ebac4c62fa96e06206f4433b95ae69674891ff"
},
"settings": {
"team_name": "yanfr-lab",
"theme_color": "#83D9DC",
"custom_filters": [],
"filters": []
}
}
Binary file added yanfr-lab-open-hr/assets/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
74 changes: 74 additions & 0 deletions yanfr-lab-open-hr/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import sqlite3
import json
from datetime import datetime

DB_NAME = "sourcing.db"

def init_db():
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS searches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
query TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
cursor.execute('''
CREATE TABLE IF NOT EXISTS candidates (
id INTEGER PRIMARY KEY AUTOINCREMENT,
search_id INTEGER,
name TEXT,
headline TEXT,
location TEXT,
profile_url TEXT,
source_platform TEXT,
hrflow_profile_key TEXT,
score REAL,
rank INTEGER,
raw_json TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(search_id) REFERENCES searches(id)
)
''')
conn.commit()
conn.close()

def save_search(query: str) -> int:
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
cursor.execute("INSERT INTO searches (query) VALUES (?)", (query,))
search_id = cursor.lastrowid
conn.commit()
conn.close()
return search_id

def save_candidate(search_id: int, candidate: dict):
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
cursor.execute('''
INSERT INTO candidates (search_id, name, headline, location, profile_url,
source_platform, hrflow_profile_key, score, rank, raw_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
search_id, candidate['name'], candidate.get('headline'), candidate.get('location'),
candidate['profile_url'], candidate.get('source_platform', 'github'),
candidate.get('hrflow_profile_key'), candidate.get('score', 0),
candidate.get('rank'), json.dumps(candidate)
))
conn.commit()
conn.close()

def get_recent_searches(limit=20):
conn = sqlite3.connect(DB_NAME)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('''
SELECT s.*, (SELECT COUNT(*) FROM candidates WHERE search_id = s.id) as candidate_count
FROM searches s ORDER BY created_at DESC LIMIT ?
''', (limit,))
rows = [dict(row) for row in cursor.fetchall()]
conn.close()
return rows

init_db()
Empty file.
Loading
Loading