ScienceTest / app.py
capradeepgujaran's picture
Update app.py
46cf72d verified
raw
history blame
65.1 kB
import gradio as gr
from groq import Groq
import os
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from datetime import datetime
import json
import tempfile
from typing import List, Dict, Tuple, Optional
from dataclasses import dataclass
import subprocess
import re
import random
@dataclass
class Question:
question: str
options: List[str]
correct_answer: int
@dataclass
class QuizFeedback:
is_correct: bool
selected: Optional[str]
correct_answer: str
class QuizGenerator:
def __init__(self, api_key: str):
self.client = Groq(api_key=api_key)
def generate_questions(self, text: str, num_questions: int) -> List[Question]:
"""Generate quiz questions using gemma2-9b-it"""
prompt = self._create_prompt(text, num_questions)
try:
response = self.client.chat.completions.create(
messages=[
{
"role": "system",
"content": "You are a quiz generator. Generate multiple choice questions that are clear and focused."
},
{
"role": "user",
"content": prompt
}
],
model="gemma2-9b-it",
temperature=0,
max_tokens=6000
)
# Extract content safely
content = response.choices[0].message.content
if not content:
raise ValueError("Empty response content")
# Parse and validate questions
questions = self._parse_response(content)
validated = self._validate_questions(questions, num_questions)
# Check if we have exactly the requested number of questions
if len(validated) < num_questions:
# Generate additional questions if needed
additional_needed = num_questions - len(validated)
additional_questions = self.generate_questions(text, additional_needed)
validated.extend(additional_questions)
elif len(validated) > num_questions:
# Trim to exact number needed
validated = validated[:num_questions]
if len(validated) != num_questions:
raise ValueError(f"Failed to generate exactly {num_questions} questions")
return validated
except Exception as e:
print(f"Error in generate_questions: {str(e)}")
if 'response' in locals():
print("Response content:", content if 'content' in locals() else None)
raise QuizGenerationError(f"Failed to generate questions: {str(e)}")
def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
"""Validate questions with strict checking and ensure correct number"""
validated = []
for q in questions:
try:
# Skip invalid questions
if not isinstance(q, dict):
continue
# Check required fields
if not all(key in q for key in ['question', 'options', 'correct_answer']):
continue
# Validate options
if not isinstance(q['options'], list) or len(q['options']) != 4:
continue
# Validate correct_answer
try:
correct_idx = int(q['correct_answer'])
if not 0 <= correct_idx <= 3:
continue
except (ValueError, TypeError):
continue
# Validate question content
if not q['question'].strip() or any(not opt.strip() for opt in q['options']):
continue
# Create validated Question object
validated.append(Question(
question=str(q['question']).strip(),
options=[str(opt).strip() for opt in q['options']],
correct_answer=correct_idx
))
except Exception as e:
print(f"Validation error: {str(e)}")
continue
# Stop if we have enough validated questions
if len(validated) == num_questions:
break
return validated
def _create_prompt(self, text: str, num_questions: int) -> str:
"""Create a simple, clear prompt optimized for gemma2-9b-it with emphasis on exact number"""
return f"""Create exactly {num_questions} multiple choice questions about this text. Return only the JSON array in this exact format:
[
{{
"question": "Write the question here?",
"options": [
"First option",
"Second option",
"Third option",
"Fourth option"
],
"correct_answer": 0
}}
]
Rules:
1. Return only the JSON array
2. Generate exactly {num_questions} questions
3. Each question must have exactly 4 options
4. correct_answer must be 0, 1, 2, or 3
5. No explanations or additional text
Text to use:
{text.strip()}"""
def _parse_response(self, response_text: str) -> List[Dict]:
"""Parse response with improved error handling"""
try:
# Clean up the response text
cleaned = response_text.strip()
# Remove any markdown formatting
cleaned = cleaned.replace('```json', '').replace('```', '').strip()
# Find the JSON array
start = cleaned.find('[')
end = cleaned.rfind(']') + 1
if start == -1 or end == 0:
raise ValueError("No JSON array found in response")
json_str = cleaned[start:end]
# Remove any trailing commas before closing brackets
json_str = re.sub(r',(\s*})', r'\1', json_str)
json_str = re.sub(r',(\s*])', r'\1', json_str)
# Try to parse the cleaned JSON
try:
return json.loads(json_str)
except json.JSONDecodeError:
# If that fails, try using ast.literal_eval as a fallback
import ast
return ast.literal_eval(json_str)
except Exception as e:
print(f"Parse error details: {str(e)}")
print(f"Attempted to parse: {response_text}")
# Last resort: try to fix the JSON manually
try:
# Remove any trailing commas and fix newlines
fixed = re.sub(r',(\s*[}\]])', r'\1', response_text)
fixed = fixed.replace('}\n{', '},{')
fixed = fixed.strip()
if not fixed.startswith('['):
fixed = '[' + fixed
if not fixed.endswith(']'):
fixed = fixed + ']'
return json.loads(fixed)
except:
raise ValueError(f"Failed to parse response: {str(e)}")
def _validate_questions(self, questions: List[Dict], num_questions: int) -> List[Question]:
"""Validate questions with strict checking"""
validated = []
for q in questions[:num_questions]:
try:
# Skip invalid questions
if not isinstance(q, dict):
continue
# Check required fields
if not all(key in q for key in ['question', 'options', 'correct_answer']):
continue
# Validate options
if not isinstance(q['options'], list) or len(q['options']) != 4:
continue
# Validate correct_answer
try:
correct_idx = int(q['correct_answer'])
if not 0 <= correct_idx <= 3:
continue
except (ValueError, TypeError):
continue
# Create validated Question object
validated.append(Question(
question=str(q['question']).strip(),
options=[str(opt).strip() for opt in q['options']],
correct_answer=correct_idx
))
except Exception as e:
print(f"Validation error: {str(e)}")
continue
return validated
def _is_valid_json(self, json_str: str) -> bool:
"""Check if a string is valid JSON"""
try:
json.loads(json_str)
return True
except:
return False
class FontManager:
"""Manages font installation and loading for the certificate generator"""
@staticmethod
def install_fonts():
"""Install required fonts if they're not already present"""
try:
# Install fonts package
subprocess.run([
"apt-get", "update", "-y"
], check=True)
subprocess.run([
"apt-get", "install", "-y",
"fonts-liberation", # Liberation Sans fonts
"fontconfig", # Font configuration
"fonts-dejavu-core" # DejaVu fonts as fallback
], check=True)
# Clear font cache
subprocess.run(["fc-cache", "-f"], check=True)
print("Fonts installed successfully")
except subprocess.CalledProcessError as e:
print(f"Warning: Could not install fonts: {e}")
except Exception as e:
print(f"Warning: Unexpected error installing fonts: {e}")
@staticmethod
def get_font_paths() -> Dict[str, str]:
"""Get the paths to the required fonts with multiple fallbacks"""
standard_paths = [
"/usr/share/fonts",
"/usr/local/share/fonts",
"/usr/share/fonts/truetype",
"~/.fonts"
]
font_paths = {
'regular': None,
'bold': None
}
# Common font filenames to try
fonts_to_try = {
'regular': [
'LiberationSans-Regular.ttf',
'DejaVuSans.ttf',
'FreeSans.ttf'
],
'bold': [
'LiberationSans-Bold.ttf',
'DejaVuSans-Bold.ttf',
'FreeSans-Bold.ttf'
]
}
def find_font(font_name: str) -> Optional[str]:
"""Search for a font file in standard locations"""
for base_path in standard_paths:
for root, _, files in os.walk(os.path.expanduser(base_path)):
if font_name in files:
return os.path.join(root, font_name)
return None
# Try to find each font
for style in ['regular', 'bold']:
for font_name in fonts_to_try[style]:
font_path = find_font(font_name)
if font_path:
font_paths[style] = font_path
break
# If no fonts found, try using fc-match as fallback
if not all(font_paths.values()):
try:
for style in ['regular', 'bold']:
if not font_paths[style]:
result = subprocess.run(
['fc-match', '-f', '%{file}', 'sans-serif:style=' + style],
capture_output=True,
text=True
)
if result.returncode == 0 and result.stdout.strip():
font_paths[style] = result.stdout.strip()
except Exception as e:
print(f"Warning: Could not use fc-match to find fonts: {e}")
return font_paths
class QuizGenerationError(Exception):
"""Exception raised for errors in quiz generation"""
pass
class CertificateGenerator:
def __init__(self):
self.certificate_size = (1200, 800)
self.background_color = '#FFFFFF'
self.border_color = '#1C1D1F'
# Install fonts if needed
FontManager.install_fonts()
self.font_paths = FontManager.get_font_paths()
def _load_fonts(self) -> Dict[str, ImageFont.FreeTypeFont]:
"""Load fonts with fallbacks"""
fonts = {}
try:
if self.font_paths['regular'] and self.font_paths['bold']:
fonts['title'] = ImageFont.truetype(self.font_paths['bold'], 36)
fonts['subtitle'] = ImageFont.truetype(self.font_paths['regular'], 14)
fonts['text'] = ImageFont.truetype(self.font_paths['regular'], 20)
fonts['name'] = ImageFont.truetype(self.font_paths['bold'], 32)
else:
raise ValueError("No suitable fonts found")
except Exception as e:
print(f"Font loading error: {e}. Using default font.")
default = ImageFont.load_default()
fonts = {
'title': default,
'subtitle': default,
'text': default,
'name': default
}
return fonts
def _add_professional_border(self, draw: ImageDraw.Draw):
"""Add professional border with improved corners"""
padding = 40
border_width = 2
corner_radius = 10
# Draw rounded rectangle border
x0, y0 = padding, padding
x1, y1 = self.certificate_size[0] - padding, self.certificate_size[1] - padding
# Draw corners
draw.arc((x0, y0, x0 + corner_radius * 2, y0 + corner_radius * 2), 180, 270, '#1C1D1F', border_width)
draw.arc((x1 - corner_radius * 2, y0, x1, y0 + corner_radius * 2), 270, 0, '#1C1D1F', border_width)
draw.arc((x0, y1 - corner_radius * 2, x0 + corner_radius * 2, y1), 90, 180, '#1C1D1F', border_width)
draw.arc((x1 - corner_radius * 2, y1 - corner_radius * 2, x1, y1), 0, 90, '#1C1D1F', border_width)
# Draw lines
draw.line((x0 + corner_radius, y0, x1 - corner_radius, y0), '#1C1D1F', border_width) # Top
draw.line((x0 + corner_radius, y1, x1 - corner_radius, y1), '#1C1D1F', border_width) # Bottom
draw.line((x0, y0 + corner_radius, x0, y1 - corner_radius), '#1C1D1F', border_width) # Left
draw.line((x1, y0 + corner_radius, x1, y1 - corner_radius), '#1C1D1F', border_width) # Right
def _add_content(
self,
draw: ImageDraw.Draw,
fonts: Dict[str, ImageFont.FreeTypeFont],
name: str,
course_name: str,
score: float,
y_offset: int = 140
):
"""Add content with adjusted vertical positioning"""
# Add "CERTIFICATE OF COMPLETION" text
draw.text((60, y_offset), "CERTIFICATE OF COMPLETION", font=fonts['subtitle'], fill='#666666')
# Add course name (large and bold)
course_name = course_name.strip() or "Assessment"
draw.text((60, y_offset + 60), course_name, font=fonts['title'], fill='#1C1D1F')
# Add instructor info
draw.text((60, y_offset + 160), "Instructor", font=fonts['subtitle'], fill='#666666')
draw.text((60, y_offset + 190), "QHSE Manager", font=fonts['text'], fill='#1C1D1F')
# Add participant name (large)
name = name.strip() or "Participant"
draw.text((60, y_offset + 280), name, font=fonts['name'], fill='#1C1D1F')
# Add date and score info
date_str = datetime.now().strftime("%b. %d, %Y")
# Date section
draw.text((60, y_offset + 360), "Date", font=fonts['subtitle'], fill='#666666')
draw.text((60, y_offset + 390), date_str, font=fonts['text'], fill='#1C1D1F')
# Score section
draw.text((300, y_offset + 360), "Score", font=fonts['subtitle'], fill='#666666')
draw.text((300, y_offset + 390), f"{float(score):.1f}%", font=fonts['text'], fill='#1C1D1F')
# Footer section
certificate_id = f"Certificate no: {datetime.now().strftime('%Y%m%d')}-{abs(hash(name)) % 10000:04d}"
ref_number = f"Reference Number: {abs(hash(name + date_str)) % 10000:04d}"
draw.text((60, 720), certificate_id, font=fonts['subtitle'], fill='#666666')
draw.text((1140, 720), ref_number, font=fonts['subtitle'], fill='#666666', anchor="ra")
def _add_logo(self, certificate: Image.Image, logo_path: str):
try:
logo = Image.open(logo_path)
# Resize logo to appropriate size
logo.thumbnail((150, 80))
# Position in top-left corner with padding
certificate.paste(logo, (60, 50), mask=logo if 'A' in logo.getbands() else None)
except Exception as e:
print(f"Error adding logo: {e}")
def _add_photo(self, certificate: Image.Image, photo_path: str):
"""Add a clear circular profile photo in the top-right corner with adjusted position"""
try:
if not photo_path or not os.path.exists(photo_path):
print(f"Photo path does not exist: {photo_path}")
return
# Open and process photo
photo = Image.open(photo_path)
# Define size for circular photo
size = (120, 120)
# Convert to RGB if not already
if photo.mode not in ('RGB', 'RGBA'):
photo = photo.convert('RGB')
# Create high-quality circular mask
mask = Image.new('L', size, 0)
draw = ImageDraw.Draw(mask)
draw.ellipse((0, 0, size[0], size[1]), fill=255)
# Resize photo maintaining aspect ratio
aspect = photo.width / photo.height
if aspect > 1:
new_height = size[1]
new_width = int(new_height * aspect)
else:
new_width = size[0]
new_height = int(new_width / aspect)
photo = photo.resize((new_width, max(new_height, 1)), Image.Resampling.LANCZOS)
# Center crop
if aspect > 1:
left = (new_width - size[0]) // 2
photo = photo.crop((left, 0, left + size[0], size[1]))
else:
top = (new_height - size[1]) // 2
photo = photo.crop((0, top, size[0], top + size[1]))
# Create circular photo
output = Image.new('RGBA', size, (0, 0, 0, 0))
output.paste(photo, (0, 0))
output.putalpha(mask)
# Adjusted position - moved down from top
photo_x = certificate.width - size[0] - 60 # 60px from right
photo_y = 50 # Increased from 40 to 50px from top
# Add white background circle
bg = Image.new('RGBA', size, (255, 255, 255, 255))
certificate.paste(bg, (photo_x, photo_y), mask=mask)
# Paste the photo
certificate.paste(output, (photo_x, photo_y), mask=output)
print(f"Successfully added photo at position ({photo_x}, {photo_y})")
except Exception as e:
print(f"Error adding photo: {str(e)}")
import traceback
traceback.print_exc()
def generate(
self,
score: float,
name: str,
course_name: str,
company_logo: Optional[str] = None,
participant_photo: Optional[str] = None
) -> str:
"""Generate certificate with improved photo handling"""
try:
# Create base certificate
certificate = Image.new('RGB', self.certificate_size, self.background_color)
draw = ImageDraw.Draw(certificate)
# Add border
self._add_professional_border(draw)
# Load fonts
fonts = self._load_fonts()
# Add company logo if provided
if company_logo and os.path.exists(company_logo):
self._add_logo(certificate, company_logo)
# Add participant photo if provided
if participant_photo:
print(f"Processing photo: {participant_photo}") # Debug info
self._add_photo(certificate, participant_photo)
# Add content
self._add_content(draw, fonts, str(name), str(course_name), float(score))
# Save certificate
return self._save_certificate(certificate)
except Exception as e:
print(f"Error generating certificate: {str(e)}")
import traceback
traceback.print_exc()
return None
def _create_base_certificate(self) -> Image.Image:
"""Create base certificate with improved background"""
# Create base image
certificate = Image.new('RGB', self.certificate_size, self.background_color)
# Add subtle gradient background (optional)
draw = ImageDraw.Draw(certificate)
# Add very subtle grain texture for professional look (optional)
width, height = certificate.size
for x in range(0, width, 4):
for y in range(0, height, 4):
if random.random() > 0.5:
draw.point((x, y), fill=(250, 250, 250))
return certificate
def _save_certificate(self, certificate: Image.Image) -> str:
"""Save certificate with improved error handling"""
try:
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
certificate.save(temp_file.name, 'PNG', quality=95)
print(f"Certificate saved to: {temp_file.name}") # Debug info
return temp_file.name
except Exception as e:
print(f"Error saving certificate: {str(e)}")
return None
class QuizApp:
def __init__(self, api_key: str):
self.quiz_generator = QuizGenerator(api_key)
self.certificate_generator = CertificateGenerator()
self.current_questions: List[Question] = []
self.logo_path = "Logo.png"
self.selected_level = "Basic" # Default level
# Map difficulty levels to number of questions
self.difficulty_levels = {
"Basic": 5,
"Intermediate": 10,
"Advanced": 20
}
# Add fixed content here
self.fixed_content = """Introduction to Construction Safety
The construction industry remains one of the most hazardous sectors in the global workforce, presenting numerous risks to worker safety and health. This comprehensive guide addresses the fundamental aspects of construction site safety, providing detailed insights into best practices, regulatory requirements, and practical implementation strategies. Safety in construction isn't merely a legal obligation—it represents a moral imperative to protect human life and ensure the well-being of every individual working in this challenging environment.
Construction site safety encompasses multiple interconnected elements, from personal protective equipment to emergency response protocols. Success in safety management requires unwavering commitment from all organizational levels, from executive leadership to front-line workers. This guide serves as a foundational resource for construction professionals, safety managers, and workers, offering detailed insights into creating and maintaining a safe working environment.
Personal Protective Equipment (PPE)
Personal protective equipment forms the first line of defense against workplace injuries in construction environments. Every worker on a construction site must understand the importance of proper PPE selection, use, and maintenance. Organizations must establish comprehensive PPE programs addressing specific site hazards while ensuring compliance with applicable regulations and standards.
Head protection serves as a critical component of personal protective equipment in construction environments. Every worker must wear appropriate hard hats meeting ANSI Z89.1 standards whenever present on site. Type I hard hats protect against vertical impact and penetration, while Type II hard hats provide additional protection against lateral impact. Construction supervisors must ensure regular inspection of all hard hats for signs of damage, including cracks, dents, or UV degradation. Workers should replace their hard hats every five years from the manufacture date, or immediately following any significant impact. Proper fitting requires adjustable suspension systems that maintain a one-inch clearance between the shell and the wearer's head.
Eye and face protection requirements address multiple hazards present in construction environments. Basic eye protection begins with safety glasses featuring side shields meeting ANSI Z87.1 standards. Workers performing tasks involving flying particles, such as grinding or cutting operations, must supplement basic eye protection with face shields. Those working outdoors require tinted protection against UV radiation, while chemical handling demands sealed goggles to prevent splash exposure. Organizations must establish and enforce proper cleaning protocols for all eye protection equipment, ensuring clear vision and maximum protection throughout the workday.
Foot protection prevents countless injuries in construction environments through proper selection and use of safety footwear. Workers must wear safety boots meeting ASTM F2413-18 standards, featuring steel or composite toe caps capable of withstanding impacts up to 75 pounds. Safety boots must incorporate puncture-resistant soles protecting against sharp objects and electrical hazard protection appropriate to working conditions. The outer soles require slip-resistant properties with clearly defined tread patterns maintained through regular inspection and replacement. Workers in wet conditions need waterproof options to maintain foot health and prevent discomfort that might lead to unsafe practices.
Hand protection requirements vary based on specific task hazards and working conditions. Cut-resistant gloves meeting ANSI/ISEA 105 standards protect against sharp materials and tools. Impact-resistant gloves incorporate padding to protect against crushing injuries during material handling. Chemical-resistant gloves prevent skin exposure to harmful substances, while electrical work requires insulated gloves meeting appropriate voltage ratings. Organizations must train workers in proper glove selection, inspection, and replacement schedules to maintain protective capabilities.
Respiratory protection becomes necessary when construction activities generate airborne hazards exceeding permissible exposure limits. Organizations must establish comprehensive respiratory protection programs following OSHA Standard 1910.134. Workers exposed to dust and particles require minimum N95-rated filtering facepieces, while chemical exposures may demand half-face or full-face respirators with appropriate cartridges. All workers using respirators must undergo medical evaluation, fit testing, and proper training in use and maintenance. Organizations must maintain detailed records of respirator assignments, cartridge changes, and fit test results.
High-visibility clothing ensures worker visibility in all working conditions, particularly crucial in areas with vehicle traffic or heavy equipment operation. Workers must wear high-visibility clothing meeting ANSI/ISEA 107 standards, with Class 2 garments sufficing for daytime work and moderate traffic conditions, while Class 3 garments become necessary in high-traffic areas or low-light conditions. Reflective materials must maintain their properties through regular cleaning and inspection. Organizations should implement color-coding systems differentiating various worker roles while maintaining minimum visibility requirements.
Fall Protection Systems
Fall protection represents one of the most critical aspects of construction safety, requiring comprehensive planning and implementation. Organizations must develop written fall protection plans for all work conducted at heights exceeding six feet in construction environments. These plans must address specific fall hazards, protection methods, rescue procedures, and training requirements.
Guardrail systems provide primary fall protection around elevated work areas, requiring proper installation and regular maintenance. Top rails must maintain a height of 42 inches (±3 inches) and withstand a force of 200 pounds applied in any outward or downward direction. Mid-rails positioned at 21 inches provide additional protection, while toe boards preventing falling objects must extend at least 4 inches above the walking surface. Support posts cannot exceed 8-foot spacing intervals, and all components require regular inspection for structural integrity.
Personal fall arrest systems become necessary when guardrail systems prove impractical or insufficient. These systems include full-body harnesses, connectors, and appropriate anchorage points supporting 5,000 pounds per attached worker. Organizations must train workers in proper harness fitting, inspection procedures, and attachment techniques. Regular inspection programs must document the condition of all components, including impact indicators and hardware integrity.
Fall protection programs must address rescue considerations, ensuring prompt response to suspended workers following fall arrest. Rescue plans must detail specific procedures, equipment requirements, and personnel responsibilities. Organizations must conduct regular rescue drills verifying the effectiveness of established procedures and identifying potential improvements.
Equipment and Machinery Safety
Heavy equipment operation demands rigorous safety protocols and continuous monitoring ensuring safe operation. Every piece of heavy equipment requires daily pre-operational inspections following detailed checklists covering all critical systems. Operators must possess appropriate certification and demonstrate competency through practical evaluation before receiving authorization to operate equipment.
Equipment maintenance programs must follow manufacturer specifications, with detailed documentation of all service activities. Organizations must implement lockout/tagout procedures during maintenance operations, ensuring equipment cannot be energized while service work proceeds. Operators must maintain inspection logs recording equipment hours, operating conditions, and any observed abnormalities.
Power tool safety begins with proper selection and maintenance of tools appropriate for specific tasks. Organizations must establish tool inspection programs verifying the condition of guards, power cords, and safety devices before each use. Workers must receive task-specific training covering proper tool operation, recognized hazards, and required protective measures.
Crane operations require comprehensive planning and continuous monitoring to maintain safe working conditions. Organizations must develop detailed lift plans for all crane operations, considering load weights, lift radius, ground conditions, and environmental factors. Qualified riggers must inspect all lifting equipment before use, verifying the condition of hooks, slings, shackles, and other components.
Site Organization and Management
Effective site organization plays a fundamental role in maintaining safe construction operations. Site layout must consider traffic patterns, material storage requirements, and emergency access needs. Organizations must develop comprehensive site plans identifying designated work areas, storage locations, and access routes supporting safe operations while maintaining operational efficiency.
Access control and security measures protect both workers and property while preventing unauthorized site entry. Entry points must feature staffed checkpoints verifying worker identification and site authorization. Visitor management protocols must include safety orientation, protective equipment requirements, and escort procedures when necessary. Organizations must maintain accurate site occupancy records supporting emergency response and evacuation procedures.
Material storage and handling requires careful planning preventing numerous workplace injuries while maintaining site efficiency. Organizations must develop storage plans considering material characteristics, handling requirements, and access needs. Storage areas require clear designation, appropriate lighting, and protection from weather conditions affecting material integrity. Segregation requirements must address chemical compatibility, fire hazard classes, and environmental protection needs.
Traffic management systems prevent vehicle-related accidents while maintaining efficient site operations. Traffic plans must separate vehicle and pedestrian routes wherever possible, implementing physical barriers when separation cannot be maintained. Speed limits, stop signs, and traffic control devices must regulate vehicle movement throughout the site. Loading zones require clear designation, with established procedures for vehicle positioning and personnel exclusion during loading operations.
Housekeeping practices significantly impact site safety through maintenance of clear access ways and work areas. Organizations must establish daily cleaning requirements addressing debris removal, material organization, and maintenance of walking surfaces. Regular inspections must verify compliance with housekeeping standards, identifying potential hazards requiring immediate attention.
Emergency Preparedness and Response
Emergency preparedness requires comprehensive planning addressing various potential emergency scenarios. Organizations must develop written emergency response plans covering fires, medical emergencies, severe weather, and hazardous material releases. These plans must designate response team members, establish clear communication channels, and define specific response procedures for each type of emergency.
Medical response capabilities must match site hazards and workforce size. First aid stations require strategic placement throughout the site, with supplies appropriate to potential injuries. Organizations must ensure sufficient numbers of trained first aid providers on each work shift, maintaining current certification records. Emergency medical equipment, including automated external defibrillators, requires regular inspection and maintenance ensuring operational readiness.
Fire prevention and response programs must address potential fire hazards through comprehensive prevention and response measures. Organizations must develop fire prevention plans identifying potential ignition sources, fuel sources, and required control measures. Hot work permits must regulate operations producing flames, sparks, or excessive heat, with fire watch requirements extending beyond work completion.
Evacuation procedures must address various emergency scenarios requiring site evacuation. Organizations must establish clearly marked evacuation routes, designated assembly areas, and procedures for accounting for all personnel. Regular evacuation drills must verify worker understanding of procedures while identifying potential improvements in evacuation capabilities.
Emergency communication systems must support rapid notification of emergency conditions while maintaining clear communication channels during emergency response. Organizations must establish redundant communication methods ensuring effective communication despite potential system failures. Regular testing must verify system functionality while familiarizing workers with proper operation.
Training and Communication
Training programs form the foundation of effective safety management through development of worker knowledge and skills. Organizations must establish comprehensive training programs addressing site-specific hazards, safety procedures, and emergency response requirements. New worker orientation must provide thorough introduction to site safety requirements before beginning work activities.
Task-specific training must address hazards and safety requirements associated with specific work activities. Workers must demonstrate competency through practical evaluation before performing hazardous tasks independently. Refresher training maintains worker knowledge while addressing identified performance deficiencies or procedure changes.
Safety communication systems must support continuous awareness of safety requirements and potential hazards. Daily pre-task safety meetings must address specific hazards and controls associated with planned work activities. Weekly toolbox talks provide focused discussion of relevant safety topics while maintaining worker engagement in safety programs.
Documentation requirements include detailed records of all training activities, including attendance records, evaluation results, and certification expiration dates. Organizations must maintain training records demonstrating worker qualification for specific tasks while identifying upcoming training requirements maintaining worker certifications.
Health Monitoring and Protection
Occupational health monitoring programs protect workers from long-term health hazards through early identification of potential health impacts. Organizations must establish monitoring programs appropriate to site-specific health hazards, including noise exposure, air quality, and ergonomic factors. Regular monitoring must verify effectiveness of control measures while identifying potential areas requiring additional protection.
Noise monitoring programs must identify areas requiring hearing protection while verifying effectiveness of implemented controls. Organizations must conduct regular noise surveys establishing boundaries of hearing protection requirements. Worker audiometric testing programs monitor potential hearing impacts while verifying effectiveness of hearing conservation measures.
Air quality monitoring addresses both general site conditions and task-specific exposures. Organizations must implement monitoring programs identifying potential exposure to harmful substances while verifying effectiveness of control measures. Documentation requirements include sampling results, equipment calibration records, and worker exposure records.
Ergonomic evaluation programs address potential musculoskeletal injuries through proper task design and work practices. Organizations must evaluate work activities identifying potential ergonomic hazards while implementing appropriate control measures. Worker training must address proper lifting techniques, use of mechanical assists, and early reporting of discomfort.
Legal Compliance and Documentation
Regulatory compliance programs ensure adherence to applicable safety regulations while maintaining required documentation demonstrating compliance. Organizations must establish systems identifying applicable regulations while monitoring regulatory changes affecting safety requirements. Regular compliance audits verify effectiveness of implemented programs while identifying areas requiring improvement.
Documentation systems must support various aspects of safety program implementation while meeting regulatory requirements. Required documentation includes training records, inspection reports, incident investigations, and exposure monitoring results. Organizations must establish document control systems ensuring availability of current procedures while maintaining historical records.
Incident investigation programs identify root causes of accidents while implementing corrective measures preventing recurrence. Organizations must establish investigation procedures ensuring thorough evaluation of all incidents, including near-miss events. Documentation requirements include investigation reports, witness statements, and corrective action tracking.
Risk Assessment and Management
Risk assessment processes identify potential hazards while evaluating adequacy of existing controls. Organizations must implement formal risk assessment procedures addressing planned activities and changing site conditions. Assessment results guide implementation of additional controls while identifying activities requiring increased supervision.
Job hazard analysis procedures evaluate specific tasks identifying potential hazards and necessary control measures. Organizations must conduct analyses for hazardous activities, documenting identified hazards and required controls. Regular review ensures continued adequacy of established controls while identifying potential improvements in work procedures.
Change management programs address safety implications of operational changes through formal evaluation processes. Organizations must establish procedures ensuring safety evaluation of proposed changes before implementation. Documentation requirements include change proposals, risk assessments, and implementation plans.
Continuous Improvement
Safety program effectiveness requires regular evaluation identifying potential improvements while maintaining program relevance. Organizations must establish review processes evaluating various program elements while incorporating lessons learned from incidents and near-misses. Worker feedback mechanisms support program improvement through identification of practical challenges in procedure implementation.
Performance metrics provide objective measurement of safety program effectiveness while identifying trends requiring attention. Organizations must establish appropriate metrics reflecting both leading and lagging indicators of safety performance. Regular review of performance data supports identification of program elements requiring improvement."""
def get_certificate_title(self, base_title: str) -> str:
"""Get certificate title with difficulty level"""
return f"{base_title} - {self.selected_level} Level"
def generate_questions(self, text: str, num_questions: int) -> Tuple[bool, List[Question]]:
"""
Generate quiz questions using the QuizGenerator
Returns (success, questions) tuple
"""
try:
questions = self.quiz_generator.generate_questions(text, num_questions)
self.current_questions = questions
return True, questions
except Exception as e:
print(f"Error generating questions: {e}")
return False, []
def calculate_score(self, answers: List[Optional[str]]) -> Tuple[float, bool, List[QuizFeedback]]:
"""
Calculate the quiz score and generate feedback
Returns (score, passed, feedback) tuple
"""
if not answers or not self.current_questions:
return 0, False, []
feedback = []
correct = 0
for question, answer in zip(self.current_questions, answers):
if answer is None:
feedback.append(QuizFeedback(False, None, question.options[question.correct_answer]))
continue
try:
selected_index = question.options.index(answer)
is_correct = selected_index == question.correct_answer
if is_correct:
correct += 1
feedback.append(QuizFeedback(
is_correct,
answer,
question.options[question.correct_answer]
))
except ValueError:
feedback.append(QuizFeedback(False, answer, question.options[question.correct_answer]))
score = (correct / len(self.current_questions)) * 100
return score, score >= 80, feedback
def update_questions(self, text: str, num_questions: int) -> Tuple[gr.update, gr.update, List[gr.update], List[Question], gr.update]:
"""
Event handler for generating new questions
"""
if not text.strip():
return (
gr.update(value=""),
gr.update(value="⚠️ Please enter some text content to generate questions."),
*[gr.update(visible=False, choices=[]) for _ in range(5)],
[],
gr.update(selected=1)
)
success, questions = self.generate_questions(text, num_questions)
if not success or not questions:
return (
gr.update(value=""),
gr.update(value="❌ Failed to generate questions. Please try again."),
*[gr.update(visible=False, choices=[]) for _ in range(5)],
[],
gr.update(selected=1)
)
# Create question display
questions_html = "# 📝 Assessment Questions\n\n"
questions_html += "> Please select one answer for each question.\n\n"
# Update radio buttons
updates = []
for i, q in enumerate(questions):
questions_html += f"### Question {i+1}\n{q.question}\n\n"
updates.append(gr.update(
visible=True,
choices=q.options,
value=None,
label=f"Select your answer:"
))
# Hide unused radio buttons
for i in range(len(questions), 5):
updates.append(gr.update(visible=False, choices=[]))
return (
gr.update(value=questions_html),
gr.update(value=""),
*updates,
questions,
gr.update(selected=1)
)
def submit_quiz(self, q1: Optional[str], q2: Optional[str], q3: Optional[str],
q4: Optional[str], q5: Optional[str], questions: List[Question]
) -> Tuple[gr.update, List[gr.update], float, str, gr.update]:
"""
Event handler for quiz submission
"""
answers = [q1, q2, q3, q4, q5][:len(questions)]
if not all(a is not None for a in answers):
return (
gr.update(value="⚠️ Please answer all questions before submitting."),
*[gr.update() for _ in range(5)],
0,
"",
gr.update(selected=1)
)
score, passed, feedback = self.calculate_score(answers)
# Create feedback HTML
feedback_html = "# Assessment Results\n\n"
for i, (q, f) in enumerate(zip(self.current_questions, feedback)):
color = "green" if f.is_correct else "red"
symbol = "✅" if f.is_correct else "❌"
feedback_html += f"""
### Question {i+1}
{q.question}
<div style="color: {color}; padding: 10px; margin: 5px 0; border-left: 3px solid {color};">
{symbol} Your answer: {f.selected}
{'' if f.is_correct else f'<br>Correct answer: {f.correct_answer}'}
</div>
"""
# Add result message
if passed:
feedback_html += self._create_success_message(score)
result_msg = f"🎉 Congratulations! You passed with {score:.1f}%"
else:
feedback_html += self._create_failure_message(score)
result_msg = f"Score: {score:.1f}%. You need 80% to pass."
return (
gr.update(value=feedback_html),
*[gr.update(visible=False) for _ in range(5)],
score,
result_msg,
gr.update(selected=2)
)
def _create_success_message(self, score: float) -> str:
return f"""
<div style="background-color: #e6ffe6; padding: 20px; margin-top: 20px; border-radius: 10px;">
<h3 style="color: #008000;">🎉 Congratulations!</h3>
<p>You passed the assessment with a score of {score:.1f}%</p>
<p>Your certificate has been generated.</p>
</div>
"""
def _create_failure_message(self, score: float) -> str:
return f"""
<div style="background-color: #ffe6e6; padding: 20px; margin-top: 20px; border-radius: 10px;">
<h3 style="color: #cc0000;">Please Try Again</h3>
<p>Your score: {score:.1f}%</p>
<p>You need 80% or higher to pass and receive a certificate.</p>
</div>
"""
def create_quiz_interface():
if not os.getenv("GROQ_API_KEY"):
raise EnvironmentError("Please set your GROQ_API_KEY environment variable")
quiz_app = QuizApp(os.getenv("GROQ_API_KEY"))
with gr.Blocks(title="CertifyMe AI", theme=gr.themes.Soft()) as demo:
# State management
current_questions = gr.State([])
current_question_idx = gr.State(0)
answer_state = gr.State([None] * 5)
# Header
gr.Markdown("""
<div style="text-align: center;">
<h1>🎓 Construction Safety Assessment Certification </h1>
<h3>Transform Your Knowledge into Recognized Achievements</h3></p>
</div>
<div style="margin: 20px 0; padding: 20px; background-color: #f8f9fa; border-radius: 10px;">
<h4>Assessment Overview:</h4>
<ul style="list-style-type: none; padding-left: 0;">
<li>📌 <strong>Basic Level:</strong> 5 questions - Perfect for beginners</li>
<li>📚 <strong>Intermediate Level:</strong> 10 questions - For those with Power BI experience</li>
<li>🎯 <strong>Advanced Level:</strong> 20 questions - Comprehensive assessment</li>
<li>⏱️ <strong>Passing Score:</strong> 80% or higher</li>
<li>🏆 <strong>Certificate:</strong> Awarded upon successful completion</li>
</ul>
<p style="margin-top: 15px;"><i>This assessment evaluates your understanding of Construction Safety.</i></p>
</div>
""", show_label=False)
with gr.Tabs() as tabs:
# Profile Setup Tab
with gr.Tab(id=1, label="📋 Step 1: Profile Setup"):
with gr.Row():
name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
email = gr.Textbox(label="Email", placeholder="Enter your email")
text_input = gr.Textbox(
label="Learning Content",
value=quiz_app.fixed_content, # Set the fixed content
interactive=False, # Make it non-editable
lines=10
)
difficulty_level = gr.Radio(
choices=["Basic", "Intermediate", "Advanced"],
value="Basic",
label="Select Difficulty Level",
info="Basic: 5 questions | Intermediate: 10 questions | Advanced: 20 questions")
with gr.Row():
participant_photo = gr.Image(label="Your Photo (Optional)", type="filepath")
generate_btn = gr.Button("Generate Assessment", variant="primary", size="lg")
# Assessment Tab
with gr.Tab(id=2, label="📝 Step 2: Take Assessment"):
with gr.Column() as main_container:
# Questions Section
with gr.Column(visible=True) as question_box:
question_display = gr.Markdown("")
current_options = gr.Radio(
choices=[],
label="Select your answer:",
visible=False
)
with gr.Row():
prev_btn = gr.Button("← Previous", variant="secondary", size="sm")
question_counter = gr.Markdown("Question 1")
next_btn = gr.Button("Next →", variant="secondary", size="sm")
gr.Markdown("---") # Separator
submit_btn = gr.Button(
"Submit Assessment",
variant="primary",
size="lg"
)
# Results Section in Assessment Tab
with gr.Column(visible=False) as results_group:
result_message = gr.Markdown(
label="Result",
show_label=True
)
feedback_box = gr.Markdown(
label="Detailed Feedback",
show_label=True
)
gr.Markdown("---") # Separator
with gr.Row(equal_height=True):
reset_btn = gr.Button(
"Reset Quiz",
variant="secondary",
size="lg",
visible=False
)
view_cert_btn = gr.Button(
"View Certificate",
variant="primary",
size="lg",
visible=False
)
# Certification Tab (Hidden by default)
with gr.Tab(id=3, label="🎓 Step 3: Get Certified", visible=False) as cert_tab:
score_display = gr.Number(label="Your Score", visible=False)
course_name = gr.Textbox(
label="Certification Title",
value=lambda: f"Construction Safety Assessment Certification", # Dynamic title with level
interactive=False
)
certificate_display = gr.Image(label="Your Certificate")
def show_certificate_tab():
return [
gr.update(visible=True), # Make cert_tab visible
gr.update(
value=f"Construction Safety Assessment Certification" # Update title with level
),
gr.update(selected=3) # Switch to cert_tab
]
# Helper Functions
def on_generate_questions(text, level):
quiz_app.selected_level = level # Store selected level
num_questions = quiz_app.difficulty_levels[level] # Get number of questions for level
success, questions = quiz_app.generate_questions(quiz_app.fixed_content, num_questions)
if not success or not questions:
return [
"",
gr.update(visible=False),
gr.update(choices=[], visible=False),
"",
[],
0,
[None] * num_questions, # Updated to use dynamic size
gr.update(selected=1),
gr.update(visible=False),
gr.update(visible=False)
]
question = questions[0]
question_md = f"""### Question 1\n{question.question}"""
return [
question_md,
gr.update(visible=True),
gr.update(
choices=question.options,
value=None,
visible=True,
label="Select your answer:"
),
f"Question 1 of {len(questions)}",
questions,
0,
[None] * len(questions),
gr.update(selected=2),
gr.update(visible=False),
gr.update(visible=False)
]
def navigate(direction, current_idx, questions, answers, current_answer):
if not questions:
return [0, answers, "", gr.update(choices=[], visible=False), "", gr.update(visible=False)]
new_answers = list(answers)
if current_answer is not None and 0 <= current_idx < len(new_answers):
new_answers[current_idx] = current_answer
new_idx = max(0, min(len(questions) - 1, current_idx + direction))
question = questions[new_idx]
question_md = f"""### Question {new_idx + 1}
{question.question}"""
return [
new_idx,
new_answers,
question_md,
gr.update(
choices=question.options,
value=new_answers[new_idx] if new_idx < len(new_answers) else None,
visible=True,
label="Select your answer:"
),
f"Question {new_idx + 1} of {len(questions)}",
gr.update(visible=True)
]
def update_answer_state(answer, idx, current_answers):
new_answers = list(current_answers)
if 0 <= idx < len(new_answers):
new_answers[idx] = answer
return new_answers
def reset_quiz(text, num_questions):
"""Handle quiz reset"""
return on_generate_questions(text, num_questions)
def view_certificate():
"""Navigate to certificate tab"""
return gr.update(selected=3)
def handle_prev(current_idx, questions, answers, current_answer):
return navigate(-1, current_idx, questions, answers, current_answer)
def handle_next(current_idx, questions, answers, current_answer):
return navigate(1, current_idx, questions, answers, current_answer)
def on_submit(questions, answers, current_idx, current_answer):
"""Handle quiz submission with proper Markdown rendering and emojis"""
final_answers = list(answers)
if 0 <= current_idx < len(final_answers):
final_answers[current_idx] = current_answer
if not all(a is not None for a in final_answers[:len(questions)]):
# Create list of unanswered question numbers
unanswered = [i+1 for i, a in enumerate(final_answers[:len(questions)]) if a is None]
warning_content = f"""
<div style="background-color: #fff3cd; padding: 20px; border-radius: 10px; border-left: 5px solid #ffa000;">
<h3 style="color: #ff6b6b; margin: 0;">⚠️ Please Complete All Questions</h3>
<p style="margin: 10px 0;">Unanswered Questions: {', '.join(map(str, unanswered))}</p>
</div>
"""
return [
warning_content, # feedback_box
gr.update(visible=True), # results_group
0, # score
"", # result_message
gr.update(visible=True), # question_box
gr.update(visible=True), # reset_btn
gr.update(visible=False), # view_cert_btn
gr.update(selected=2) # tabs
]
score, passed, feedback = quiz_app.calculate_score(final_answers[:len(questions)])
# Create feedback content using proper Markdown with emojis
feedback_content = f"""# Assessment Results
**Score: {score:.1f}%**
"""
for i, (q, f) in enumerate(zip(questions, feedback)):
icon = "✅" if f.is_correct else "❌"
color = "green" if f.is_correct else "red"
# Using markdown syntax with color formatting
feedback_content += f"""### Question {i+1}
{q.question}
{icon} **Your answer:** {f.selected or 'No answer'}
{'' if f.is_correct else f'**Correct answer:** {f.correct_answer}'}
"""
# Add summary box
if passed:
feedback_content += f"""
---
## 🎉 Congratulations!
You passed with a score of {score:.1f}%!
"""
else:
feedback_content += f"""
---
## Need Improvement
You scored {score:.1f}%. You need 80% or higher to pass.
Please try again.
"""
return [
feedback_content, # feedback_box
gr.update(visible=True), # results_group
score, # score_display
f"Score: {score:.1f}%", # result_message
gr.update(visible=False), # question_box
gr.update(visible=not passed), # reset_btn
gr.update(visible=passed), # view_cert_btn
gr.update(selected=2) # tabs
]
# Event Handlers
generate_btn.click(
fn=on_generate_questions,
inputs=[text_input, difficulty_level], # Replace num_questions with difficulty_level
outputs=[
question_display,
question_box,
current_options,
question_counter,
current_questions,
current_question_idx,
answer_state,
tabs,
results_group,
view_cert_btn
]
)
prev_btn.click(
fn=handle_prev,
inputs=[current_question_idx, current_questions, answer_state, current_options],
outputs=[current_question_idx, answer_state, question_display, current_options, question_counter, question_box]
)
next_btn.click(
fn=handle_next,
inputs=[current_question_idx, current_questions, answer_state, current_options],
outputs=[current_question_idx, answer_state, question_display, current_options, question_counter, question_box]
)
submit_btn.click(
fn=on_submit,
inputs=[current_questions, answer_state, current_question_idx, current_options],
outputs=[
feedback_box,
results_group,
score_display,
result_message, # Now properly defined
question_box,
reset_btn,
view_cert_btn,
tabs
]
)
reset_btn.click(
fn=on_generate_questions,
inputs=[text_input, difficulty_level], # Replace num_questions with difficulty_level
outputs=[
question_display,
question_box,
current_options,
question_counter,
current_questions,
current_question_idx,
answer_state,
tabs,
results_group,
view_cert_btn
]
)
view_cert_btn.click(
fn=show_certificate_tab,
outputs=[cert_tab, course_name, tabs] # Add course_name to outputs
)
current_options.change(
fn=update_answer_state,
inputs=[current_options, current_question_idx, answer_state],
outputs=answer_state
)
score_display.change(
fn=lambda s, n, c, p: quiz_app.certificate_generator.generate(
s, n,
quiz_app.get_certificate_title(c), # Add difficulty level to title
quiz_app.logo_path, p
) or gr.update(value=None),
inputs=[score_display, name, course_name, participant_photo],
outputs=certificate_display
)
return demo
if __name__ == "__main__":
demo = create_quiz_interface()
demo.launch()