shaileshjadhavSS commited on
Commit
53f9c43
·
1 Parent(s): f7c75d0

Added local and AWS functionality for questions

Browse files
.gitattributes DELETED
@@ -1,35 +0,0 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ __pycache__/
__init__.py ADDED
File without changes
app.py CHANGED
@@ -1,174 +1,59 @@
1
- import streamlit as st
2
- import requests
3
  import os
4
- from datetime import datetime
 
 
 
 
5
 
6
- import requests
7
- import json
8
 
9
  # Slack Webhook
10
  SLACK_WEBHOOK_URL = os.environ["SLACK_WEBHOOK_URL"]
11
-
12
-
13
- def send_candidate_info(name, email, years_of_experience, skills, performance_score=None):
14
- # Prepare the candidate details message
15
- candidate_message = {
16
- "text": f"Round - 1\nCandidate Name - {name}\nEmail: {email}\nTotal experience - {years_of_experience} years\nSkills: {skills}\nPerformance Score: {performance_score}"
17
- }
18
-
19
- # Send the candidate details message
20
- response = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(candidate_message), headers={'Content-Type': 'application/json'})
21
-
22
- if response.status_code == 200:
23
- print("Candidate info sent successfully!")
24
- else:
25
- print(f"Failed to send candidate info: {response.status_code}, {response.text}")
26
-
27
 
28
  def call():
29
- # Store the start time if it's the first time
30
- if 'start_time' not in st.session_state:
31
- st.session_state['start_time'] = datetime.now()
32
-
33
  # Define questions
34
- questions = [
35
- {
36
- "question": "What is 2+2?",
37
- "option1": "3",
38
- "option2": "4",
39
- "option3": "5",
40
- "option4": "6",
41
- "answer": "4",
42
- "importance": "high"
43
- },
44
- {
45
- "question": "What is 3*6?",
46
- "option1": "12",
47
- "option2": "18",
48
- "option3": "21",
49
- "option4": "24",
50
- "answer": "18",
51
- "importance": "low"
52
- },
53
- {
54
- "question": "What is 8/2?",
55
- "option1": "2",
56
- "option2": "3",
57
- "option3": "4",
58
- "option4": "5",
59
- "answer": "4",
60
- "importance": "medium"
61
- },
62
- {
63
- "question": "What is 5-3?",
64
- "option1": "1",
65
- "option2": "2",
66
- "option3": "3",
67
- "option4": "4",
68
- "answer": "2",
69
- "importance": "high"
70
- }
71
- ]
72
-
73
- marks = {
74
- "high": 3,
75
- "medium": 2,
76
- "low": 1
77
- }
78
-
79
  score = 0
80
  total_questions = len(questions)
81
 
82
- # Create a progress bar
83
- progress_bar = st.progress(0)
84
-
85
- # Time logic for countdown
86
- start_time = st.session_state['start_time']
87
- elapsed_time = datetime.now() - start_time
88
- remaining_time = max(0, 300 - int(elapsed_time.total_seconds())) # 5 minutes = 300 seconds
89
- minutes, seconds = divmod(remaining_time, 60)
90
- st.text(f"Time Remaining: {minutes:02}:{seconds:02}")
91
-
92
  for idx, question in enumerate(questions):
93
  # Section for each question with styling
94
- st.markdown(f"### Question {idx + 1}: {question['question']}")
95
- selected_option = st.radio(
96
- "",
97
- [question['option1'], question['option2'], question['option3'], question['option4']],
98
- key=f"q{idx}",
99
- label_visibility="collapsed",
100
- help="Choose the correct answer"
101
- )
102
 
103
  # Checking for correct answer and assigning points based on importance
104
  if selected_option == question['answer']:
105
  score += 1
106
 
107
- # Update the progress bar after each question
108
- progress_bar.progress((idx + 1) / total_questions)
109
 
110
- if st.button("Submit Test", use_container_width=True):
111
  st.session_state['test_started'] = False
112
- st.success(f"Test completed!")
113
- send_candidate_info(st.session_state['name'], st.session_state['email'], st.session_state['experience'], st.session_state['technology'], (score/total_questions)*100)
114
 
115
 
116
  def main():
117
  # Set page config with custom title and layout
