Skip to content

Commit c96dabe

Browse files
committed
Refactor data loading out of app.py
1 parent 620e6a5 commit c96dabe

File tree

5 files changed

+433
-377
lines changed

5 files changed

+433
-377
lines changed

app.py

Lines changed: 38 additions & 221 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import time
1010
import hashlib
1111
import base64
12+
from data.data_loader import load_military_job_codes, translate_military_code
1213

1314
# Configure page settings and styling
1415
svg_content = '''
@@ -17,15 +18,17 @@
1718
</svg>
1819
'''
1920

21+
2022
def get_svg_base64(svg_content):
21-
b64 = base64.b64encode(svg_content.encode('utf-8')).decode('utf-8')
22-
return f"data:image/svg+xml;base64,{b64}"
23+
b64 = base64.b64encode(svg_content.encode('utf-8')).decode('utf-8')
24+
return f"data:image/svg+xml;base64,{b64}"
25+
2326

2427
# Configure the page with your brand colors
2528
st.set_page_config(
26-
page_title="VetsAI: Vets Who Code Assistant",
27-
page_icon=get_svg_base64(svg_content),
28-
layout="wide"
29+
page_title="VetsAI: Vets Who Code Assistant",
30+
page_icon=get_svg_base64(svg_content),
31+
layout="wide"
2932
)
3033

3134
# Define VWC brand colors and add custom CSS
@@ -114,198 +117,6 @@ def get_svg_base64(svg_content):
114117
if not client.api_key: # Changed from openai.api_key to client.api_key
115118
raise ValueError("OpenAI API key not found in Streamlit secrets.")
116119

117-
def parse_mos_file(file_content: str) -> dict:
118-
"""Parse military job code text file content into a structured dictionary."""
119-
lines = file_content.strip().split('\n')
120-
job_code, title, description = "", "", []
121-
parsing_description = False
122-
123-
for line in lines:
124-
line = line.strip()
125-
if not line:
126-
continue
127-
if line.startswith("Job Code:"):
128-
job_code = line.replace("Job Code:", "").strip()
129-
elif line.startswith("Description:"):
130-
parsing_description = True
131-
elif parsing_description:
132-
description.append(line)
133-
134-
for line in description:
135-
if line:
136-
title = line
137-
break
138-
139-
full_text = ' '.join(description).lower()
140-
category = "general"
141-
category_keywords = {
142-
"information_technology": ["technology", "computer", "network", "data", "software", "hardware", "system", "database"],
143-
"communications": ["communications", "signal", "radio", "transmission", "telecom"],
144-
"intelligence": ["intelligence", "analysis", "surveillance", "reconnaissance"],
145-
"maintenance": ["maintenance", "repair", "technical", "equipment"],
146-
"cyber": ["cyber", "security", "information assurance", "cryptographic"]
147-
}
148-
149-
for cat, keywords in category_keywords.items():
150-
if any(keyword in full_text for keyword in keywords):
151-
category = cat
152-
break
153-
154-
return {
155-
"title": title or "Military Professional",
156-
"category": category,
157-
"skills": [line for line in description if line and len(line) > 10]
158-
}
159-
160-
def load_military_job_codes() -> dict:
161-
base_path = "data/employment_transitions/job_codes"
162-
job_codes = {}
163-
branches = {
164-
"army": {"path": "army", "prefix": "MOS"},
165-
"air_force": {"path": "air_force", "prefix": "AFSC"},
166-
"coast_guard": {"path": "coast_guard", "prefix": "RATE"},
167-
"navy": {"path": "navy", "prefix": "RATE"},
168-
"marine_corps": {"path": "marine_corps", "prefix": "MOS"}
169-
}
170-
171-
for branch, info in branches.items():
172-
branch_path = os.path.join(base_path, info["path"])
173-
if os.path.exists(branch_path):
174-
for file in os.listdir(branch_path):
175-
if file.endswith('.txt'):
176-
try:
177-
with open(os.path.join(branch_path, file), 'r') as f:
178-
content = f.read()
179-
code = file.replace('.txt', '')
180-
details = parse_mos_file(content)
181-
vwc_mapping = map_to_vwc_path(details.get('category', ''), details.get('skills', []))
182-
details.update({
183-
'vwc_path': vwc_mapping['path'],
184-
'tech_focus': vwc_mapping['tech_focus'],
185-
'branch': branch,
186-
'code_type': info['prefix']
187-
})
188-
job_codes[f"{info['prefix']}_{code}"] = details
189-
except Exception as e:
190-
logger.error(f"Error loading {file}: {e}")
191-
continue
192-
return job_codes
193-
194-
def map_to_vwc_path(category: str, skills: List[str]) -> dict:
195-
"""Map military job categories and skills to VWC tech stack paths."""
196-
default_path = {
197-
"path": "Full Stack Development",
198-
"tech_focus": [
199-
"JavaScript/TypeScript fundamentals",
200-
"Next.js and Tailwind for frontend",
201-
"Python with FastAPI/Django for backend"
202-
]
203-
}
204-
205-
tech_paths = {
206-
"information_technology": {
207-
"path": "Full Stack Development",
208-
"tech_focus": [
209-
"JavaScript/TypeScript with focus on system architecture",
210-
"Next.js for complex web applications",
211-
"Python backend services with FastAPI"
212-
]
213-
},
214-
"cyber": {
215-
"path": "Security-Focused Development",
216-
"tech_focus": [
217-
"TypeScript for type-safe applications",
218-
"Secure API development with FastAPI/Django",
219-
"AI/ML for security applications"
220-
]
221-
},
222-
"communications": {
223-
"path": "Frontend Development",
224-
"tech_focus": [
225-
"JavaScript/TypeScript specialization",
226-
"Advanced Next.js and Tailwind",
227-
"API integration with Python backends"
228-
]
229-
},
230-
"intelligence": {
231-
"path": "AI/ML Development",
232-
"tech_focus": [
233-
"Python for data processing",
234-
"ML model deployment with FastAPI",
235-
"Next.js for ML application frontends"
236-
]
237-
},
238-
"maintenance": {
239-
"path": "Backend Development",
240-
"tech_focus": [
241-
"Python backend development",
242-
"API design with FastAPI/Django",
243-
"Basic frontend with Next.js"
244-
]
245-
}
246-
}
247-
248-
skill_keywords = {
249-
"programming": "software",
250-
"database": "data",
251-
"network": "communications",
252-
"security": "cyber",
253-
"analysis": "intelligence"
254-
}
255-
256-
if category.lower() in tech_paths:
257-
return tech_paths[category.lower()]
258-
259-
for skill in skills:
260-
skill_lower = skill.lower()
261-
for keyword, category in skill_keywords.items():
262-
if keyword in skill_lower and category in tech_paths:
263-
return tech_paths[category]
264-
265-
return default_path
266-
267-
def translate_military_code(code: str, job_codes: dict) -> dict:
268-
"""Translate military code to VWC development path."""
269-
code = code.upper().strip()
270-
prefixes = ["MOS", "AFSC", "RATE"]
271-
for prefix in prefixes:
272-
if code.startswith(prefix):
273-
code = code.replace(prefix, "").strip()
274-
275-
possible_codes = [f"MOS_{code}", f"AFSC_{code}", f"RATE_{code}"]
276-
277-
for possible_code in possible_codes:
278-
if possible_code in job_codes:
279-
job_data = job_codes[possible_code]
280-
return {
281-
"found": True,
282-
"data": {
283-
"title": job_data.get('title', 'Military Professional'),
284-
"branch": job_data.get('branch', 'Military'),
285-
"dev_path": job_data.get('vwc_path', 'Full Stack Development'),
286-
"tech_focus": job_data.get('tech_focus', []),
287-
"skills": job_data.get('skills', [])
288-
}
289-
}
290-
291-
return {
292-
"found": False,
293-
"data": {
294-
"title": "Military Professional",
295-
"branch": "Military",
296-
"dev_path": "Full Stack Development",
297-
"tech_focus": [
298-
"Start with JavaScript/TypeScript fundamentals",
299-
"Build projects with Next.js and Tailwind",
300-
"Learn Python backend development with FastAPI"
301-
],
302-
"skills": [
303-
"Leadership and team coordination",
304-
"Problem-solving and adaptation",
305-
"Project planning and execution"
306-
]
307-
}
308-
}
309120

