Nattyboi commited on
Commit
f8a0c51
·
verified ·
1 Parent(s): 89fed88

Added ARS (#1)

Browse files

- initial commit (66ceaab066d0ebf7b2b7016d40d54d1fe19afd65)
- Create requirements.txt (de33359dd9db20b1a5220f54f94499db83ee4400)
- Create app.py (a459994d80cab3676a0784225f33ce8ee807bf46)
- Create Dockerfile (0b94fdd7d4a103dd8f14ab99a1fd27d8570fa952)
- Create utils.py (e3884a1638689f48e57cff92937e1b2117418535)
- Update requirements.txt (c36320979d66a8b4c08d365746223f2010283b33)
- Update requirements.txt (6d9386f3f4618dcfd06337740ca3674f0e670912)
- Update app.py (fe28cb295fdf32c2d5cb32546d9bb2617806684e)
- Update app.py (c12efb0fc478f222e913bc60e015082df6e2ec70)
- connected stuff (1bb6f096a2f7b8f9b6c8b2ca3be91d658a866dac)
- changed .env (1f1ff083964e7ba2bfe7905f486bada5ed3bf1aa)
- removed pycache (66a875b92f3de7e31494703e26ef1ed0e888bc36)
- app.py updated (3e286b70c8540b34d56c4e86694f8c5b09cb21f9)
- deleted file (ca1717f8945c27fe4a6604428a90c8b044506274)
- added stuff (1a41961c0f49ccef85fe3f9ac550b856268b05d6)
- updated.py (2026af211aa1e2f8faf804a209845aabcd242027)
- added some stuff (1abc2753eb6b31cd4ab9a56dd63b652d43d5ef65)
- added some stuff (c4073a5a04aa9ec7ecdee64aa6b9225de6724245)
- added some stuff (4cd1ffd1352bcacbe91347d1b8444790edcac164)
- added more stuff (6ba5f500be5004e03820dfd7a04f3d805e847237)
- added jwt auth (26a8b59f87b0e656d4768bfaeb067f857689f7c2)
- added streaks feature (756ed25f5c7a8853c5a610da3e3a2ff827dcfc71)
- changed db up a bit (54b7518c7cfdc7128521023acaf38bf3d3911b79)
- very scripted (8382c943e322dfd4eac3dc652ab03bfcb813d8ff)
- added logout (ba7b47505c09469c56dfb64e0d87c7385a56c1b0)
- changed up something for jwt (0dceb1e95c0489889149296967eaa7a435f869fe)
- changed up something for jwt (936d31ee6555b0af6d2ab2868285cb6c3ff2a06f)
- added logout (d8149f4a80cc9b8aff86ec3d49133e30b47706f4)
- added http 226 error (ec44a32881eb42ee4cf2aedd2d8b82b6ef5466ff)
- added user details (5818dbbacf34d5bedef6cfa1e0dae49b4e06c7af)
- hopefully doesn't crash (f9ab8e5b1bbd0bf9234c7049c7c66bbe9405aaed)
- added dream role and fixed interem role bug (e692eccad884bff8c45c27806b01d14b7480e23b)
- added questionaire to the user details (2128dedf2eb30e4f265b834ed2ae6bcf73277ab9)
- added dream role and fixed interem role bug (c43a9826b7d30564d6612d5cbb5207347ef01b26)
- added dream role and fixed interem role bug (c4c7ccc42d7654a99dd41271465b536f76afadfb)
- fixed questionaire retrieval (5142863f09b619e922aec68ed749521636f80b14)
- fixed updating questionaire (83338db95cab796fe5dc0c58ec1ae4a84dd1a3f1)
- completed this shit (80d2e7b7d4d7c6bc8dfb744f4ae367cbdfe7a0cc)
- login stuff (995533c4da23fefc7bcedac8c447150f9d2002dd)
- HOP (d46a40ec686260dab05748b2228820ca78922c17)
- added provider (398210d72dc645f110c51e4ff99ed310541b3f69)
- changed 0 to 1 (cf203377de703160ff234ab241686d303761b45a)
- added a new data type (73371fd5625a8d6be23cb8bfb6ebd1e2b207e90f)
- made the object less strict (0c276d34853fc789bd61ae121431ae3462429032)
- added userCourse stuff (441685320d5827ac57061ad01f4d769beaa2fbf6)
- fixed verification for refresh token (06533adec92074da1fd5b4e6e1aa3f4dc18377a9)
- added course link (ef82ce661b2485092253cf818107b9871ed31046)
- updated util.py (1060ca882e8dbe7c691ea70893dfdd088b69ae1e)
- fixed streaks issue (8f2e112bdf80fdfd3ecf5cbc30239d48c93c31ab)
- added a new streaks feature (c38f2344cb9151733e8d9dc815f5d9afd9cc9340)
- removed testes.py (5d4fab2eb76ab5e8b86eb63885668ca19ef9823a)
- added some routes (323b3570d37d797a278c36a594ab193ba45a5b16)
- put in the customer rate (32d2a84afc19d7a441f7551fc25e62a1fb3da76a)
- changed testing parameters to stable parameters (aa8143aa91809f595238076b8fdd231db10f57fd)
- added most features (f1b601bf9a32ceb99c318737d02a897617570476)
- added index (8159bdee32b01b8091e05bcbf795306dc36ef4b1)
- updated stream (90bab231aeba71101e6690719818c4579eb49ef7)
- fixed weekend warrior pointing not working (82124ef6be18af0b53420fd7489c12831ea51fb3)
- added total Point to float (ca1bfdd7969f9b6ea94614ae2cc16591e3dde8ed)
- removed int response (a76a90cd34e674c245b02c7a314001941505d39f)
- removed int response (dbebc697ab2f2a03b2fcf09ae396106037442bc1)
- made it production grade (161667a95596eb917d533337909c171af5b131b3)
- removed the returning levels (0d5781045e2e69697f43fada26336a51c9a61ae4)
- removed the returning levels (578e7e0d52ce14b9659ed51f8563440f74975c48)
- added the ability for leaderboard to be recalculated upon every new point, which may slow down performance so I will make it a background process (b8596d51f0ecefc5c14465f45272b43d0c37e4d7)
- changed some stuff I can't remember no need for background processes (1225834a1f805048df83dafcaae0eb2b14a9b7a8)
- added badge url as part of return for rankers (07ffd862cf5c61e01373f48ff4aece5532806fbf)
- added a fix to leaderboard (9bb2f7cc4c1be1c60c90fdd83c585f367a015de1)
- hot fix for leaderboard (d6b9f3da4b60f331556e71e0ba6b1c2435da0812)
- refactored codebase (9f571c04077337b986a1325b165c59a8766ef975)
- updated codebase to make it faster (9f13856d0db8ce88f0824536439fe8b9ba82b2e8)
- added .env.example (0b04c95f3b76a9231af0f1308f251005e2d3229a)
- redis addd to requirements (560381218fd289d3432b440fd72be29c8d35a414)
- cryptography added to requirements (35852be5b10b4f896180be9c62b00871002b8898)
- fixed the refresh token incorrect token error (0197cb38c358ec91a0ff325ddfa19a0f920d60f1)
- fixed byte12 error (58d4afb077be8b27f9d7f62a28613419cd67735c)
- changed name (4238535596edd98310a8e25b3e5a742cc6ebcfe8)
- updated get levels (b37e48c96dbb8a22a788b209b8d4661d5a7dcb29)
- updated get levels (2b9a22d22e0309e20d0d4daa97016e733ad6141d)
- updated get levels (9186c0c7df7fdb539f4929004031c912e5fc9ce0)
- added star url (c74fe081c2e6c12a9683afa2d6f00908ad851aa0)
- fixed error (f9371dab78067b9e9d57699df1314bdfdd17d337)
- fixed error (752266eda903a150c6970f9799d4886842cf4342)
- fixed star url not changing (d9c224e559e244df502b942e4e51f40b2e7dc1b8)
- ars (4eab69a9f03b414e40eecc6f40fb55c1bf6bb4b4)
- stage 1 for ARS (1e531e2133dd12d2ddff48ce04fd72cca732f458)
- added ars route (1aa5032c5dcce62618f8ca891f8c500385563076)
- fixed merge conflict (103cfcd37fd3266b1396516d241c41b998383332)

Ars/__init__.py ADDED
File without changes
Ars/ai_functions.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pdfminer.high_level import extract_text_to_fp
2
+ from io import BytesIO
3
+ from objects import ai,ResumeData,AutomationRiskResult,AutomationRiskInput,RealWorldQuestion,SkillDepthResult,SkillDepthInput,BreakDownByDomainUpdate,FlaggedRiskAreasUpdate,BoostSuggestionsUpdate,AICollabReadinessInput
4
+ from embedDoc import search_pinecone_text
5
+ from fastapi import UploadFile
6
+
7
+ def extract_text_from_bytes(pdf_bytes: bytes) -> str:
8
+ output_string = BytesIO()
9
+ with BytesIO(pdf_bytes) as input_stream:
10
+ extract_text_to_fp(input_stream, output_string)
11
+ return output_string.getvalue().decode()
12
+
13
+
14
+ async def resume_analysis(upload_file:UploadFile) -> ResumeData:
15
+ contents = await upload_file.read()
16
+ resume = extract_text_from_bytes(pdf_bytes=contents)
17
+ if resume:
18
+ prompt = f"""
19
+ You are an AI resume parser. Read the following resume and extract the following structured information from the resume below if any was found:
20
+
21
+ 1. Work experience details: job titles and descriptions.(max3)
22
+ 2. List of technical skills (technologies, tools, platforms, etc.).(max4)
23
+ 3. Soft and transferable skills:
24
+ - Leadership and collaboration
25
+ - Critical thinking and problem-solving
26
+ - Communication skills
27
+ - Cross-functional/interdisciplinary experience
28
+ - Initiative and adaptability
29
+ 4. Career path details:
30
+ - Current or intended role/field
31
+ - Industry and sector context
32
+ - Career trajectory trends
33
+ 5. Project experience (if any): project titles, descriptions, role.(max3)
34
+ 6. Evidence of upskilling (optional): certifications, courses, learning projects, hackathons.
35
+
36
+ Return the extracted data as a JSON object that matches this schema:
37
+
38
+ Here is the resume:
39
+
40
+ {resume}
41
+ """
42
+ result =ai.chat(prompt=prompt,output_schema=ResumeData)
43
+ return result
44
+
45
+
46
+
47
+
48
+
49
+ def calculate_automation_risk(resume:ResumeData):
50
+ def generate_real_world_data(resume:ResumeData):
51
+ prompt=f"Generata a search query using the resume details below I want to check some text that has data on future jobs reports using semantic searches Here is the resume data: {resume} "
52
+ result = ai.chat(prompt=prompt,output_schema=RealWorldQuestion)
53
+ search_result =search_pinecone_text(query_text=result.question)
54
+
55
+ return search_result
56
+
57
+ real_world_data=generate_real_world_data(resume=resume)
58
+ prompt = f"""
59
+ You are an Automation Risk Calculator AI. Your task is to analyze the user's resume and any provided real-world data to estimate how automatable their skills and job titles are.
60
+
61
+ For each of the following factors, please evaluate on a scale from 0 to 5, where:
62
+
63
+ 0 = Not present or irrelevant
64
+ 1 = Very low presence or impact
65
+ 2 = Low presence or impact
66
+ 3 = Moderate presence or impact
67
+ 4 = High presence or impact
68
+ 5 = Very high presence or impact
69
+
70
+ Factors to score:
71
+
72
+ - repetitiveness of tasks
73
+ - creativity required
74
+ - emotional intelligence needed
75
+ - reliance on data-driven tasks
76
+ - physical/manual work involved
77
+ - level of autonomous decision-making
78
+ - need for strategic thinking
79
+ - importance of collaboration
80
+ - current use of AI tools in the role
81
+ - recent upskilling or adaptability
82
+
83
+ The real-world data may contain labor market trends, automation probabilities, or other relevant insights — use it if helpful.
84
+
85
+ After scoring all factors, calculate an overall automation risk score between 0 and 100, where:
86
+ - 100 means very low automation risk (skills are highly resilient to automation),
87
+ - 0 means very high automation risk (skills are highly automatable).
88
+
89
+ Return only the final integer risk score (0 to 100) — no explanations or extra text.
90
+
91
+ Here is the resume:
92
+
93
+ (
94
+ {resume}
95
+ )
96
+
97
+ Here is the real-world data:
98
+
99
+ (
100
+ {real_world_data}
101
+ )
102
+ """
103
+ result = ai.chat(prompt=prompt, output_schema=AutomationRiskInput)
104
+ return result
105
+
106
+
107
+
108
+ def calculate_skill_depth(resume:ResumeData):
109
+
110
+
111
+ prompt = f"""
112
+ You are a Skill Depth Calculator AI. Your task is to analyze the user's resume data and score various skill-related factors.
113
+
114
+ For each of the following factors, please score on a scale from 0 to 5, where:
115
+
116
+ 0 = Not present or no depth
117
+ 1 = Very shallow experience or presence
118
+ 2 = Basic or limited experience
119
+ 3 = Moderate experience or involvement
120
+ 4 = Strong experience or senior level
121
+ 5 = Expert or highly advanced experience
122
+
123
+ Factors to score include (but are not limited to):
124
+
125
+ - Years of experience per skill
126
+ - Seniority level in roles held
127
+ - Number and relevance of certifications
128
+ - Breadth of skills (variety and diversity)
129
+ - Depth in core technical skills
130
+ - Depth in leadership or management skills
131
+ - Involvement in complex projects
132
+ - Contributions to strategic initiatives
133
+ - Frequency of skill usage in recent roles
134
+ - Evidence of continuous learning or upskilling
135
+ - Cross-functional collaboration skills
136
+ - Recognition or awards related to skills
137
+ - Public speaking or training experience
138
+ - Publications or patents (if any)
139
+ - Industry-specific expertise depth
140
+ - Mentoring or coaching experience
141
+ - Ability to innovate using skills
142
+ - Adaptability to new technologies
143
+ - Problem-solving skills depth
144
+ - Communication skills related to technical content
145
+
146
+
147
+
148
+ Here is the resume:
149
+
150
+ (
151
+ {resume}
152
+ )
153
+ """
154
+
155
+ result=ai.chat(prompt=prompt,output_schema=SkillDepthInput)
156
+ return result
157
+
158
+
159
+
160
+ def calculate_Ai_collab_readiness(resume:ResumeData):
161
+
162
+
163
+ prompt = f"""
164
+ You are an AI Collaboration Readiness Calculator. Your task is to read the following resume and assess how ready the individual is to **collaborate with AI tools**, not just compete against them.
165
+
166
+ Score the user on the following 10 readiness traits, each on a scale from 0 to 5:
167
+
168
+ 0 = Not mentioned or no evidence
169
+ 1 = Very minimal evidence or weak relevance
170
+ 2 = Some mention, limited depth
171
+ 3 = Moderate use or awareness
172
+ 4 = Strong use or understanding
173
+ 5 = Expert use or deep integration
174
+
175
+ Traits to evaluate:
176
+
177
+ - Mention of AI tools (e.g., ChatGPT, GitHub Copilot, Midjourney, etc.)
178
+ - Adaptability to AI-enhanced workflows
179
+ - Willingness to learn and adopt AI tools
180
+ - Understanding of AI ethics and responsible use
181
+ - Demonstrated collaboration with AI (not just use)
182
+ - Use of AI for problem-solving
183
+ - Creativity in applying AI to new tasks or contexts
184
+ - Speed in learning and applying new AI technologies
185
+ - Communication skills related to explaining or using AI
186
+ - Integration of AI into work/study processes
187
+
188
+
189
+ Here is the resume:
190
+
191
+ (
192
+ {resume}
193
+ )
194
+ """
195
+
196
+ result=ai.chat(prompt=prompt,output_schema=AICollabReadinessInput)
197
+ return result
198
+
199
+
200
+
201
+
202
+
203
+
204
+ def generate_domain_breakdown(resume:ResumeData,**helpful_key_data):
205
+
206
+
207
+ prompt = f"""
208
+ You have one clear objective: analyze the following resume and produce a domain skills breakdown based on the taxonomy below.
209
+
210
+ **Task:**
211
+ Classify and score the candidate’s skills/domains on a scale from 0 to 100, where 0 means no proficiency and 100 means expert-level proficiency.
212
+
213
+ **Example output format:**
214
+ - Technical: 65
215
+ - Creative: 80
216
+ - Strategy: 75
217
+ - Collaboration: 60
218
+
219
+ **Domains to consider (but not limited to):**
220
+ - Technical (e.g., software development, programming)
221
+ - Creative (e.g., design, content creation)
222
+ - Strategy (e.g., planning, business analysis)
223
+ - Collaboration (e.g., teamwork, communication)
224
+
225
+ **Requirements:**
226
+ - Return only the domain names and their scores as shown in the example.
227
+ - Scores must be integers between 0 and 100.
228
+ - Provide 4–6 domain categories relevant to the resume content.
229
+ - Use your best judgment based on the resume to assign scores reflecting demonstrated skills and experience.
230
+
231
+ Here is the resume to analyze:
232
+
233
+ ({resume})
234
+
235
+
236
+ **Analysis data to aid evaluation**:
237
+ **{helpful_key_data}**
238
+ """
239
+
240
+ result=ai.chat(prompt=prompt,output_schema=BreakDownByDomainUpdate)
241
+ return result
242
+
243
+
244
+
245
+
246
+
247
+ def generate_flagged_risk_areas(resume:ResumeData,**helpful_key_data):
248
+
249
+
250
+ prompt = f"""
251
+
252
+ **You are an AI Resume Risk Evaluator.**
253
+ Your job is to review the following resume and flag potential *career resilience risks*. Focus strictly on:
254
+
255
+ 1. **Missing recent/up-to-date certifications**
256
+ 2. **Lack of AI or automation tool usage**
257
+ 3. **Dependence on easily automatable tasks** (e.g., repetitive data entry, basic spreadsheets)
258
+
259
+ For each issue identified, return **a concise 1–2 sentence explanation** with clear language.
260
+
261
+ **Response Format (Examples)**:
262
+
263
+ * "Heavy reliance on spreadsheet tasks, which are easily automated."
264
+ * "No evidence of recent certifications to stay current in the field."
265
+ * "Resume lacks any mention of AI or automation tools."
266
+
267
+ **Resume to evaluate**:
268
+
269
+ ```
270
+ ({resume})
271
+ ```
272
+
273
+ **Analysis data to aid evaluation**:
274
+ **{helpful_key_data}**
275
+
276
+ """
277
+ result=ai.chat(prompt=prompt,output_schema=FlaggedRiskAreasUpdate)
278
+ return result
279
+
280
+
281
+
282
+
283
+ def generate_boost_suggestion(resume:ResumeData,**helpful_key_data):
284
+
285
+
286
+ prompt = f"""
287
+ You are a Resume Enhancement AI. Analyze the resume below and generate 2 to 4 clear, actionable suggestions to help the user strengthen their profile by addressing skill or experience gaps.
288
+
289
+ Focus on practical, AI-driven improvements such as:
290
+ - Mastering relevant AI tools
291
+ - Enrolling in advanced or specialized courses
292
+ - Expanding the complexity or impact of projects
293
+ - Obtaining up-to-date certifications
294
+
295
+ **Examples**:
296
+ - "Learn ChatGPT to boost your coding efficiency."
297
+ - "Enhance your Python skills through an advanced course."
298
+ - "Add recent certifications to demonstrate continuous growth."
299
+ - "Integrate AI tools into your project portfolio."
300
+
301
+ **Guidelines**:
302
+ - Suggestions must be specific, relevant, and directly tied to the resume content.
303
+ - Keep each suggestion concise (1–2 sentences).
304
+ - Avoid generic advice; prioritize actionable, targeted improvements.
305
+
306
+ Resume to analyze:
307
+
308
+ ({resume})
309
+ **Analysis data to aid evaluation**:
310
+ **{helpful_key_data}**
311
+ """
312
+
313
+ result=ai.chat(prompt=prompt,output_schema=BoostSuggestionsUpdate)
314
+ return result
315
+
316
+
Ars/controllers.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from core import r
2
+ from repositories import create_boost_suggestions,create_breakdown_by_domain,create_flagged_risk_areas,create_user_resilience
3
+ from fastapi import UploadFile
4
+ from ai_functions import resume_analysis,calculate_automation_risk,calculate_Ai_collab_readiness,calculate_skill_depth,generate_boost_suggestion,generate_domain_breakdown,generate_flagged_risk_areas
5
+ from objects import AICollabReadiness,SkillDepthResult,AutomationRiskResult
6
+ async def resilience_analysis(file:UploadFile):
7
+ resume= await resume_analysis(file)
8
+ risk = calculate_automation_risk(resume)
9
+ risk = AutomationRiskResult(**risk.model_dump())
10
+ skill_depth = calculate_skill_depth(resume)
11
+ skill_depth= SkillDepthResult(**skill_depth.model_dump())
12
+ ai_readiness = calculate_Ai_collab_readiness(resume)
13
+ ai_readiness = AICollabReadiness(**ai_readiness.model_dump())
14
+ ResilienceScore = ((1-(risk.result/100))*0.5+(skill_depth.result/100)*0.3+(ai_readiness.result/100)*0.2)
15
+ flagged_risk =generate_flagged_risk_areas(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
16
+ boost_suggestion = generate_boost_suggestion(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
17
+ domain_breakdown = generate_domain_breakdown(resume=resume,skil_depth=skill_depth,risk=risk,ai_readiness=ai_readiness)
18
+
19
+ return {"overall score": ResilienceScore,"flagged Risk": flagged_risk,"boost suggestion":boost_suggestion,"domain breakdown":domain_breakdown,"resume":resume,"skil_depth":skill_depth,"risk":risk,"ai_readiness":ai_readiness}
Ars/core.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # db.py
2
+ import os
3
+ from dotenv import load_dotenv
4
+ import redis
5
+ from motor.motor_asyncio import AsyncIOMotorClient
6
+ load_dotenv()
7
+
8
+ MONGO_URI = os.getenv('MONGO_URI')
9
+ DB_NAME = "crayonics"
10
+ REDIS_HOST = os.getenv('REDIS_HOST', 'localhost')
11
+ REDIS_PORT = int(os.getenv('REDIS_PORT', 6379))
12
+ REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', None)
13
+ FERNET_SECRET_KEY = os.getenv('FERNET_SECRET_KEY')
14
+ REDIS_USERNAME=os.getenv('REDIS_USERNAME')
15
+ client = AsyncIOMotorClient(MONGO_URI)
16
+ db = client[DB_NAME]
17
+ r = redis.StrictRedis(
18
+ host=REDIS_HOST,
19
+ port=REDIS_PORT,
20
+ password=REDIS_PASSWORD,
21
+ username=REDIS_USERNAME,
22
+ db=0,
23
+ decode_responses=True
24
+ )
Ars/embedDoc.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pinecone import Pinecone
2
+ from langchain_text_splitters import RecursiveCharacterTextSplitter
3
+ import os
4
+ from dotenv import load_dotenv
5
+ import time
6
+
7
+ load_dotenv()
8
+ def chunk_list(lst, chunk_size):
9
+ """Yield successive chunks of size `chunk_size` from list."""
10
+ for i in range(0, len(lst), chunk_size):
11
+ yield lst[i:i + chunk_size]
12
+
13
+ def upsert_text_with_chunks(
14
+ text: str,
15
+ *,
16
+ index_host: str = "https://resume-42eo81u.svc.aped-4627-b74a.pinecone.io",
17
+ namespace: str = "default",
18
+ chunk_size: int = 1000,
19
+ chunk_overlap: int = 200
20
+ ) -> None:
21
+ """
22
+ Splits a long text into overlapping chunks and upserts them directly into a Pinecone index
23
+ that has integrated embedding enabled.
24
+
25
+ Args:
26
+ text (str): The full text document to embed.
27
+ index_host (str): Pinecone index host URL.
28
+ namespace (str): Pinecone namespace to upsert into.
29
+ chunk_size (int): Max characters per chunk.
30
+ chunk_overlap (int): Overlap in characters between chunks.
31
+ """
32
+ api_key = os.getenv("PINECONE_API_KEY")
33
+ if not api_key:
34
+ raise EnvironmentError("Set PINECONE_API_KEY in environment")
35
+
36
+ pc = Pinecone(api_key=api_key)
37
+ index = pc.Index(host=index_host)
38
+
39
+ splitter = RecursiveCharacterTextSplitter(
40
+ chunk_size=chunk_size,
41
+ chunk_overlap=chunk_overlap,
42
+ length_function=len,
43
+ is_separator_regex=False
44
+ )
45
+ chunks = splitter.split_text(text)
46
+ if not chunks:
47
+ print("No chunks generated — exiting.")
48
+ return
49
+
50
+ records = [
51
+ {
52
+ "_id": f"chunk-{i}",
53
+ "text": chunk
54
+ }
55
+ for i, chunk in enumerate(chunks)
56
+ ]
57
+
58
+ for batch in chunk_list(records, 50):
59
+ print("Inserting")
60
+ index.upsert_records(records=batch, namespace=namespace)
61
+ time.sleep(60)
62
+ print("resting")
63
+ print(f"✅ Upserted {len(records)} valid chunks (out of {len(chunks)}) into namespace '{namespace}'.")
64
+
65
+
66
+
67
+
68
+
69
+ from pinecone import Pinecone
70
+ from typing import List, Dict
71
+
72
+ def search_pinecone_text(
73
+ query_text: str,
74
+ index_host: str = "https://resume-42eo81u.svc.aped-4627-b74a.pinecone.io",
75
+ namespace: str = "default",
76
+ top_k: int = 2,
77
+ fields: List[str] = ["category", "text"]
78
+ ) -> List[Dict]:
79
+ """
80
+ Search a Pinecone index using a text query.
81
+
82
+ Args:
83
+ api_key (str): Your Pinecone API key.
84
+ index_host (str): The specific index host URL.
85
+ namespace (str): The namespace to search within.
86
+ query_text (str): The input text to search for.
87
+ top_k (int): Number of top results to return.
88
+ fields (List[str]): Metadata fields to include in the response.
89
+
90
+ Returns:
91
+ List[Dict]: The top matching results.
92
+ """
93
+ api_key = os.getenv("PINECONE_API_KEY")
94
+ pc = Pinecone(api_key=api_key)
95
+ index = pc.Index(host=index_host)
96
+
97
+ results = index.search(
98
+ namespace=namespace,
99
+ query={"inputs": {"text": query_text}, "top_k": top_k},
100
+ fields=fields
101
+ )
102
+ print()
103
+ hits =results.result['hits']
104
+ result=[]
105
+ for hit in hits:
106
+ text = hit['fields']['text']
107
+ score = hit['_score']
108
+ result.append({"text":text,"score":score})
109
+ return result
Ars/imports.py ADDED
File without changes
Ars/objects.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from pydantic import Field, model_validator, BaseModel
3
+ from typing import List, Optional, Union,Type, TypeVar
4
+ from bson import ObjectId
5
+ import openai
6
+ from google import genai
7
+ from google.genai import types
8
+ import os
9
+ from dotenv import load_dotenv
10
+ load_dotenv()
11
+ GOOGLE_API_KEY=os.getenv("GEMINI_API_KEY")
12
+ OPENAI_API_KEY=os.getenv("OPENAI_API_KEY")
13
+ T = TypeVar("T", bound=BaseModel)
14
+ class AIWrapper:
15
+ def __init__(self, provider='openai'):
16
+ self.provider = provider.lower()
17
+
18
+ if self.provider == 'openai':
19
+ openai.api_key = OPENAI_API_KEY
20
+ elif self.provider == 'gemini':
21
+ self.gemini_client = genai.Client(
22
+ api_key=GOOGLE_API_KEY,
23
+ http_options=types.HttpOptions(api_version='v1alpha')
24
+ )
25
+ else:
26
+ raise ValueError("Provider must be 'openai' or 'gemini'")
27
+
28
+ def chat(self, prompt: str,output_schema:Type[T]) -> T:
29
+ """
30
+ Generate a response from the AI provider and return it parsed into the specified schema.
31
+
32
+ Args:
33
+ prompt (str): The input prompt.
34
+ output_schema (Type[T]): A Pydantic model representing the output schema.
35
+
36
+ Returns:
37
+ T: Parsed AI response as an instance of the output_schema.
38
+ """
39
+ if self.provider == 'openai':
40
+ return self._openai_chat(prompt)
41
+ elif self.provider == 'gemini':
42
+ return self._gemini_chat(prompt,output_schema=output_schema)
43
+
44
+ def _openai_chat(self, prompt: str) -> str:
45
+ response = openai.ChatCompletion.create(
46
+ model="gpt-4",
47
+ messages=[
48
+ {"role": "user", "content": prompt}
49
+ ]
50
+ )
51
+ return response['choices'][0]['message']['content']
52
+
53
+ def _gemini_chat(self, prompt: str, output_schema: Type[T]) -> T:
54
+ response = self.gemini_client.models.generate_content(
55
+ model='gemini-2.0-flash-001',
56
+ contents=prompt,
57
+ config=types.GenerateContentConfig(
58
+ response_mime_type='application/json',
59
+ response_schema=output_schema,
60
+ ),
61
+ )
62
+
63
+ return response.parsed
64
+
65
+
66
+ ai = AIWrapper(provider='gemini')
67
+
68
+ class UserResilienceScoreCreate(BaseModel):
69
+
70
+ overallScore: float
71
+ userId: str
72
+ BreakDownByDomainId: str
73
+ FlaggedRiskAreasId: str
74
+ BoostSuggestionsId: str
75
+
76
+
77
+ class UserResilienceScoreUpdate(BaseModel):
78
+
79
+ overallScore: Optional[float]=None
80
+ BreakDownByDomainId: Optional[str]=None
81
+ FlaggedRiskAreasId: Optional[str]=None
82
+ BoostSuggestionsId: Optional[str]=None
83
+
84
+
85
+
86
+ class BreakDownByDomainCreate(BaseModel):
87
+ userId:str
88
+ Technical:float
89
+ Creative:float
90
+ Strategy:float
91
+ Collaboration:float
92
+
93
+
94
+ class BreakDownByDomainUpdate(BaseModel):
95
+ Technical:Optional[float]=None
96
+ Creative:Optional[float]=None
97
+ Strategy:Optional[float]=None
98
+ Collaboration:Optional[float]=None
99
+
100
+
101
+ class FlaggedRiskAreasCreate(BaseModel):
102
+ userId:str
103
+ risk_areas:List[str]
104
+
105
+ class FlaggedRiskAreasUpdate(BaseModel):
106
+
107
+ risk_areas:Optional[List[str]]=None
108
+
109
+
110
+ class BoostSuggestionsCreate(BaseModel):
111
+
112
+ boost_suggestions:List[str]
113
+
114
+
115
+
116
+ class ProjectExperienceDetails(BaseModel):
117
+ ProjectTitles: str = Field(..., description="The title(s) of the project(s) involved in.")
118
+ descriptions: str = Field(..., description="Detailed description of the project and what it entailed.")
119
+ RoleInTheProject: str = Field(..., description="The specific role played within the project.")
120
+
121
+ class WorkExperienceDetails(BaseModel):
122
+ JobTitles: str = Field(..., description="The job titles held in past employment.")
123
+ JobDescriptions: str = Field(..., description="Summary of responsibilities and duties in these jobs.")
124
+
125
+ class SoftTransferableSkills(BaseModel):
126
+ LeadershipAndCollaborationIndicators: str = Field(..., description="Evidence or examples demonstrating leadership and teamwork.")
127
+ CriticalThinkingOrProblemSolvingVerb: str = Field(..., description="Examples of critical thinking or problem solving performed.")
128
+ CommunicationSkills: str = Field(None, description="Description of communication skills and contexts.")
129
+ CrossFunctionalOrInterdisciplinaryExperience: str = Field(..., description="Experience working across teams or disciplines.")
130
+ InitiativeAndAdaptabilityLanguage: str = Field(..., description="Examples of taking initiative and adapting to change.")
131
+
132
+ class CareerPathInformation(BaseModel):
133
+ CurrentOrIntendedRoleOrField: str = Field(..., description="Current or intended professional role or field of work.")
134
+ IndustryAndSectorContext: str = Field(..., description="Context about the industry and sector related to the career path.")
135
+ CareerTrajectoryTrends: str = Field(..., description="Observed or expected trends in the career trajectory or sector.")
136
+
137
+ class EvidenceOfUpskillingAndLifelongLearning(BaseModel):
138
+ CertificationsCoursesOrBootcampsListed: Optional[List[str]] = Field(None, description="List of certifications, courses, or bootcamps completed.")
139
+ SelfInitiatedLearningProjectsOrNonDegreeEducationalAchievements: Optional[List[str]] = Field(None, description="List of personal projects or non-degree achievements.")
140
+ ParticipationInHackathonsClubsOrProfessionalCommunities: Optional[List[str]] = Field(None, description="Involvement in hackathons, clubs, or professional groups.")
141
+
142
+ class AIRelatedKeywords(BaseModel):
143
+ AiToolsAndTechnologies: Optional[List[str]] = Field(
144
+ None,
145
+ description="List of AI tools and technologies mentioned in the resume, e.g., ChatGPT, TensorFlow."
146
+ )
147
+ conceptsAndTechniques: Optional[List[str]] = Field(
148
+ None,
149
+ description="AI concepts or techniques like NLP, computer vision, or reinforcement learning."
150
+ )
151
+ aiIntegratedProjectsMentioned: Optional[List[str]] = Field(
152
+ None,
153
+ description="Names or descriptions of projects where AI was applied."
154
+ )
155
+ usageContextDescriptions: Optional[List[str]] = Field(
156
+ None,
157
+ description="Sentences or phrases describing how AI was used in projects or tasks."
158
+ )
159
+
160
+ class ResumeData(BaseModel):
161
+ workExperienceDetails:Optional[List[WorkExperienceDetails]]=None
162
+ listOfExplicitTechnicalSkills:Optional[List[str]]=None
163
+ softTransferableSkills:List[SoftTransferableSkills]
164
+ projectExperienceDetails:Optional[List[ProjectExperienceDetails]]=None
165
+ careerPathInformation:CareerPathInformation
166
+ evidenceOfUpskillingAndLifelongLearning:Optional[EvidenceOfUpskillingAndLifelongLearning]=None
167
+ aiRelatedKeywords:AIRelatedKeywords
168
+
169
+ class RealWorldQuestion(BaseModel):
170
+ question:str
171
+
172
+
173
+
174
+
175
+ class AutomationRiskInput(BaseModel):
176
+ # Resume background fields
177
+ job_title: str = Field(..., description="Most recent job title")
178
+ industry: str = Field(..., description="Industry sector (e.g., finance, education, manufacturing)")
179
+ years_experience: int = Field(..., ge=0, description="Years of professional experience")
180
+ education_level: str = Field(..., description="Highest education level (e.g., Bachelors, Masters, PhD)")
181
+
182
+ technical_skills: List[str] = Field(default_factory=list, description="List of technical skills")
183
+ soft_skills: List[str] = Field(default_factory=list, description="List of soft skills")
184
+ managerial_experience: bool = Field(..., description="Has managed teams or projects")
185
+ customer_facing_roles: bool = Field(..., description="Has held customer-facing roles")
186
+ domain_specialization: Optional[str] = Field(None, description="Specialized domain (e.g., legal, medical)")
187
+ recent_certifications: List[str] = Field(default_factory=list, description="Certifications obtained recently")
188
+
189
+ # Scored traits (all int 0-5)
190
+ repetitiveness_score: int = Field(..., ge=0, le=5, description="Repetitiveness of the tasks performed")
191
+ creativity_score: int = Field(..., ge=0, le=5, description="Creativity required in the role")
192
+ emotional_intelligence_score: int = Field(..., ge=0, le=5, description="Importance of emotional intelligence")
193
+ data_driven_tasks_score: int = Field(..., ge=0, le=5, description="Dependence on data-driven tasks")
194
+ physical_task_score: int = Field(..., ge=0, le=5, description="Amount of physical/manual work")
195
+ decision_making_level: int = Field(..., ge=0, le=5, description="Level of autonomous decision-making")
196
+ strategic_thinking_score: int = Field(..., ge=0, le=5, description="Need for strategic thinking")
197
+ collaboration_score: int = Field(..., ge=0, le=5, description="Collaboration required in the role")
198
+ ai_dependency_score: int = Field(..., ge=0, le=5, description="How much AI tools are already used")
199
+ upskilling_index: int = Field(..., ge=0, le=5, description="Recent evidence of upskilling/adaptability")
200
+
201
+
202
+ class AutomationRiskResult(AutomationRiskInput):
203
+ result: Optional[int] =0
204
+ @model_validator(mode='after')
205
+ def calculate_result(self,cls) -> int:
206
+ """
207
+ Calculate the overall automation risk score (0-100)
208
+ based on the scored traits.
209
+ """
210
+
211
+ # Weights for each scored trait (example weights; you can tune these)
212
+ weights = {
213
+ "repetitiveness_score": 15,
214
+ "creativity_score": -10,
215
+ "emotional_intelligence_score": -10,
216
+ "data_driven_tasks_score": 10,
217
+ "physical_task_score": 10,
218
+ "decision_making_level": -10,
219
+ "strategic_thinking_score": -10,
220
+ "collaboration_score": -5,
221
+ "ai_dependency_score": 5,
222
+ "upskilling_index": -5,
223
+ }
224
+
225
+ # Sum weighted scores
226
+ score = 0
227
+ for field, weight in weights.items():
228
+ value = getattr(self, field)
229
+ score += value * weight
230
+
231
+ # Normalize score to 0-100 range
232
+ # Minimum possible score
233
+ min_score = sum(0 * w for w in weights.values())
234
+ # Maximum possible score
235
+ max_score = sum(5 * w if w > 0 else 0 for w in weights.values()) + \
236
+ sum(0 * w if w < 0 else 0 for w in weights.values())
237
+
238
+ # Because some weights are negative, min/max can be tricky.
239
+ # Let's compute min and max more precisely:
240
+
241
+ min_score = sum(0 * w if w > 0 else 5 * w for w in weights.values())
242
+ max_score = sum(5 * w if w > 0 else 0 * w for w in weights.values())
243
+
244
+ # Clamp the score between min and max
245
+ score = max(min_score, min(max_score, score))
246
+
247
+ # Map score linearly to 0-100
248
+ normalized_score = int((score - min_score) / (max_score - min_score) * 100)
249
+
250
+ self.result = normalized_score
251
+ return self
252
+
253
+
254
+
255
+
256
+
257
+ class SkillDepthInput(BaseModel):
258
+ # Core scoring fields (all 0-5 integers)
259
+ years_experience_per_skill: int = Field(..., ge=0, le=5, description="Depth of years experience per skill")
260
+ seniority_level: int = Field(..., ge=0, le=5, description="Seniority level in roles held")
261
+ certification_presence: int = Field(..., ge=0, le=5, description="Number and relevance of certifications")
262
+ breadth_of_skills: int = Field(..., ge=0, le=5, description="Variety and diversity of skills")
263
+ technical_skill_depth: int = Field(..., ge=0, le=5, description="Depth in core technical skills")
264
+ leadership_skill_depth: int = Field(..., ge=0, le=5, description="Depth in leadership or management skills")
265
+ complex_projects_involvement: int = Field(..., ge=0, le=5, description="Involvement in complex projects")
266
+ strategic_initiatives_contribution: int = Field(..., ge=0, le=5, description="Contributions to strategic initiatives")
267
+ recent_skill_usage_frequency: int = Field(..., ge=0, le=5, description="Frequency of skill usage in recent roles")
268
+ continuous_learning_evidence: int = Field(..., ge=0, le=5, description="Evidence of continuous learning or upskilling")
269
+ cross_functional_collaboration: int = Field(..., ge=0, le=5, description="Cross-functional collaboration skills")
270
+ recognition_awards: int = Field(..., ge=0, le=5, description="Recognition or awards related to skills")
271
+ public_speaking_training: int = Field(..., ge=0, le=5, description="Public speaking or training experience")
272
+ publications_patents: int = Field(..., ge=0, le=5, description="Publications or patents (if any)")
273
+ industry_expertise_depth: int = Field(..., ge=0, le=5, description="Industry-specific expertise depth")
274
+ mentoring_coaching_experience: int = Field(..., ge=0, le=5, description="Mentoring or coaching experience")
275
+ innovation_ability: int = Field(..., ge=0, le=5, description="Ability to innovate using skills")
276
+ adaptability_to_technologies: int = Field(..., ge=0, le=5, description="Adaptability to new technologies")
277
+ problem_solving_depth: int = Field(..., ge=0, le=5, description="Problem-solving skills depth")
278
+ technical_communication_skills: int = Field(..., ge=0, le=5, description="Communication skills related to technical content")
279
+
280
+
281
+ class SkillDepthResult(SkillDepthInput):
282
+ result: Optional[int] =0
283
+ @model_validator(mode='after')
284
+ def calculate_result(self) -> None:
285
+ fields = [
286
+ self.years_experience_per_skill,
287
+ self.seniority_level,
288
+ self.certification_presence,
289
+ self.breadth_of_skills,
290
+ self.technical_skill_depth,
291
+ self.leadership_skill_depth,
292
+ self.complex_projects_involvement,
293
+ self.strategic_initiatives_contribution,
294
+ self.recent_skill_usage_frequency,
295
+ self.continuous_learning_evidence,
296
+ self.cross_functional_collaboration,
297
+ self.recognition_awards,
298
+ self.public_speaking_training,
299
+ self.publications_patents,
300
+ self.industry_expertise_depth,
301
+ self.mentoring_coaching_experience,
302
+ self.innovation_ability,
303
+ self.adaptability_to_technologies,
304
+ self.problem_solving_depth,
305
+ self.technical_communication_skills,
306
+ ]
307
+
308
+ max_total = 5 * len(fields)
309
+ total_score = sum(fields)
310
+ self.result = int((total_score / max_total) * 100)
311
+ return self
312
+
313
+
314
+ class AICollabReadinessInput(BaseModel):
315
+ ai_tool_familiarity: int = Field(..., ge=0, le=5, description="Familiarity with AI tools and platforms")
316
+ adaptability_to_ai_workflows: int = Field(..., ge=0, le=5, description="Ability to adapt to AI-enhanced workflows")
317
+ willingness_to_learn_ai_skills: int = Field(..., ge=0, le=5, description="Motivation and willingness to learn AI skills")
318
+ ai_ethics_understanding: int = Field(..., ge=0, le=5, description="Understanding of AI ethics and responsible use")
319
+ collaboration_with_ai: int = Field(..., ge=0, le=5, description="Experience or mindset to collaborate effectively with AI systems")
320
+ problem_solving_with_ai: int = Field(..., ge=0, le=5, description="Skill in using AI to solve complex problems")
321
+ creativity_in_ai_use: int = Field(..., ge=0, le=5, description="Creativity in leveraging AI capabilities")
322
+ ai_learning_speed: int = Field(..., ge=0, le=5, description="Speed of learning new AI technologies")
323
+ communication_about_ai: int = Field(..., ge=0, le=5, description="Ability to communicate AI concepts effectively")
324
+ ai_tool_integration: int = Field(..., ge=0, le=5, description="Skill in integrating AI tools into existing workflows")
325
+
326
+ class AICollabReadiness(AICollabReadinessInput):
327
+ result: Optional[int] =0
328
+ @model_validator(mode='after')
329
+ def calculate_result(self) -> None:
330
+ fields = [
331
+ self.ai_tool_familiarity,
332
+ self.adaptability_to_ai_workflows,
333
+ self.willingness_to_learn_ai_skills,
334
+ self.ai_ethics_understanding,
335
+ self.collaboration_with_ai,
336
+ self.problem_solving_with_ai,
337
+ self.creativity_in_ai_use,
338
+ self.ai_learning_speed,
339
+ self.communication_about_ai,
340
+ self.ai_tool_integration,
341
+ ]
342
+ max_total = 5 * len(fields)
343
+ total_score = sum(fields)
344
+ self.result = int((total_score / max_total) * 100)
345
+ return self
346
+
347
+
348
+
349
+
350
+ class BoostSuggestionsUpdate(BaseModel):
351
+
352
+ boost_suggestions:List[str]
353
+
354
+
355
+
356
+ class UserResilienceScoreOut(UserResilienceScoreCreate):
357
+ _id: Optional[ObjectId]=None # Make sure _id can be Optional
358
+ id:Optional[str]=None
359
+ # To convert MongoDB ObjectId to string
360
+ class Config:
361
+ json_encoders = {
362
+ ObjectId: str
363
+ }
364
+
365
+ # Custom validator to handle the ObjectId conversion if needed
366
+ @model_validator(mode='before')
367
+ def handle_objectid(cls, values):
368
+ if '_id' in values and isinstance(values['_id'], ObjectId):
369
+ values['id'] = str(values['_id']) # Convert ObjectId to string
370
+ return values
371
+
372
+ class BreakDownByDomainOut(BreakDownByDomainCreate):
373
+ _id: Optional[ObjectId]=None # Make sure _id can be Optional
374
+ id:Optional[str]=None
375
+ # To convert MongoDB ObjectId to string
376
+ class Config:
377
+ json_encoders = {
378
+ ObjectId: str
379
+ }
380
+
381
+ # Custom validator to handle the ObjectId conversion if needed
382
+ @model_validator(mode='before')
383
+ def handle_objectid(cls, values):
384
+ if '_id' in values and isinstance(values['_id'], ObjectId):
385
+ values['id'] = str(values['_id']) # Convert ObjectId to string
386
+ return values
387
+
388
+ class FlaggedRiskAreasOut(FlaggedRiskAreasCreate):
389
+ _id: Optional[ObjectId]=None # Make sure _id can be Optional
390
+ id:Optional[str]=None
391
+ class Config:
392
+ json_encoders = {
393
+ ObjectId: str
394
+ }
395
+
396
+ # Custom validator to handle the ObjectId conversion if needed
397
+ @model_validator(mode='before')
398
+ def handle_objectid(cls, values):
399
+ if '_id' in values and isinstance(values['_id'], ObjectId):
400
+ values['id'] = str(values['_id']) # Convert ObjectId to string
401
+ return values
402
+ class BoostSuggestionsOut(BoostSuggestionsCreate):
403
+ _id: Optional[ObjectId]=None # Make sure _id can be Optional
404
+ id:Optional[str]=None
405
+ class Config:
406
+ json_encoders = {
407
+ ObjectId: str
408
+ }
409
+
410
+ # Custom validator to handle the ObjectId conversion if needed
411
+ @model_validator(mode='before')
412
+ def handle_objectid(cls, values):
413
+ if '_id' in values and isinstance(values['_id'], ObjectId):
414
+ values['id'] = str(values['_id']) # Convert ObjectId to string
415
+ return values
Ars/repositories.py ADDED
@@ -0,0 +1,407 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from core import db
2
+ from bson import ObjectId
3
+ from fastapi import HTTPException
4
+ from typing import Optional, List
5
+ from motor.motor_asyncio import AsyncIOMotorDatabase
6
+ from objects import UserResilienceScoreCreate, UserResilienceScoreOut,BreakDownByDomainCreate,BreakDownByDomainOut,FlaggedRiskAreasCreate,FlaggedRiskAreasOut,BoostSuggestionsCreate,BoostSuggestionsOut,BoostSuggestionsUpdate,UserResilienceScoreUpdate,FlaggedRiskAreasUpdate,BreakDownByDomainUpdate
7
+
8
+
9
+ async def create_user_resilience( data: UserResilienceScoreCreate) -> UserResilienceScoreOut:
10
+ """
11
+ Create a new UserResilienceScore in the database.
12
+
13
+ Args:
14
+ db: The MongoDB database instance.
15
+ data: A Pydantic object containing the fields to create.
16
+
17
+ Returns:
18
+ The newly created object.
19
+ """
20
+ result = await db.user_resilience.insert_one(data.model_dump())
21
+ created = await db.user_resilience.find_one({"_id": result.inserted_id})
22
+ out = UserResilienceScoreOut(**created)
23
+ return out
24
+
25
+
26
+ async def get_user_resilience( object_id: str) -> Optional[UserResilienceScoreOut]:
27
+ """
28
+ Retrieve a UserResilienceScore by its ID.
29
+
30
+ Args:
31
+ db: The MongoDB database instance.
32
+ object_id: The ID of the object to retrieve.
33
+
34
+ Returns:
35
+ An bject, or raises 404 if not found.
36
+ """
37
+ if not ObjectId.is_valid(object_id):
38
+ raise HTTPException(status_code=400, detail="Invalid ID format")
39
+
40
+ result = await db.user_resilience.find_one({"_id": ObjectId(object_id)})
41
+
42
+ if result is None:
43
+ raise HTTPException(status_code=404, detail="Object not found")
44
+ out =UserResilienceScoreOut(**result)
45
+ return out
46
+
47
+
48
+ async def update_user_resilience( object_id: str, data: UserResilienceScoreUpdate) -> UserResilienceScoreOut:
49
+ """
50
+ Update a UserResilienceScore by its ID.
51
+
52
+ Args:
53
+ db: The MongoDB database instance.
54
+ object_id: The ID of the object to update.
55
+ data: A Pydantic object with the updated fields.
56
+
57
+ Returns:
58
+ The updated object dictionary.
59
+ """
60
+ if not ObjectId.is_valid(object_id):
61
+ raise HTTPException(status_code=400, detail="Invalid ID format")
62
+
63
+ update_data = {k: v for k, v in data.dict().items() if v is not None}
64
+ result = await db.user_resilience.update_one(
65
+ {"_id": ObjectId(object_id)},
66
+ {"$set": update_data}
67
+ )
68
+
69
+ if result.matched_count == 0:
70
+ raise HTTPException(status_code=404, detail="Object not found")
71
+
72
+ updateresult =await db.user_resilience.find_one({"_id": ObjectId(object_id)})
73
+ out = UserResilienceScoreOut(**updateresult)
74
+ return out
75
+
76
+
77
+
78
+ async def delete_user_resilience( object_id: str) -> dict:
79
+ """
80
+ Delete a UserResilienceScore by its ID.
81
+
82
+ Args:
83
+ db: The MongoDB database instance.
84
+ object_id: The ID of the object to delete.
85
+
86
+ Returns:
87
+ A confirmation message or raises 404 if object is not found.
88
+ """
89
+ if not ObjectId.is_valid(object_id):
90
+ raise HTTPException(status_code=400, detail="Invalid ID format")
91
+
92
+ result = await db.user_resilience.delete_one({"_id": ObjectId(object_id)})
93
+
94
+ if result.deleted_count == 0:
95
+ raise HTTPException(status_code=404, detail="Object not found")
96
+
97
+ return {"message": "Object deleted successfully"}
98
+
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+ async def create_breakdown_by_domain( data: BreakDownByDomainCreate) -> BreakDownByDomainOut:
118
+ """
119
+ Create a new BreakDownByDomain in the database.
120
+
121
+ Args:
122
+ db: The MongoDB database instance.
123
+ data: A Pydantic object containing the fields to create.
124
+
125
+ Returns:
126
+ A dictionary representing the newly created object.
127
+ """
128
+ result = await db.breakdown_by_domain.insert_one(data.dict())
129
+ created = await db.breakdown_by_domain.find_one({"_id": result.inserted_id})
130
+ out = BreakDownByDomainOut(**created)
131
+ return out
132
+
133
+
134
+ async def get_breakdown_by_domain( object_id: str) -> Optional[ BreakDownByDomainOut]:
135
+ """
136
+ Retrieve a BreakDownByDomain by its ID.
137
+
138
+ Args:
139
+ db: The MongoDB database instance.
140
+ object_id: The ID of the object to retrieve.
141
+
142
+ Returns:
143
+ A dictionary of the found object, or raises 404 if not found.
144
+ """
145
+ if not ObjectId.is_valid(object_id):
146
+ raise HTTPException(status_code=400, detail="Invalid ID format")
147
+
148
+ result = await db.breakdown_by_domain.find_one({"_id": ObjectId(object_id)})
149
+
150
+ if result is None:
151
+ raise HTTPException(status_code=404, detail="Object not found")
152
+ out = BreakDownByDomainOut(**result)
153
+ return out
154
+
155
+
156
+ async def update_breakdown_by_domain( object_id: str, data: BreakDownByDomainUpdate) -> BreakDownByDomainOut:
157
+ """
158
+ Update a BreakDownByDomain by its ID.
159
+
160
+ Args:
161
+ db: The MongoDB database instance.
162
+ object_id: The ID of the object to update.
163
+ data: A Pydantic object with the updated fields.
164
+
165
+ Returns:
166
+ The updated object dictionary.
167
+ """
168
+ if not ObjectId.is_valid(object_id):
169
+ raise HTTPException(status_code=400, detail="Invalid ID format")
170
+
171
+ update_data = {k: v for k, v in data.dict().items() if v is not None}
172
+ result = await db.breakdown_by_domain.update_one(
173
+ {"_id": ObjectId(object_id)},
174
+ {"$set": update_data}
175
+ )
176
+
177
+ if result.matched_count == 0:
178
+ raise HTTPException(status_code=404, detail="Object not found")
179
+
180
+ updateresult =await db. breakdown_by_domain.find_one({"_id": ObjectId(object_id)})
181
+ out = BreakDownByDomainOut(**updateresult)
182
+ return out
183
+
184
+
185
+
186
+ async def delete_breakdown_by_domain( object_id: str) -> dict:
187
+ """
188
+ Delete a BreakDownByDomain by its ID.
189
+
190
+ Args:
191
+ db: The MongoDB database instance.
192
+ object_id: The ID of the object to delete.
193
+
194
+ Returns:
195
+ A confirmation message or raises 404 if object is not found.
196
+ """
197
+ if not ObjectId.is_valid(object_id):
198
+ raise HTTPException(status_code=400, detail="Invalid ID format")
199
+
200
+ result = await db.breakdown_by_domain.delete_one({"_id": ObjectId(object_id)})
201
+
202
+ if result.deleted_count == 0:
203
+ raise HTTPException(status_code=404, detail="Object not found")
204
+
205
+ return {"message": "Object deleted successfully"}
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+ async def create_flagged_risk_areas( data: FlaggedRiskAreasCreate) -> FlaggedRiskAreasOut:
216
+ """
217
+ Create a new BreakDownByDomain in the database.
218
+
219
+ Args:
220
+ db: The MongoDB database instance.
221
+ data: A Pydantic object containing the fields to create.
222
+
223
+ Returns:
224
+ A dictionary representing the newly created object.
225
+ """
226
+ result = await db.flagged_risk_areas.insert_one(data.dict())
227
+ created = await db.flagged_risk_areas.find_one({"_id": result.inserted_id})
228
+ out = FlaggedRiskAreasOut(**created)
229
+ return out
230
+
231
+
232
+ async def get_flagged_risk_areas( object_id: str) -> Optional[FlaggedRiskAreasOut]:
233
+ """
234
+ Retrieve a FlaggedRiskAreas by its ID.
235
+
236
+ Args:
237
+ db: The MongoDB database instance.
238
+ object_id: The ID of the object to retrieve.
239
+
240
+ Returns:
241
+ A dictionary of the found object, or raises 404 if not found.
242
+ """
243
+ if not ObjectId.is_valid(object_id):
244
+ raise HTTPException(status_code=400, detail="Invalid ID format")
245
+
246
+ result = await db.flagged_risk_areas.find_one({"_id": ObjectId(object_id)})
247
+
248
+ if result is None:
249
+ raise HTTPException(status_code=404, detail="Object not found")
250
+ out =FlaggedRiskAreasOut(**result)
251
+ return out
252
+
253
+
254
+ async def update_flagged_risk_areas( object_id: str, data: FlaggedRiskAreasUpdate) -> FlaggedRiskAreasOut:
255
+ """
256
+ Update a FlaggedRiskAreas by its ID.
257
+
258
+ Args:
259
+ db: The MongoDB database instance.
260
+ object_id: The ID of the object to update.
261
+ data: A Pydantic object with the updated fields.
262
+
263
+ Returns:
264
+ The updated object dictionary.
265
+ """
266
+ if not ObjectId.is_valid(object_id):
267
+ raise HTTPException(status_code=400, detail="Invalid ID format")
268
+
269
+ update_data = {k: v for k, v in data.dict().items() if v is not None}
270
+ result = await db.flagged_risk_areas.update_one(
271
+ {"_id": ObjectId(object_id)},
272
+ {"$set": update_data}
273
+ )
274
+
275
+ if result.matched_count == 0:
276
+ raise HTTPException(status_code=404, detail="Object not found")
277
+
278
+ updateresult =await db.flagged_risk_areas.find_one({"_id": ObjectId(object_id)})
279
+ out = FlaggedRiskAreasOut(**updateresult)
280
+ return out
281
+
282
+
283
+
284
+ async def delete_flagged_risk_areas( object_id: str) -> dict:
285
+ """
286
+ Delete a FlaggedRiskAreas by its ID.
287
+
288
+ Args:
289
+ db: The MongoDB database instance.
290
+ object_id: The ID of the object to delete.
291
+
292
+ Returns:
293
+ A confirmation message or raises 404 if object is not found.
294
+ """
295
+ if not ObjectId.is_valid(object_id):
296
+ raise HTTPException(status_code=400, detail="Invalid ID format")
297
+
298
+ result = await db.flagged_risk_areas.delete_one({"_id": ObjectId(object_id)})
299
+
300
+ if result.deleted_count == 0:
301
+ raise HTTPException(status_code=404, detail="Object not found")
302
+
303
+ return {"message": "Object deleted successfully"}
304
+
305
+
306
+
307
+
308
+
309
+
310
+
311
+
312
+
313
+
314
+
315
+
316
+
317
+
318
+
319
+ async def create_boost_suggestions( data: BoostSuggestionsCreate) -> BoostSuggestionsOut:
320
+ """
321
+ Create a new BoostSuggestions in the database.
322
+
323
+ Args:
324
+ db: The MongoDB database instance.
325
+ data: A Pydantic object containing the fields to create.
326
+
327
+ Returns:
328
+ A dictionary representing the newly created object.
329
+ """
330
+ result = await db.boost_suggestions.insert_one(data.dict())
331
+ created = await db.boost_suggestions.find_one({"_id": result.inserted_id})
332
+ out = BoostSuggestionsOut(**created)
333
+ return out
334
+
335
+
336
+ async def get_boost_suggestions( object_id: str) -> Optional[BoostSuggestionsOut]:
337
+ """
338
+ Retrieve a BoostSuggestions by its ID.
339
+
340
+ Args:
341
+ db: The MongoDB database instance.
342
+ object_id: The ID of the object to retrieve.
343
+
344
+ Returns:
345
+ A dictionary of the found object, or raises 404 if not found.
346
+ """
347
+ if not ObjectId.is_valid(object_id):
348
+ raise HTTPException(status_code=400, detail="Invalid ID format")
349
+
350
+ result = await db.boost_suggestions.find_one({"_id": ObjectId(object_id)})
351
+
352
+ if result is None:
353
+ raise HTTPException(status_code=404, detail="Object not found")
354
+ out =BoostSuggestionsOut(**result)
355
+ return out
356
+
357
+
358
+ async def update_boost_suggestions( object_id: str, data: BoostSuggestionsUpdate) -> BoostSuggestionsOut:
359
+ """
360
+ Update a BoostSuggestions by its ID.
361
+
362
+ Args:
363
+ db: The MongoDB database instance.
364
+ object_id: The ID of the object to update.
365
+ data: A Pydantic object with the updated fields.
366
+
367
+ Returns:
368
+ The updated object dictionary.
369
+ """
370
+ if not ObjectId.is_valid(object_id):
371
+ raise HTTPException(status_code=400, detail="Invalid ID format")
372
+
373
+ update_data = {k: v for k, v in data.dict().items() if v is not None}
374
+ result = await db.boost_suggestions.update_one(
375
+ {"_id": ObjectId(object_id)},
376
+ {"$set": update_data}
377
+ )
378
+
379
+ if result.matched_count == 0:
380
+ raise HTTPException(status_code=404, detail="Object not found")
381
+
382
+ updateresult =await db.boost_suggestions.find_one({"_id": ObjectId(object_id)})
383
+ out = BoostSuggestionsOut(**updateresult)
384
+ return out
385
+
386
+
387
+
388
+ async def delete_boost_suggestions( object_id: str) -> dict:
389
+ """
390
+ Delete a BoostSuggestions by its ID.
391
+
392
+ Args:
393
+ db: The MongoDB database instance.
394
+ object_id: The ID of the object to delete.
395
+
396
+ Returns:
397
+ A confirmation message or raises 404 if object is not found.
398
+ """
399
+ if not ObjectId.is_valid(object_id):
400
+ raise HTTPException(status_code=400, detail="Invalid ID format")
401
+
402
+ result = await db.boost_suggestions.delete_one({"_id": ObjectId(object_id)})
403
+
404
+ if result.deleted_count == 0:
405
+ raise HTTPException(status_code=404, detail="Object not found")
406
+
407
+ return {"message": "Object deleted successfully"}
Ars/routes.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ from fastapi import FastAPI, File, UploadFile,HTTPException
3
+ from controllers import resilience_analysis
4
+ from embedDoc import upsert_text_with_chunks,search_pinecone_text
5
+ ARS = FastAPI()
6
+
7
+ @ARS.post("/risk-analysis")
8
+ async def perform_risk_analysis(file: UploadFile = File(...)):
9
+ if file.content_type != "application/pdf":
10
+ return HTTPException(status_code=400, detail={"error": "File must be a PDF."})
11
+ ResilienceScore = await resilience_analysis(file=file)
12
+ return ResilienceScore
13
+
14
+
15
+
README.md CHANGED
@@ -8,3 +8,7 @@ pinned: false
8
  license: apache-2.0
9
  ---
10
 
 
 
 
 
 
8
  license: apache-2.0
9
  ---
10
 
11
+ <<<<<<< HEAD
12
+ =======
13
+ if you are changing the connection string in the env to a new one ensure you populate the levels table with levels up to level9 for a default career path and default level name else points won't be calculated properly
14
+ >>>>>>> master
app.py CHANGED
@@ -2,11 +2,13 @@ from controller.imports import *
2
 
3
  import logging
4
  from datetime import datetime
 
5
  logging.basicConfig(level=logging.INFO)
6
  logger = logging.getLogger(__name__)
7
 
8
  app = FastAPI()
9
  app.mount('/gamification',gamification)
 
10
 
11
 
12
 
 
2
 
3
  import logging
4
  from datetime import datetime
5
+ from Ars.routes import ARS
6
  logging.basicConfig(level=logging.INFO)
7
  logger = logging.getLogger(__name__)
8
 
9
  app = FastAPI()
10
  app.mount('/gamification',gamification)
11
+ app.mount('/Ars',ARS)
12
 
13
 
14
 
gamification/pointLogic.py CHANGED
@@ -93,6 +93,7 @@ def get_all_simple_points_func(userId) -> SimpleIndividualUserLevel:
93
  db = client[db_name]
94
  collection = db[collection_name]
95
  dreamJob = get_dream_job(userId=userId)
 
96
 
97
  point_cursor = collection.find({"userId": userId})
98
  try:
@@ -101,6 +102,17 @@ def get_all_simple_points_func(userId) -> SimpleIndividualUserLevel:
101
  totalPoints = sum([point['numOfPoints'] for point in points_list])
102
  particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
103
 
 
 
 
 
 
 
 
 
 
 
 
104
  points = SimpleIndividualUserLevel(totalpoints=totalPoints,levelName=particularLevelInfo[0].levelName,maxPoints=particularLevelInfo[0].maxPoints,minPoints=particularLevelInfo[0].minPoints,levelNumber=particularLevelInfo[0].levelNumber)
105
  except:
106
  totalPoints = 0
 
93
  db = client[db_name]
94
  collection = db[collection_name]
95
  dreamJob = get_dream_job(userId=userId)
96
+ <<<<<<< HEAD
97
 
98
  point_cursor = collection.find({"userId": userId})
99
  try:
 
102
  totalPoints = sum([point['numOfPoints'] for point in points_list])
103
  particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
104
 
105
+ =======
106
+ print(dreamJob)
107
+ point_cursor = collection.find({"userId": userId})
108
+ try:
109
+ points_list = list(point_cursor)
110
+
111
+ totalPoints = sum([point['numOfPoints'] for point in points_list])
112
+
113
+ particularLevelInfo = get_particular_level(dreamJob=dreamJob,totalPoints=totalPoints)
114
+ print(particularLevelInfo)
115
+ >>>>>>> master
116
  points = SimpleIndividualUserLevel(totalpoints=totalPoints,levelName=particularLevelInfo[0].levelName,maxPoints=particularLevelInfo[0].maxPoints,minPoints=particularLevelInfo[0].minPoints,levelNumber=particularLevelInfo[0].levelNumber)
117
  except:
118
  totalPoints = 0
requirements.txt CHANGED
@@ -2,6 +2,10 @@ fastapi[all]
2
  requests
3
  python-dotenv
4
  pymupdf
 
 
 
 
5
  pinecone
6
  sentence-transformers
7
  einops
 
2
  requests
3
  python-dotenv
4
  pymupdf
5
+ <<<<<<< HEAD
6
+ =======
7
+ motor
8
+ >>>>>>> master
9
  pinecone
10
  sentence-transformers
11
  einops