118
  st.set_page_config(page_title="Candidate MCQ Platform", layout="wide")
119
-
120
- # Display logo and header in the top section
121
- st.markdown("""
122
- <style>
123
- .header {
124
- text-align: center;
125
- font-size: 36px;
126
- font-weight: bold;
127
- margin-top: 20px;
128
- color: #059D66;
129
- }
130
- .logo {
131
- display: block;
132
- margin: 0 auto;
133
- max-width: 100px;
134
- height: auto;
135
- }
136
- </style>
137
- <div class="header">
138
- <img src="https://framerusercontent.com/images/QNgS2NOAyBozR6eFdgzSFhJRbY.png?scale-down-to=512" class="logo" />
139
- Candidate Assessment Platform
140
- </div>
141
- """, unsafe_allow_html=True)
142
 
143
  if 'test_started' not in st.session_state:
144
  st.session_state['test_started'] = False
145
 
146
  if not st.session_state['test_started']:
147
  st.title("Welcome to the Candidate Assessment Platform")
148
- with st.form("signup_form"):
149
- st.header("Sign Up")
150
- name = st.text_input("Full Name", help="Please enter your full name")
151
- email = st.text_input("Email", help="Please enter your email")
152
- experience = st.slider("Total Years of Experience", 0, 20, 0, help="Select your years of experience")
153
- technology = st.selectbox("Technology", ["Python", "Django", "RoR", "NodeJS", "React", "Flutter"])
154
-
155
- # Validation to ensure inputs are provided
156
- signup_submit = st.form_submit_button("Start Test")
157
- if signup_submit:
158
- if not name or not email:
159
- st.error("Name and Email are required! Please fill both fields.")
160
- else:
161
- st.session_state['test_started'] = True
162
- st.session_state['name'] = name
163
- st.session_state['email'] = email
164
- st.session_state['experience'] = experience
165
- st.session_state['technology'] = technology
166
- st.header("Instructions")
167
- st.info("""
168
- 1. You have 30 minutes to complete the test.
169
- 2. Each question is multiple-choice.
170
- 3. Your results will be shared upon completion.
171
- """)
172
  else:
173
  call()
174
 
 
 
 
1
  import os
2
+ import streamlit as st
3
+ from settings import BASE_DIR, NUMBER_OF_TECHNICAL_QUESTIONS, NUMBER_OF_COMMON_QUESTIONS
4
+ from core.slack_notifier import SlackNotifier
5
+ from core.questions_loader_local import QuestionLoaderLocal
6
+ from presentation.layout import Layout
7
 
8
+ import streamlit as st
9
+
10
 
11
  # Slack Webhook
12
  SLACK_WEBHOOK_URL = os.environ["SLACK_WEBHOOK_URL"]
13
+ layout = Layout()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  def call():
 
 
 
 
16
  # Define questions
17
+ questions = QuestionLoaderLocal(os.path.join(BASE_DIR, "questions", st.session_state['technology'].lower(), "questions.csv"), NUMBER_OF_TECHNICAL_QUESTIONS).fetch_questions()
18
+ common_questions = QuestionLoaderLocal(os.path.join(BASE_DIR, "questions", "common", "questions.csv"), NUMBER_OF_COMMON_QUESTIONS).fetch_questions()
19
+ questions.extend(common_questions)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  score = 0
21
  total_questions = len(questions)
22
 
 
 
 
 
 
 
 
 
 
 
23
  for idx, question in enumerate(questions):
24
  # Section for each question with styling
25
+ selected_option = layout.render_test_question(question, idx)
 
 
 
 
 
 
 
26
 
27
  # Checking for correct answer and assigning points based on importance
28
  if selected_option == question['answer']:
29
  score += 1
30
 
 
 
31
 
32
+ if st.button("Submit Test", use_container_width=True, type="primary"):
33
  st.session_state['test_started'] = False
34
+ layout.render_completion_message(score, total_questions)
35
+ SlackNotifier(SLACK_WEBHOOK_URL).send_candidate_info(st.session_state['name'], st.session_state['email'], st.session_state['experience'], st.session_state['technology'], (score/total_questions)*100)
36
 
37
 
38
  def main():
