9
9
import time
10
10
import hashlib
11
11
import base64
12
+ from data .data_loader import load_military_job_codes , translate_military_code
12
13
13
14
# Configure page settings and styling
14
15
svg_content = '''
17
18
</svg>
18
19
'''
19
20
21
+
20
22
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
+
23
26
24
27
# Configure the page with your brand colors
25
28
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"
29
32
)
30
33
31
34
# Define VWC brand colors and add custom CSS
@@ -114,198 +117,6 @@ def get_svg_base64(svg_content):
114
117
if not client .api_key : # Changed from openai.api_key to client.api_key
115
118
raise ValueError ("OpenAI API key not found in Streamlit secrets." )
116
119
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
- }
309
120
310
121
def get_chat_response (messages : List [Dict ]) -> str :
311
122
"""Get response from OpenAI chat completion."""
@@ -320,6 +131,7 @@ def get_chat_response(messages: List[Dict]) -> str:
320
131
logger .error (f"OpenAI API error: { e } " )
321
132
raise
322
133
134
+
323
135
def export_chat_history (chat_history : List [Dict ]) -> str :
324
136
"""Export chat history to JSON."""
325
137
export_data = {
@@ -328,6 +140,7 @@ def export_chat_history(chat_history: List[Dict]) -> str:
328
140
}
329
141
return json .dumps (export_data , indent = 2 )
330
142
143
+
331
144
def save_feedback (feedback : Dict ):
332
145
"""Save user feedback to file."""
333
146
feedback_dir = "feedback"
@@ -339,37 +152,39 @@ def save_feedback(feedback: Dict):
339
152
with open (feedback_file , 'w' ) as f :
340
153
json .dump (feedback , f , indent = 2 )
341
154
155
+
342
156
def handle_command (command : str ) -> str :
343
157
"""Handle special commands including MOS translation."""
344
158
parts = command .lower ().split ()
345
159
if not parts :
346
160
return None
347
-
161
+
348
162
cmd = parts [0 ]
349
163
if cmd in ['/mos' , '/afsc' , '/rate' ]:
350
164
if len (parts ) < 2 :
351
165
return "Please provide a military job code. Example: `/mos 25B`"
352
-
166
+
353
167
code = parts [1 ]
354
168
translation = translate_military_code (code , st .session_state .job_codes )
355
169
if translation ['found' ]:
356
170
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' ]))
363
177
)
364
178
else :
365
179
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' ]))
369
183
)
370
-
184
+
371
185
return None
372
186
187
+
373
188
def initialize_chat ():
374
189
"""Initialize the chat with a VWC-focused welcome message."""
375
190
welcome_message = {
@@ -396,24 +211,25 @@ def initialize_chat():
396
211
}
397
212
return [welcome_message ]
398
213
214
+
399
215
def main ():
400
216
"""Main application function."""
401
217
st .title ("🇺🇸 VetsAI: Vets Who Code Assistant" )
402
-
218
+
403
219
# Initialize session
404
220
if 'session_id' not in st .session_state :
405
221
st .session_state .session_id = hashlib .md5 (str (time .time ()).encode ()).hexdigest ()
406
-
222
+
407
223
if 'job_codes' not in st .session_state :
408
224
try :
409
225
st .session_state .job_codes = load_military_job_codes ()
410
226
except Exception as e :
411
227
logger .error (f"Error loading job codes: { e } " )
412
228
st .session_state .job_codes = {}
413
-
229
+
414
230
if 'messages' not in st .session_state :
415
231
st .session_state .messages = initialize_chat ()
416
-
232
+
417
233
# Sidebar with VWC tech stack resources
418
234
with st .sidebar :
419
235
st .markdown ("""
@@ -481,7 +297,7 @@ def main():
481
297
"content" : (
482
298
"You are a specialized AI assistant for Vets Who Code members, designed to provide clear, practical technical guidance "
483
299
"to veterans transitioning into software development careers.\n \n "
484
-
300
+
485
301
"CORE TECH STACK:\n "
486
302
"- Frontend: JavaScript, TypeScript, React, Next.js\n "
487
303
"- Styling: CSS, Tailwind CSS\n "
@@ -490,34 +306,34 @@ def main():
490
306
"- Advanced: AI/ML fundamentals\n "
491
307
"- Development Tools: Git, GitHub, VS Code\n "
492
308
"- Testing: Jest, Pytest\n \n "
493
-
309
+
494
310
"CAREER TRANSITION GUIDANCE:\n "
495
311
"1. Resume Development:\n "
496
312
" - Technical Skills: Programming Languages, Frameworks, Tools, Cloud, Testing\n "
497
313
" - Military Experience Translation: Leadership, Problem-solving, Team Collaboration\n \n "
498
-
314
+
499
315
"2. Portfolio Development:\n "
500
316
" - Clean code and documentation\n "
501
317
" - Version control and API integration\n "
502
318
" - Responsive design and performance\n "
503
319
" - Testing and TypeScript implementation\n "
504
320
" - Security and accessibility standards\n \n "
505
-
321
+
506
322
"LEARNING PATHS:\n "
507
323
"1. Fundamentals: HTML, CSS, JavaScript, Git\n "
508
324
"2. Intermediate: TypeScript, React, Python\n "
509
325
"3. Advanced: Next.js, FastAPI, Streamlit, AI/ML\n \n "
510
-
326
+
511
327
"PROJECT FOCUS:\n "
512
328
"1. Portfolio Projects: Personal website, APIs, Data visualization\n "
513
329
"2. Technical Skills: Code quality, Testing, Security, Performance\n "
514
330
"3. Career Materials: GitHub profile, Technical blog, Documentation\n \n "
515
-
331
+
516
332
"Remember: Provide practical guidance for building technical skills and transitioning to software development careers. "
517
333
"Focus on concrete examples and best practices."
518
334
)
519
335
})
520
-
336
+
521
337
response = get_chat_response (messages )
522
338
st .markdown (response )
523
339
st .session_state .messages .append ({
@@ -547,5 +363,6 @@ def main():
547
363
save_feedback (feedback )
548
364
st .success ("Thank you for your feedback!" )
549
365
366
+
550
367
if __name__ == "__main__" :
551
- main ()
368
+ main ()
0 commit comments