310121
def get_chat_response(messages: List[Dict]) -> str:
311122
"""Get response from OpenAI chat completion."""
@@ -320,6 +131,7 @@ def get_chat_response(messages: List[Dict]) -> str:
320131
logger.error(f"OpenAI API error: {e}")
321132
raise
322133

134+
323135
def export_chat_history(chat_history: List[Dict]) -> str:
324136
"""Export chat history to JSON."""
325137
export_data = {
@@ -328,6 +140,7 @@ def export_chat_history(chat_history: List[Dict]) -> str:
328140
}
329141
return json.dumps(export_data, indent=2)
330142

143+
331144
def save_feedback(feedback: Dict):
332145
"""Save user feedback to file."""
333146
feedback_dir = "feedback"
@@ -339,37 +152,39 @@ def save_feedback(feedback: Dict):
339152
with open(feedback_file, 'w') as f:
340153
json.dump(feedback, f, indent=2)
341154

155+
342156
def handle_command(command: str) -> str:
343157
"""Handle special commands including MOS translation."""
344158
parts = command.lower().split()
345159
if not parts:
346160
return None
347-
161+
348162
cmd = parts[0]
349163
if cmd in ['/mos', '/afsc', '/rate']:
350164
if len(parts) < 2:
351165
return "Please provide a military job code. Example: `/mos 25B`"
352-
166+
353167
code = parts[1]
354168
translation = translate_military_code(code, st.session_state.job_codes)
355169
if translation['found']:
356170
return (
357-
f"🎖️ **{translation['data']['title']}** ({translation['data']['branch']})\n\n"
358-
f"💻 **VWC Development Path**: {translation['data']['dev_path']}\n\n"
359-
"🔧 **Military Skills**:\n" +
360-
"\n".join(f"- {skill}" for skill in translation['data']['skills']) +
361-
"\n\n📚 **VWC Tech Focus**:\n" +
362-
"\n".join(f"{i+1}. {focus}" for i, focus in enumerate(translation['data']['tech_focus']))
171+
f"🎖️ **{translation['data']['title']}** ({translation['data']['branch']})\n\n"
172+
f"💻 **VWC Development Path**: {translation['data']['dev_path']}\n\n"
173+
"🔧 **Military Skills**:\n" +
174+
"\n".join(f"- {skill}" for skill in translation['data']['skills']) +
175+
"\n\n📚 **VWC Tech Focus**:\n" +
176+
"\n".join(f"{i + 1}. {focus}" for i, focus in enumerate(translation['data']['tech_focus']))
363177
)
364178
else:
365179
return (
366-
"I don't have that specific code in my database, but here's a recommended "
367-
"VWC learning path based on general military experience:\n\n" +
368-
"\n".join(f"{i+1}. {focus}" for i, focus in enumerate(translation['data']['tech_focus']))
180+
"I don't have that specific code in my database, but here's a recommended "
181+
"VWC learning path based on general military experience:\n\n" +
182+
"\n".join(f"{i + 1}. {focus}" for i, focus in enumerate(translation['data']['tech_focus']))
369183
)
370-
184+
371185
return None
372186