39
  # Set page config with custom title and layout
40
  st.set_page_config(page_title="Candidate MCQ Platform", layout="wide")
41
+ layout.render_header()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  if 'test_started' not in st.session_state:
44
  st.session_state['test_started'] = False
45
 
46
  if not st.session_state['test_started']:
47
  st.title("Welcome to the Candidate Assessment Platform")
48
+ name, email, experience, technology, submit = layout.render_signup_form()
49
+ if name and email:
50
+ st.session_state['name'] = name
51
+ st.session_state['email'] = email
52
+ st.session_state['experience'] = experience
53
+ st.session_state['technology'] = technology
54
+ layout.render_instructions()
55
+ if submit:
56
+ st.session_state['test_started'] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  else:
58
  call()
59
 
core/__init__.py ADDED
File without changes
core/questions_loader_aws.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import boto3
2
+ import csv
3
+ from botocore.exceptions import NoCredentialsError, PartialCredentialsError
4
+
5
+ class QuestionLoaderAWS:
6
+ def __init__(self, bucket_name, aws_access_key, aws_secret_key):
7
+ """
8
+ Initializes the QuestionLoader with AWS credentials and bucket name.
9
+
10
+ :param bucket_name: The name of the S3 bucket.
11
+ :param aws_access_key: AWS access key ID.
12
+ :param aws_secret_key: AWS secret access key.
13
+ """
14
+ self.bucket_name = bucket_name
15
+ self.s3_client = boto3.client(
16
+ 's3',
17
+ aws_access_key_id=aws_access_key,
18
+ aws_secret_access_key=aws_secret_key
19
+ )
20
+
21
+ def fetch_questions(self, technology):
22
+ """
23
+ Fetches the questions for the given technology from the S3 bucket.
24
+
25
+ :param technology: The technology (e.g., Python, Django) to fetch questions for.
26
+ :return: A list of dictionaries, where each dictionary represents a question.
27
+ :raises: Exception if the file cannot be fetched or read.
28
+ """
29
+ file_key = f"questions/{technology}/questions.csv"
30
+
31
+ try:
32
+ response = self.s3_client.get_object(Bucket=self.bucket_name, Key=file_key)
33
+ questions = []
34
+
35
+ # Decode and parse the CSV content
36
+ lines = response['Body'].read().decode('utf-8').splitlines()
37
+ csv_reader = csv.DictReader(lines)
38
+ for row in csv_reader:
39
+ questions.append({
40
+ "question": row["question"],
41
+ "option1": row["option1"],
42
+ "option2": row["option2"],
43
+ "option3": row["option3"],
44
+ "option4": row["option4"],
45
+ "answer": row["answer"],
46
+ "importance": row["importance"].lower()
47
+ })
48
+
49
+ return questions
50
+
51
+ except self.s3_client.exceptions.NoSuchKey:
52
+ raise FileNotFoundError(f"No questions found for technology: {technology}")
53
+
54
+ except (NoCredentialsError, PartialCredentialsError):
55
+ raise PermissionError("Invalid AWS credentials. Please check your .env file.")
56
+
57
+ except Exception as e:
58
+ raise RuntimeError(f"Failed to fetch questions: {str(e)}")
core/questions_loader_dummy.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class QuestionLoaderDummy:
2
+ def __init__(self):
3
+ """
4
+ Initializes the QuestionLoader with Dummy data.
5
+ """
6
+ pass
7
+
8
+ def fetch_questions(self, technology=None):
9
+ """
10
+ Fetches the questions for the given technology from the S3 bucket.
11
+
12
+ :param technology: The technology (e.g., Python, Django) to fetch questions for.
13
+ :return: A list of dictionaries, where each dictionary represents a question.
14
+ :raises: Exception if the file cannot be fetched or read.
15
+ """
16
+ return [
17
+ {
18
+ "question": "What is 2+2?",
19
+ "option1": "3",
20
+ "option2": "4",
21
+ "option3": "5",
22
+ "option4": "6",
23
+ "answer": "4",
24
+ "importance": "high"
25
+ },
26
+ {
27
+ "question": "What is 3*6?",
28
+ "option1": "12",
29
+ "option2": "18",
30
+ "option3": "21",
31
+ "option4": "24",
32
+ "answer": "18",
33
+ "importance": "low"
34
+ },
35
+ {
36
+ "question": "What is 8/2?",
37
+ "option1": "2",
38
+ "option2": "3",
39
+ "option3": "4",
40
+ "option4": "5",
41
+ "answer": "4",
42
+ "importance": "medium"
43
+ },
44
+ {
45
+ "question": "What is 5-3?",
46
+ "option1": "1",
47
+ "option2": "2",
48
+ "option3": "3",
49
+ "option4": "4",
50
+ "answer": "2",
51
+ "importance": "high"
52
+ }
53
+ ]
core/questions_loader_local.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import csv
2
+ import os
3
+ import random
4
+
5
+ class QuestionLoaderLocal:
6
+ def __init__(self, file_path, question_count):
7
+ """
8
+ Initializes the QuestionLoader with a base path for local files.
9
+
10
+ :param base_path: The base path where the question files are located.
11
+ """
12
+ self.base_path = "candidate_assesment/data"
13
+ self.question_count = question_count
14
+ self.file_path = file_path
15
+
16
+ def fetch_questions(self):
17
+ """
18
+ Fetches the questions for the given technology from the local file system.
19
+
20
+ :param technology: The technology (e.g., Python, Django) to fetch questions for.
21
+ :return: A list of dictionaries, where each dictionary represents a question.
22
+ :raises: Exception if the file cannot be fetched or read.
23
+ """
24
+ # file_path = os.path.join(BASE_DIR, "questions", technology, "questions.csv")
25
+ if not os.path.exists(self.file_path):
26
+ raise FileNotFoundError(f"No questions found for technology")
27
+
28
+ try:
29
+ questions = []
30
+
31
+ # Read and parse the CSV file
32
+ with open(self.file_path, mode="r", encoding="utf-8") as file:
33
+ csv_reader = csv.DictReader(file)
34
+ for row in csv_reader:
35
+ questions.append({
36
+ "question": row["question"],
37
+ "option1": row["option1"],
38
+ "option2": row["option2"],
39
+ "option3": row["option3"],
40
+ "option4": row["option4"],
41
+ "answer": row["answer"],
42
+ "importance": row["importance"].lower()
43
+ })
44
+ # Randomly select 20 questions
45
+ sampled_questions = random.sample(questions, min(self.question_count, len(questions)))
46
+ return sampled_questions
47
+
48
+ except Exception as e:
49
+ raise RuntimeError(f"Failed to fetch questions: {str(e)}")
core/slack_notifier.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import requests
3
+
4
+ class SlackNotifier:
5
+ def __init__(self, webhook_url):
6
+ """
7
+ Initializes the SlackNotifier with the webhook URL.
8
+
9
+ :param webhook_url: The Slack webhook URL for sending messages.
10
+ """
11
+ self.webhook_url = webhook_url
12
+
13
+ def send_message(self, text):
14
+ """
15
+ Sends a plain text message to Slack.
16
+
17
+ :param text: The message to send.
18
+ :raises: Exception if the message fails to send.
19
+ """
20
+ payload = {"text": text}
21
+
22
+ response = requests.post(
23
+ self.webhook_url,
24
+ data=json.dumps(payload),
25
+ headers={"Content-Type": "application/json"}
26
+ )
27
+
28
+ if response.status_code != 200:
29
+ raise RuntimeError(f"Failed to send message to Slack: {response.status_code}, {response.text}")
30
+
31
+ def send_candidate_info(self, name, email, years_of_experience, skills, performance_score=None):
32
+ """
33
+ Sends a formatted message with candidate information to Slack.
34
+
35
+ :param name: Candidate's name.
36
+ :param email: Candidate's email.
37
+ :param years_of_experience: Total years of experience.
38
+ :param skills: Candidate's skills.
39
+ :param performance_score: Candidate's performance score (optional).
40
+ """
41
+ message = (
42
+ f"*Candidate Assessment Report*\n\n"
43
+ f"*Name:* {name}\n"
44
+ f"*Email:* {email}\n"
45
+ f"*Experience:* {years_of_experience} years\n"
46
+ f"*Skills:* {skills}\n"
47
+ f"*Performance Score:* {performance_score if performance_score is not None else 'N/A'}\n"
48
+ )
49
+
50
+ self.send_message(message)
core/utils.py ADDED
File without changes
presentation/__init__.py ADDED
File without changes
presentation/layout.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from settings import TECHNOLOGIES_LIST
3
+ class Layout:
4
+ @staticmethod
5
+ def render_header():
6
+ """
7
+ Renders the header of the application with a logo and title.
8
+ """
9
+ st.markdown(
10
+ """
11
+ <style>
12
+ .header {
13
+ text-align: center;
14
+ font-size: 28px; /* Reduced size */
15
+ font-weight: 600; /* Slightly lighter font weight */
16
+ color: #034732; /* Darker green for a more professional look */
17
+ margin-top: 15px;
18
+ margin-bottom: 5px;
19
+ }
20
+ .sub-header {
21
+ text-align: center;
22
+ font-size: 14px; /* Subtle and smaller font */
23
+ color: #5E6472; /* Muted grayish-blue for contrast */
24
+ margin-bottom: 20px;
25
+ }
26
+ .logo {
27
+ display: block;
28
+ margin: 0 auto;
29
+ max-width: 60px; /* Smaller logo size */
30
+ height: auto;
31
+ margin-bottom: 10px;
32
+ filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25)); /* Added subtle shadow */
33
+ }
34
+ .divider {
35
+ height: 1px;
36
+ margin: 20px 0;
37
+ background-color: #D0D0D0; /* Light gray for a clean divider */
38
+ border: none;
39
+ }
40
+ </style>
41
+ <div>
42
+ <img src="https://framerusercontent.com/images/QNgS2NOAyBozR6eFdgzSFhJRbY.png?scale-down-to=512" class="logo" />
43
+ <div class="header">NonStop Assessments</div>
44
+ <div class="sub-header">Empowering talent, one question at a time.</div>
45
+ <hr class="divider">
46
+ </div>
47
+ """,
48
+ unsafe_allow_html=True
49
+ )
50
+
51
+
52
+ @staticmethod
53
+ def render_signup_form():
54
+ """
55
+ Renders the signup form for candidate details.
56
+
57
+ :return: Tuple containing name, email, experience, and selected technology.
58
+ """
59
+ with st.form("signup_form"):
60
+ st.header("Sign Up")
61
+
62
+ name = st.text_input("Full Name", help="Please enter your full name")
63
+ email = st.text_input("Email", help="Please enter your email")
64
+ experience = st.slider("Total Years of Experience", 0, 20, 0, help="Select your years of experience")
65
+ technology = st.selectbox("Technology", TECHNOLOGIES_LIST, help="Select your primary technology")
66
+ submit = st.form_submit_button("Start Test")
67
+
68
+ if submit:
69
+ if not name or not email:
70
+ st.error("Name and Email are required! Please fill both fields.")
71
+ return None, None, None, None, None
72
+ return name, email, experience, technology, submit
73
+
74
+ return None, None, None, None, None
75
+
76
+ @staticmethod
77
+ def render_instructions():
78
+ """
79
+ Renders test instructions for the candidates.
80
+ """
81
+ st.header("Instructions")
82
+ st.info(
83
+ """
84
+ 1. You have 30 minutes to complete the test.
85
+ 2. Each question is multiple-choice.
86
+ 3. Your results will be shared upon completion.
87
+ """
88
+ )
89
+
90
+ @staticmethod
91
+ def render_progress_bar(current, total):
92
+ """
93
+ Renders a progress bar for the test.
94
+
95
+ :param current: Current progress (integer).
96
+ :param total: Total progress (integer).
97
+ """
98
+ st.progress(current / total)
99
+
100
+ @staticmethod
101
+ def render_test_question(question, idx):
102
+ """
103
+ Renders a single test question with multiple-choice options.
104
+
105
+ :param question: Dictionary containing question details.
106
+ :param idx: Index of the question for unique Streamlit keys.
107
+ :return: Selected option from the user.
108
+ """
109
+ st.markdown(f"""
110
+ <div class="question-card">
111
+ <b>Question {idx + 1}:</b> {question['question']}
112
+ </div>
113
+ """,
114
+ unsafe_allow_html=True,
115
+ )
116
+ selected_option = st.radio(
117
+ "",
118
+ [question['option1'], question['option2'], question['option3'], question['option4']],
119
+ key=f"q{idx}",
120
+ label_visibility="collapsed",
121
+ help="Choose the correct answer"
122
+ )
123
+ st.markdown("---")
124
+ return selected_option
125
+
126
+ @staticmethod
127
+ def render_completion_message(score, total):
128
+ """
129
+ Displays a completion message after the test is submitted.
130
+
131
+ :param score: Total score obtained by the candidate.
132
+ :param total: Total questions in the test.
133
+ """
134
+ st.success(f"Test completed! Your score: {score}/{total}")
questions/ai/questions.csv ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ question,option1,option2,option3,option4,answer,importance
2
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
3
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
4
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
5
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
6
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
7
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
8
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
9
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
10
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
11
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
12
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
13
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
14
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
15
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
16
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
17
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
18
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
19
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
20
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
21
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
22
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
23
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
24
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
25
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
26
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
27
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
28
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
29
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
30
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
31
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
32
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
33
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
34
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
35
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
36
+ Question X,Option A,Option B,Option C,Option D,Answer,medium
questions/common/questions.csv ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ question,option1,option2,option3,option4,answer,importance
2
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
3
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
4
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
5
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
6
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
7
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
8
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
9
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
10
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
11
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
12
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
13
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
14
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
15
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
16
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
17
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
18
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
19
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
20
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
21
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
22
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
23
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
24
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
25
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
26
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
27
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
28
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
29
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
30
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
31
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
32
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
33
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
34
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
35
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
36
+ Common Question,Option A,Option B,Option C,Option D,Answer,medium
questions/python/questions.csv ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ question,option1,option2,option3,option4,answer,importance
2
+ What is the output of: print(type([]))?,list,tuple,dict,set,list,low
3
+ Which keyword is used to define a function in Python?,fun,function,def,define,def,low
4
+ What does the len() function do?,Returns length of a sequence,Returns a list,Converts string to list,Creates a dictionary,Returns length of a sequence,low
5
+ How do you declare a variable in Python?,var x = 10,x = 10,int x = 10,declare x = 10,x = 10,low
6
+ Which of the following is a valid Python data type?,integer,float,string,All of the above,All of the above,low
7
+ What is the output of: print(3 * 'Python')?,PythonPythonPython,Error,Python3,None,PythonPythonPython,medium
8
+ Which method is used to add an element to a set?,add(),append(),insert(),extend(),add(),medium
9
+ What is the output of: print(5 // 2)?,2.5,2,3,Error,2,medium
10
+ What is the correct syntax to create a dictionary?,{key: value},[key: value],(key: value),key: value,{key: value},medium
11
+ How do you handle exceptions in Python?,try-catch,try-finally,try-except,try-else,try-except,medium
12
+ What is a Python decorator?,A function returning another function,A type of module,A Python class,A data structure,A function returning another function,high
13
+ What is the purpose of the 'with' statement?,Memory management,Simplify exception handling,File handling and cleanup,All of the above,All of the above,high
14
+ What is the time complexity of accessing an element in a dictionary?,O(1),O(n),O(log n),O(n^2),O(1),high
15
+ Which module is used for multithreading in Python?,multiprocessing,threading,os,subprocess,threading,high
16
+ What is the output of: print([i**2 for i in range(3)])?,"[0, 1, 4]","[1, 4, 9]","[0, 2, 4]","[1, 2, 3]","[0, 1, 4]",high
settings.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ BASE_DIR = Path(__file__).resolve().parent
4
+ NUMBER_OF_TECHNICAL_QUESTIONS = os.environ.get("NUMBER_OF_QUESTIONS", 20)
5
+ NUMBER_OF_COMMON_QUESTIONS = os.environ.get("NUMBER_OF_COMMON_QUESTIONS", 10)
6
+ TECHNOLOGIES = os.environ.get("TECHNOLOGIES", "Python,Django,RoR,NodeJS,React,Flutter,QA,Java,Data Engineering,AI")
7
+ TECHNOLOGIES_LIST = TECHNOLOGIES.split(",")