Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import os
|
3 |
+
import json
|
4 |
+
from openai import OpenAI
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
|
7 |
+
# Load environment variables
|
8 |
+
load_dotenv()
|
9 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
10 |
+
|
11 |
+
# Initialize OpenAI client
|
12 |
+
client = OpenAI(api_key=api_key)
|
13 |
+
|
14 |
+
# Define the base careers directory
|
15 |
+
base_careers_dir = "careers"
|
16 |
+
|
17 |
+
# Ensure the base careers directory exists
|
18 |
+
os.makedirs(base_careers_dir, exist_ok=True)
|
19 |
+
|
20 |
+
prompt_template = """
|
21 |
+
Generate a detailed JSON object representing a career card for a {career}. The JSON structure should match the provided IPS Officer example, covering the following sections:
|
22 |
+
|
23 |
+
1. "CareerCard":
|
24 |
+
- "Title": The career name (e.g., Doctor, Software Developer, Teacher).
|
25 |
+
- "Enabled": Set to 1 (indicating the card is available).
|
26 |
+
- "ImageMale": A relevant image path for a male character (e.g., "images/careers/doctor/doctor_male.jpg").
|
27 |
+
- "ImageFemale": A relevant image path for a female character (e.g., "images/careers/doctor/doctor_female.jpg").
|
28 |
+
- "Overview": A detailed, gender-inclusive description (3-4 sentences) explaining the career's primary role, responsibilities, and importance.
|
29 |
+
- "Benefits and Challenges":
|
30 |
+
- "Benefits": A list of at least **4 positive aspects** of this career (e.g., job security, personal growth).
|
31 |
+
- "Job Challenges": A list of at least **4 difficulties** or challenges (e.g., stress, work-life balance).
|
32 |
+
- "Dual Impact Highlights":
|
33 |
+
- "Personal Growth": Describe how this career enhances **personal development** (skills, knowledge, personal fulfillment).
|
34 |
+
- "Community Impact": Explain how this career **benefits society** or the broader community.
|
35 |
+
- "Choices":
|
36 |
+
Provide **two decision options** that reflect different approaches to the career. Each option should:
|
37 |
+
- "Option": A choice description. One should focus on **maximizing personal growth**, and the other on **balancing personal and community impact**.
|
38 |
+
- "PersonalImpact": A score between 1-5 for how much this option enhances personal growth.
|
39 |
+
- "CommunityImpact": A score between 1-5 for how much this option helps the community.
|
40 |
+
- "Result": A description of the **outcome** of choosing this path.
|
41 |
+
|
42 |
+
2. "Scenarios":
|
43 |
+
Provide **two real-world scenarios** for this career. Each scenario must:
|
44 |
+
- "Title": A short, engaging title (e.g., "Emergency Surgery Crisis").
|
45 |
+
- "BackgroundImage": A path to a scenario-related image (e.g., "images/careers/doctor/emergency_room.jpg").
|
46 |
+
- "Description": A detailed, gender-neutral narrative (3-5 sentences) presenting a realistic challenge faced by professionals in this career.
|
47 |
+
- "DecisionCards": Two decision options for handling the scenario, each with:
|
48 |
+
- "Option": The action the player can choose (e.g., "Perform emergency surgery").
|
49 |
+
- "Outcomes":
|
50 |
+
- "PersonalImpact": A score (1-7) representing the effect on personal development.
|
51 |
+
- "CommunityImpact": A score (1-7) representing the effect on the community.
|
52 |
+
|
53 |
+
3. "Result":
|
54 |
+
Define the final outcomes based on the player’s total score. Include:
|
55 |
+
- "Achieved": Message for a **high score** (e.g., "You've become a respected leader in your field!").
|
56 |
+
- "AchievedImage": "images/careers/{career}/achieved_{career}.jpg",
|
57 |
+
- "Moderate": Message for a **moderate score** (e.g., "You've made a solid impact but can achieve more.").
|
58 |
+
- "ModerateImage": "images/careers/{career}/moderate_{career}.jpg",
|
59 |
+
- "Retry": Message for a **low score** (e.g., "Your journey ends here… but you can try again!").
|
60 |
+
- "RetryImage": "images/careers/{career}/retry_{career}.jpg",
|
61 |
+
- "Quit": Image path if the player exits the career early (e.g., "Game Over!").
|
62 |
+
- "QuitImage": "images/careers/{career}/game_over.jpg",
|
63 |
+
Ensure the JSON is properly formatted and adheres to the structure exactly. Generate unique and creative responses for each section while maintaining consistency across different careers and all three gender representations.
|
64 |
+
"""
|
65 |
+
|
66 |
+
def generate_career_profile(career, job_category, gender):
|
67 |
+
# Map gender to image key and label
|
68 |
+
gender_map = {
|
69 |
+
"Male": ("Male", "male"),
|
70 |
+
"Female": ("Female", "female"),
|
71 |
+
"Neutral": ("Neutral", "neutral")
|
72 |
+
}
|
73 |
+
gender_label, gender_key = gender_map[gender]
|
74 |
+
|
75 |
+
# Define the prompt
|
76 |
+
prompt = prompt_template.format(
|
77 |
+
career=career,
|
78 |
+
career_lower=career.replace(" ", "_").lower(),
|
79 |
+
gender=gender_key,
|
80 |
+
gender_label=gender_label
|
81 |
+
)
|
82 |
+
|
83 |
+
# Generate the JSON response
|
84 |
+
response = client.chat.completions.create(
|
85 |
+
model="gpt-4o-mini",
|
86 |
+
messages=[
|
87 |
+
{"role": "system", "content": "You are an AI that generates structured career profiles in JSON format."},
|
88 |
+
{"role": "user", "content": prompt},
|
89 |
+
],
|
90 |
+
)
|
91 |
+
|
92 |
+
# Debugging: Print the response
|
93 |
+
print("Response from OpenAI:", response)
|
94 |
+
|
95 |
+
# Extract and clean response content
|
96 |
+
if response.choices:
|
97 |
+
content = response.choices[0].message.content.strip()
|
98 |
+
|
99 |
+
# Remove possible markdown formatting
|
100 |
+
if content.startswith("```json"):
|
101 |
+
content = content[7:-3].strip()
|
102 |
+
|
103 |
+
try:
|
104 |
+
career_profile = json.loads(content) # Convert string to JSON
|
105 |
+
|
106 |
+
# Define the category-specific directory path
|
107 |
+
category_dir = os.path.join(base_careers_dir, job_category)
|
108 |
+
os.makedirs(category_dir, exist_ok=True) # Ensure the category directory exists
|
109 |
+
|
110 |
+
# Define the file path
|
111 |
+
file_path = os.path.join(category_dir, f"{career.replace(' ', '_').lower()}_{gender_key}.json")
|
112 |
+
|
113 |
+
# Save the JSON to a file
|
114 |
+
with open(file_path, "w", encoding="utf-8") as f:
|
115 |
+
json.dump(career_profile, f, indent=4)
|
116 |
+
|
117 |
+
return f"Career profile for '{career}' ({gender}) has been saved as '{file_path}'."
|
118 |
+
|
119 |
+
except json.JSONDecodeError:
|
120 |
+
return "Error: Failed to parse OpenAI response into JSON."
|
121 |
+
|
122 |
+
return "Error: OpenAI API response did not contain the expected content."
|
123 |
+
|
124 |
+
# Define the Gradio interface
|
125 |
+
iface = gr.Interface(
|
126 |
+
fn=generate_career_profile,
|
127 |
+
inputs=[
|
128 |
+
gr.Textbox(label="Job Profile Name"),
|
129 |
+
gr.Dropdown(
|
130 |
+
label="Job Category",
|
131 |
+
choices=["Education", "Technology", "Healthcare", "Hospitality", "Finance", "Marketing", "Engineering", "Arts and Design", "Legal", "Science and Research","Civil Services"],
|
132 |
+
type="value",
|
133 |
+
),
|
134 |
+
gr.Dropdown(
|
135 |
+
label="Gender",
|
136 |
+
choices=["Male", "Female", "Neutral"],
|
137 |
+
type="value",
|
138 |
+
),
|
139 |
+
],
|
140 |
+
outputs=gr.Textbox(label="Status"),
|
141 |
+
live=False
|
142 |
+
)
|
143 |
+
|
144 |
+
# Launch the interface
|
145 |
+
iface.launch()
|