187+
373188
def initialize_chat():
374189
"""Initialize the chat with a VWC-focused welcome message."""
375190
welcome_message = {
@@ -396,24 +211,25 @@ def initialize_chat():
396211
}
397212
return [welcome_message]
398213

214+
399215
def main():
400216
"""Main application function."""
401217
st.title("🇺🇸 VetsAI: Vets Who Code Assistant")
402-
218+
403219
# Initialize session
404220
if 'session_id' not in st.session_state:
405221
st.session_state.session_id = hashlib.md5(str(time.time()).encode()).hexdigest()
406-
222+
407223
if 'job_codes' not in st.session_state:
408224
try:
409225
st.session_state.job_codes = load_military_job_codes()
410226
except Exception as e:
411227
logger.error(f"Error loading job codes: {e}")
412228
st.session_state.job_codes = {}
413-
229+
414230
if 'messages' not in st.session_state:
415231
st.session_state.messages = initialize_chat()
416-
232+
417233
# Sidebar with VWC tech stack resources
418234
with st.sidebar:
419235
st.markdown("""
@@ -481,7 +297,7 @@ def main():
481297
"content": (
482298
"You are a specialized AI assistant for Vets Who Code members, designed to provide clear, practical technical guidance "
483299
"to veterans transitioning into software development careers.\n\n"
484-
300+
485301
"CORE TECH STACK:\n"
486302
"- Frontend: JavaScript, TypeScript, React, Next.js\n"
487303
"- Styling: CSS, Tailwind CSS\n"
@@ -490,34 +306,34 @@ def main():
490306
"- Advanced: AI/ML fundamentals\n"
491307
"- Development Tools: Git, GitHub, VS Code\n"
492308
"- Testing: Jest, Pytest\n\n"
493-
309+
494310
"CAREER TRANSITION GUIDANCE:\n"
495311
"1. Resume Development:\n"
496312
" - Technical Skills: Programming Languages, Frameworks, Tools, Cloud, Testing\n"
497313
" - Military Experience Translation: Leadership, Problem-solving, Team Collaboration\n\n"
498-
314+
499315
"2. Portfolio Development:\n"
500316
" - Clean code and documentation\n"
501317
" - Version control and API integration\n"
502318
" - Responsive design and performance\n"
503319
" - Testing and TypeScript implementation\n"
504320
" - Security and accessibility standards\n\n"
505-
321+
506322
"LEARNING PATHS:\n"
507323
"1. Fundamentals: HTML, CSS, JavaScript, Git\n"
508324
"2. Intermediate: TypeScript, React, Python\n"
509325
"3. Advanced: Next.js, FastAPI, Streamlit, AI/ML\n\n"
510-
326+
511327
"PROJECT FOCUS:\n"
512328
"1. Portfolio Projects: Personal website, APIs, Data visualization\n"
513329
"2. Technical Skills: Code quality, Testing, Security, Performance\n"
514330
"3. Career Materials: GitHub profile, Technical blog, Documentation\n\n"
515-
331+
516332
"Remember: Provide practical guidance for building technical skills and transitioning to software development careers. "
517333
"Focus on concrete examples and best practices."
518334
)
519335
})
520-
336+
521337
response = get_chat_response(messages)
522338
st.markdown(response)
523339
st.session_state.messages.append({
@@ -547,5 +363,6 @@ def main():
547363
save_feedback(feedback)
548364
st.success("Thank you for your feedback!")
549365

366+
550367
if __name__ == "__main__":
551-
main()
368+
main()

0 commit comments

Comments
 (0)