PPE Recommendation System
Problem Statement
Businesses see PPE requirements in SDS documents but don't know how to translate generic requirements into actionable purchasing decisions:
Hand Protection:
- "Chemical resistant nitrile rubber gloves" — What specific thickness (mil) is needed? Which brands meet the requirement?
Eye Protection:
- "Safety glasses with side shields" vs "Chemical splash goggles" — When is each required? What ANSI rating is needed?
Respiratory Protection:
- "Use in well-ventilated area" vs "Organic vapor respirator required" — What cartridge type? What APF rating?
Body Protection:
- "Wear protective clothing" — Lab coat sufficient? Chemical apron? Full Tyvek coverall?
This creates a gap between regulatory compliance (having an SDS) and practical safety (using the right PPE).
Research Summary
OSHA Requirements for PPE Selection
According to OSHA's Personal Protective Equipment Standard (29 CFR 1910.138), hand protection is required when workers are at risk of:
- Skin absorption of harmful substances
- Severe cuts or lacerations
- Abrasions, punctures, chemical burns
- Thermal burns and temperature extremes
Gloves must be selected based on:
- Material being handled
- Particular hazard involved
- Suitability for the operation
Key Factors in Glove Selection
Per OSHA Glove Selection Guidelines:
| Factor | Description |
|---|---|
| Type of chemicals | CAS numbers and chemical families |
| Nature of contact | Total immersion, splash, incidental |
| Duration of contact | Short-term vs. continuous exposure |
| Breakthrough time | How long before chemical permeates |
| Degradation rating | How quickly material breaks down |
| Area requiring protection | Hand only, forearm, full arm |
| Grip requirements | Dry, wet, oily surfaces |
| Thermal protection | Hot or cold materials |
Chemical Resistance Rating Systems
ANSI/ISEA 105 Standard Ratings:
| Rating | Breakthrough Time |
|---|---|
| 0 | < 10 minutes |
| 1 | ≥ 10 minutes |
| 2 | ≥ 30 minutes |
| 3 | ≥ 60 minutes |
| 4 | ≥ 120 minutes |
| 5 | ≥ 240 minutes |
| 6 | ≥ 480 minutes |
DOE Qualitative Ratings:
- VG - Very Good (recommended)
- G - Good (acceptable)
- F - Fair (use with caution)
- P - Poor (not recommended)
Glove Material Properties
| Material | Best For | Avoid |
|---|---|---|
| Nitrile | Oils, greases, acids, caustics, alcohols, chlorinated solvents | Strong oxidizers, aromatic solvents, ketones, acetates |
| Butyl | Ketones, esters, aldehydes, strong acids/bases, peroxides | Aliphatic/aromatic hydrocarbons, gasoline |
| Neoprene | Straight-chain hydrocarbons, alcohols, fats/oils, Freon | Strong oxidizers |
| PVC | Acids, bases, fats, oils | Most organic solvents |
| Viton | Aromatic/aliphatic hydrocarbons, chlorinated solvents | Ketones, esters |
| Latex (Natural Rubber) | Aqueous solutions, alcohols, dilute acids/bases | Oils, organic solvents |
Important Warnings
From OSHA PPE Guidelines:
"Improper selection of gloves may give workers a false sense of security since chemicals may penetrate the 'protection' without it showing any signs of failure."
"No fabric (including leather, neoprene, latex) can provide protection against all potential hazards."
Eye and Face Protection
OSHA Requirements (29 CFR 1910.133)
Per OSHA Eye and Face Protection Standard, employers must provide eye/face protection when exposed to:
- Flying particles
- Molten metal
- Liquid chemicals, acids, or caustic liquids
- Chemical gases or vapors
- Potentially injurious light radiation
Eye Protection Types
| Type | ANSI Marking | Best For | Limitations |
|---|---|---|---|
| Safety Glasses | Z87+ | Impact hazards, dust, flying particles | NO splash protection, gaps around frame |
| Safety Glasses w/ Side Shields | Z87+ | Impact + side protection | Still has gaps, not for liquids |
| Chemical Splash Goggles | Z87+ D3 | Chemical splashes, liquid hazards | Can fog, less comfortable |
| Indirect Vent Goggles | Z87+ D4 | Chemical vapors + splash | Maximum chemical protection |
| Face Shield | Z87+ | Secondary protection over glasses/goggles | NOT standalone eye protection |
| Full-Face Respirator | Z87.1 + NIOSH | Combined eye + respiratory | Most protection, requires fit testing |
ANSI Z87.1 Markings Explained
| Marking | Meaning |
|---|---|
| Z87+ | High-impact rated (required for all safety eyewear) |
| D3 | Droplet and splash protection |
| D4 | Dust protection |
| D5 | Fine dust protection |
| W + shade # | Welding filter shade |
Eye Protection Selection Matrix
| Hazard Type | Minimum Protection | Recommended |
|---|---|---|
| Dust, particles | Safety glasses w/ side shields | Goggles for heavy dust |
| Liquid splash (non-corrosive) | Chemical splash goggles (D3) | Face shield + goggles |
| Corrosive liquids (acids/bases) | Indirect vent goggles (D3/D4) | Face shield + goggles |
| Chemical vapors/fumes | Indirect vent goggles (D4) | Full-face respirator |
| High-velocity impact | Safety glasses (Z87+) | Face shield + glasses |
Key Selection Factors
- Physical state of chemical: Liquid splash vs. vapor/gas
- Corrosivity: Corrosive chemicals require sealed goggles
- Splash probability: High splash risk = goggles + face shield
- GHS Hazard Codes:
- H314 (Severe skin burns and eye damage) → Indirect vent goggles + face shield
- H318 (Serious eye damage) → Chemical splash goggles minimum
- H319 (Eye irritation) → Safety glasses with side shields
Respiratory Protection
OSHA Requirements (29 CFR 1910.134)
Per OSHA Respiratory Protection Standard, respiratory protection is required when:
- Engineering controls are not feasible
- Engineering controls are being implemented
- Emergencies may occur
Assigned Protection Factors (APF)
The APF indicates the level of protection a respirator provides when properly fitted. From OSHA APF Guidelines:
| Respirator Type | APF | Use When Exposure Is |
|---|---|---|
| Filtering Facepiece (N95, P100) | 10 | ≤ 10x PEL |
| Half-Mask with Cartridges | 10 | ≤ 10x PEL |
| Full-Face with Cartridges | 50 | ≤ 50x PEL |
| Powered Air-Purifying (PAPR) Half-Mask | 50 | ≤ 50x PEL |
| PAPR Full-Face/Hood | 1000 | ≤ 1000x PEL |
| Supplied Air (SAR) Half-Mask | 10 | ≤ 10x PEL |
| SAR Full-Face | 50 | ≤ 50x PEL |
| SCBA (Self-Contained) | 10,000 | IDLH conditions |
Calculating Required APF:
Required APF = Exposure Level ÷ Permissible Exposure Limit (PEL)
Example: 500 ppm exposure ÷ 50 ppm PEL = APF 10 minimum
Filter/Cartridge Classifications
Per NIOSH Filter Classifications:
Particulate Filters (N/R/P Series):
| Series | Oil Resistance | Efficiency Options |
|---|---|---|
| N (Not oil resistant) | None | N95, N99, N100 |
| R (Resistant to oil) | Limited | R95, R99, R100 |
| P (Oil Proof) | Yes | P95, P99, P100 |
- 95 = 95% filtration efficiency
- 99 = 99% filtration efficiency
- 100 = 99.97% efficiency (HEPA equivalent)
Chemical Cartridge Color Codes:
| Color | Protection Against | Common Uses |
|---|---|---|
| Black | Organic Vapors (OV) | Solvents, paints, adhesives |
| White | Acid Gases (AG) | Chlorine, hydrogen chloride, sulfur dioxide |
| Yellow | Organic Vapors + Acid Gases | Combination hazards |
| Green | Ammonia/Methylamine | Cleaning products, refrigerants |
| Olive | Multi-Gas/Vapor | Complex chemical environments |
| Orange | Dust/Mist/Fume | Particulates (often w/ other cartridges) |
| Purple (Magenta) | P100 Particulate | Asbestos, lead, pharmaceutical |
Respirator Selection Matrix
| Chemical Type | Physical Form | Recommended Respirator |
|---|---|---|
| Dusts, fibers | Particulate | N95 or P100 filter |
| Oil-based mists | Particulate | R95 or P100 |
| Organic solvents | Vapor | OV cartridge (black) |
| Acids | Vapor/gas | Acid gas cartridge (white) |
| Formaldehyde | Vapor | Formaldehyde cartridge |
| Ammonia | Gas | Ammonia cartridge (green) |
| Mixed organic/acid | Vapor | Multi-gas cartridge (olive) |
| Unknown/IDLH | Any | SCBA only |
Key Selection Factors
- Contaminant identity: Required for cartridge selection
- Concentration level: Determines APF needed
- Oxygen level: < 19.5% O₂ requires supplied air
- Physical form: Particulate vs. vapor/gas
- Oil presence: Determines N/R/P series
- Exposure limit: OSHA PEL, NIOSH REL, or ACGIH TLV
Warning Signs Requiring Respirators
From SDS hazard statements:
- H330/H331 (Fatal/Toxic if inhaled) → Full-face respirator minimum
- H332 (Harmful if inhaled) → Half-mask with appropriate cartridge
- H334 (Respiratory sensitization) → Consider supplied air
- H335 (Respiratory irritation) → N95 or cartridge based on form
Skin and Body Protection
OSHA Requirements (29 CFR 1910.132)
Protective clothing is required when employees are exposed to hazards that can cause injury through absorption, physical contact, or contamination.
Protection Levels (EPA/OSHA)
| Level | Components | Use For |
|---|---|---|
| A | Fully encapsulated suit, SCBA | Unknown hazards, high vapor/gas |
| B | Chemical splash suit, SCBA | High liquid splash, known hazards |
| C | Chemical splash suit, APR | Known hazards, adequate APF |
| D | Work uniform, safety glasses | Nuisance contamination only |
Note: Most industrial/commercial settings use Level C or D
Body Protection Types
| Type | Material Options | Best For | Limitations |
|---|---|---|---|
| Lab Coat | Cotton, polyester, FR | Light splash, particulates | Absorbs liquids, no barrier |
| Chemical Apron | PVC, neoprene, rubber | Front splash protection | Back/arms exposed |
| Sleeve Protectors | Tyvek, PVC | Arm protection w/ apron | Limited coverage |
| Coveralls | Cotton, FR, Tyvek | Full body particulate | Limited chemical resistance |
| Chemical Coveralls | Tychem, Saranex | Full body chemical splash | Hot, single-use |
| Encapsulated Suit | Tychem, Viton | Maximum protection | Expensive, training required |
Material Selection for Chemical Resistance
| Material | Chemical Resistance | Best For |
|---|---|---|
| Tyvek | Particulates only | Dry chemicals, dusts, asbestos |
| Tychem QC | Light splash | General chemical splash |
| Tychem 2000 | Most chemicals | Moderate chemical exposure |
| Tychem 4000 | Concentrated chemicals | Strong acids/bases |
| Tychem 6000 | Highly toxic chemicals | Toxic industrial chemicals |
| Saranex | Many organics | Solvents, oils |
| Neoprene | Acids, caustics, oils | Chemical processing |
| Viton | Aromatics, chlorinated | Petrochemical |
Body Protection Selection Matrix
| Hazard Type | Minimum Protection | Enhanced Protection |
|---|---|---|
| Dry particulates | Lab coat | Tyvek coverall |
| Light liquid splash | Chemical apron | Tychem QC coverall |
| Corrosive splash | Chemical apron + sleeves | Tychem 2000+ coverall |
| Concentrated acids/bases | Tychem 4000 coverall | Level B suit |
| Toxic liquids | Tychem 6000 coverall | Level A suit |
| Full immersion | Level A suit | Double suit |
Key Selection Factors
- Chemical concentration: Higher = more resistant material
- Contact type: Splash vs. immersion vs. vapor
- Duration of exposure: Longer = more protection
- Body area at risk: Full body vs. specific areas
- GHS Hazard Codes:
- H310 (Fatal in contact with skin) → Chemical coverall minimum
- H311 (Toxic in contact with skin) → Chemical apron + sleeves
- H312 (Harmful in contact with skin) → Lab coat + gloves
- H314 (Severe skin burns) → Chemical-resistant coverall
Proposed System Architecture
Phase 1: Chemical Resistance Database (MVP)
Build a PPE Material Compatibility Database using data from:
- OSHA Glove Selection Chart
- Ansell Chemical Resistance Guide
- Manufacturer chemical resistance charts
Database Schema
-- ============================================================================
-- PPE Chemical Resistance Reference Table (Gloves)
-- Maps chemicals (by CAS number) to glove material compatibility
-- ============================================================================
CREATE TABLE ppe_glove_chemical_resistance (
resistance_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cas_number VARCHAR(20) NOT NULL,
chemical_name VARCHAR(255) NOT NULL,
chemical_family VARCHAR(100), -- e.g., 'ketone', 'alcohol', 'acid'
-- Glove material ratings (VG=Very Good, G=Good, F=Fair, P=Poor, NR=Not Recommended)
nitrile_rating VARCHAR(10),
butyl_rating VARCHAR(10),
neoprene_rating VARCHAR(10),
pvc_rating VARCHAR(10),
viton_rating VARCHAR(10),
latex_rating VARCHAR(10),
-- Breakthrough times in minutes (per material)
nitrile_breakthrough_min INT,
butyl_breakthrough_min INT,
neoprene_breakthrough_min INT,
viton_breakthrough_min INT,
-- Recommended thickness (mil)
recommended_thickness_mil INT,
-- Metadata
source VARCHAR(255), -- e.g., 'OSHA', 'Ansell', 'North Safety'
notes TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP,
UNIQUE(cas_number)
);
CREATE INDEX idx_ppe_glove_resistance_cas ON ppe_glove_chemical_resistance(cas_number);
-- ============================================================================
-- Eye Protection Reference Table
-- Maps hazard types to eye protection requirements
-- ============================================================================
CREATE TABLE ppe_eye_protection_reference (
reference_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Hazard classification
hazard_type VARCHAR(50) NOT NULL, -- 'chemical_splash', 'chemical_vapor', 'dust', 'impact'
ghs_hazard_code VARCHAR(10), -- H314, H318, H319, etc.
-- Recommended protection
minimum_protection VARCHAR(50) NOT NULL, -- 'safety_glasses', 'splash_goggles', 'indirect_vent_goggles'
recommended_protection VARCHAR(50),
ansi_marking_required VARCHAR(20), -- 'Z87+', 'Z87+ D3', 'Z87+ D4'
-- Additional requirements
face_shield_recommended BOOLEAN DEFAULT FALSE,
full_face_respirator_option BOOLEAN DEFAULT FALSE,
-- Reasoning
selection_reasoning TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- ============================================================================
-- Respiratory Protection Reference Table
-- Maps chemicals to respirator cartridge requirements
-- ============================================================================
CREATE TABLE ppe_respiratory_reference (
reference_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Chemical identification
cas_number VARCHAR(20),
chemical_name VARCHAR(255),
chemical_family VARCHAR(100), -- 'organic_solvent', 'acid', 'ammonia', 'particulate'
-- Exposure limits (for APF calculation)
osha_pel_ppm DECIMAL(10,4),
osha_pel_mgm3 DECIMAL(10,4),
niosh_rel_ppm DECIMAL(10,4),
niosh_rel_mgm3 DECIMAL(10,4),
niosh_idlh_ppm DECIMAL(10,4), -- Immediately Dangerous to Life/Health
-- Cartridge requirements
cartridge_type VARCHAR(50), -- 'organic_vapor', 'acid_gas', 'ammonia', 'multi_gas', 'particulate'
cartridge_color VARCHAR(20), -- 'black', 'white', 'green', 'yellow', 'olive', 'magenta'
particulate_filter VARCHAR(10), -- 'N95', 'P100', etc.
-- Physical form considerations
vapor_pressure_mmhg DECIMAL(10,4),
is_particulate BOOLEAN DEFAULT FALSE,
oil_present BOOLEAN DEFAULT FALSE,
-- Minimum respirator requirements
min_respirator_type VARCHAR(50), -- 'filtering_facepiece', 'half_mask', 'full_face', 'papr', 'scba'
min_apf INT, -- Minimum Assigned Protection Factor
-- Warnings
requires_supplied_air BOOLEAN DEFAULT FALSE, -- For low O2 or IDLH
special_considerations TEXT,
source VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP,
UNIQUE(cas_number)
);
CREATE INDEX idx_ppe_respiratory_cas ON ppe_respiratory_reference(cas_number);
CREATE INDEX idx_ppe_respiratory_family ON ppe_respiratory_reference(chemical_family);
-- ============================================================================
-- Body Protection Reference Table
-- Maps hazard types to body protection requirements
-- ============================================================================
CREATE TABLE ppe_body_protection_reference (
reference_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Hazard classification
hazard_type VARCHAR(50) NOT NULL, -- 'particulate', 'light_splash', 'corrosive', 'toxic', 'immersion'
ghs_hazard_code VARCHAR(10), -- H310, H311, H312, H314, etc.
-- Recommended protection
minimum_protection VARCHAR(50) NOT NULL, -- 'lab_coat', 'chemical_apron', 'tyvek_coverall', 'tychem_coverall'
recommended_protection VARCHAR(50),
protection_level VARCHAR(5), -- 'A', 'B', 'C', 'D'
-- Material requirements
min_material_type VARCHAR(50), -- 'cotton', 'tyvek', 'tychem_qc', 'tychem_2000', etc.
-- Selection reasoning
selection_reasoning TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
-- ============================================================================
-- Body Protection Material Chemical Resistance
-- Maps chemicals to body protection material compatibility
-- ============================================================================
CREATE TABLE ppe_body_material_resistance (
resistance_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cas_number VARCHAR(20) NOT NULL,
chemical_name VARCHAR(255) NOT NULL,
-- Material ratings (VG=Very Good, G=Good, F=Fair, P=Poor, NR=Not Recommended)
tyvek_rating VARCHAR(10),
tychem_qc_rating VARCHAR(10),
tychem_2000_rating VARCHAR(10),
tychem_4000_rating VARCHAR(10),
tychem_6000_rating VARCHAR(10),
saranex_rating VARCHAR(10),
-- Breakthrough times in minutes
tychem_2000_breakthrough_min INT,
tychem_4000_breakthrough_min INT,
source VARCHAR(255),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(cas_number)
);
-- ============================================================================
-- PPE Product Catalog (All Types)
-- Curated list of PPE products with specifications
-- ============================================================================
CREATE TABLE ppe_products (
product_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Product identification
product_name VARCHAR(255) NOT NULL,
manufacturer VARCHAR(100) NOT NULL,
product_code VARCHAR(100),
-- PPE classification
ppe_category VARCHAR(50) NOT NULL, -- 'hand', 'eye', 'respiratory', 'body'
ppe_type VARCHAR(50) NOT NULL, -- 'gloves', 'goggles', 'respirator', 'coverall', etc.
ppe_subtype VARCHAR(50), -- e.g., 'chemical_splash_goggles', 'half_mask_respirator'
-- === GLOVE-SPECIFIC FIELDS ===
glove_material VARCHAR(50), -- 'nitrile', 'butyl', 'neoprene', etc.
glove_thickness_mil DECIMAL(4,1),
glove_length_inches DECIMAL(4,1),
glove_ansi_level INT, -- 0-6 per ANSI/ISEA 105
-- === EYE PROTECTION FIELDS ===
eye_ansi_marking VARCHAR(20), -- 'Z87+', 'Z87+ D3', 'Z87+ D4'
eye_lens_type VARCHAR(50), -- 'clear', 'tinted', 'anti_fog'
eye_has_side_shields BOOLEAN,
eye_indirect_vent BOOLEAN,
-- === RESPIRATOR FIELDS ===
respirator_type VARCHAR(50), -- 'filtering_facepiece', 'half_mask', 'full_face', 'papr'
respirator_apf INT, -- Assigned Protection Factor
respirator_cartridge_type VARCHAR(50), -- 'organic_vapor', 'acid_gas', 'multi_gas'
respirator_filter_rating VARCHAR(10), -- 'N95', 'P100', etc.
respirator_niosh_approved BOOLEAN DEFAULT TRUE,
-- === BODY PROTECTION FIELDS ===
body_coverage VARCHAR(50), -- 'apron', 'coverall', 'encapsulated'
body_material VARCHAR(50), -- 'tyvek', 'tychem_2000', etc.
body_protection_level VARCHAR(5), -- 'A', 'B', 'C', 'D'
body_seam_type VARCHAR(50), -- 'serged', 'bound', 'taped'
-- === COMMON FIELDS ===
certifications JSONB, -- Array of certifications
size_options VARCHAR(100), -- 'S, M, L, XL, 2XL'
-- Purchasing information
purchase_url VARCHAR(500),
price_range VARCHAR(50), -- e.g., '$10-15/pair', '$50-75/box'
pack_quantity INT,
-- Availability
is_active BOOLEAN DEFAULT TRUE,
-- Metadata
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP
);
CREATE INDEX idx_ppe_products_category ON ppe_products(ppe_category);
CREATE INDEX idx_ppe_products_type ON ppe_products(ppe_type);
-- ============================================================================
-- PPE Recommendations (generated per SDS)
-- Stores calculated recommendations for each SDS based on composition
-- ============================================================================
CREATE TABLE ppe_sds_recommendations (
recommendation_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
sds_id UUID NOT NULL REFERENCES chemiq_sds_documents(sds_id),
-- === HAND PROTECTION ===
glove_material VARCHAR(50),
glove_thickness_mil INT,
glove_ansi_level INT,
glove_reasoning TEXT,
glove_confidence DECIMAL(3,2),
-- === EYE PROTECTION ===
eye_protection_type VARCHAR(50), -- 'safety_glasses', 'splash_goggles', 'indirect_vent_goggles', 'face_shield'
eye_ansi_marking VARCHAR(20),
eye_face_shield_required BOOLEAN DEFAULT FALSE,
eye_reasoning TEXT,
eye_confidence DECIMAL(3,2),
-- === RESPIRATORY PROTECTION ===
respirator_type VARCHAR(50), -- 'none', 'n95', 'half_mask', 'full_face', 'papr', 'scba'
respirator_cartridge VARCHAR(50),
respirator_filter VARCHAR(10),
respirator_apf_required INT,
respirator_reasoning TEXT,
respirator_confidence DECIMAL(3,2),
-- === BODY PROTECTION ===
body_protection_type VARCHAR(50), -- 'lab_coat', 'chemical_apron', 'coverall', 'encapsulated'
body_material VARCHAR(50),
body_protection_level VARCHAR(5),
body_reasoning TEXT,
body_confidence DECIMAL(3,2),
-- === OVERALL ===
overall_confidence DECIMAL(3,2), -- 0.00 - 1.00
generated_at TIMESTAMP DEFAULT NOW(),
generated_by VARCHAR(50), -- 'algorithm', 'ai', 'manual'
last_reviewed_at TIMESTAMP,
reviewed_by UUID,
UNIQUE(sds_id)
);
-- ============================================================================
-- GHS Hazard Code to PPE Mapping
-- Quick lookup for hazard statement → PPE requirements
-- ============================================================================
CREATE TABLE ppe_ghs_hazard_mapping (
mapping_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
ghs_code VARCHAR(10) NOT NULL, -- H314, H318, H330, etc.
hazard_statement TEXT,
hazard_category VARCHAR(50), -- 'skin', 'eye', 'inhalation', 'ingestion'
-- PPE requirements triggered by this hazard
eye_protection_min VARCHAR(50),
eye_protection_recommended VARCHAR(50),
hand_protection_min VARCHAR(50),
respirator_required BOOLEAN,
respirator_type_min VARCHAR(50),
body_protection_min VARCHAR(50),
notes TEXT,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(ghs_code)
);
-- Populate common GHS hazard mappings
INSERT INTO ppe_ghs_hazard_mapping (ghs_code, hazard_statement, hazard_category, eye_protection_min, eye_protection_recommended, hand_protection_min, respirator_required, body_protection_min) VALUES
('H310', 'Fatal in contact with skin', 'skin', 'splash_goggles', 'face_shield', 'chemical_resistant', TRUE, 'chemical_coverall'),
('H311', 'Toxic in contact with skin', 'skin', 'splash_goggles', 'splash_goggles', 'chemical_resistant', FALSE, 'chemical_apron'),
('H312', 'Harmful in contact with skin', 'skin', 'safety_glasses', 'splash_goggles', 'chemical_resistant', FALSE, 'lab_coat'),
('H314', 'Causes severe skin burns and eye damage', 'skin', 'indirect_vent_goggles', 'face_shield', 'chemical_resistant', FALSE, 'chemical_coverall'),
('H315', 'Causes skin irritation', 'skin', 'safety_glasses', 'safety_glasses', 'general_purpose', FALSE, 'lab_coat'),
('H317', 'May cause an allergic skin reaction', 'skin', 'safety_glasses', 'safety_glasses', 'chemical_resistant', FALSE, 'lab_coat'),
('H318', 'Causes serious eye damage', 'eye', 'splash_goggles', 'face_shield', 'general_purpose', FALSE, NULL),
('H319', 'Causes serious eye irritation', 'eye', 'safety_glasses', 'splash_goggles', 'general_purpose', FALSE, NULL),
('H330', 'Fatal if inhaled', 'inhalation', 'full_face_respirator', 'full_face_respirator', 'chemical_resistant', TRUE, 'chemical_coverall'),
('H331', 'Toxic if inhaled', 'inhalation', 'splash_goggles', 'full_face_respirator', 'chemical_resistant', TRUE, 'chemical_apron'),
('H332', 'Harmful if inhaled', 'inhalation', 'safety_glasses', 'splash_goggles', 'general_purpose', TRUE, 'lab_coat'),
('H334', 'May cause allergy or asthma symptoms if inhaled', 'inhalation', 'splash_goggles', 'splash_goggles', 'chemical_resistant', TRUE, 'lab_coat'),
('H335', 'May cause respiratory irritation', 'inhalation', 'safety_glasses', 'safety_glasses', 'general_purpose', TRUE, NULL);
Recommendation Algorithms
# ============================================================================
# GLOVE RECOMMENDATION ALGORITHM
# ============================================================================
def get_glove_recommendation(composition: List[SDSComposition]) -> GloveRecommendation:
"""
Analyze SDS composition and recommend appropriate glove material.
Algorithm:
1. Look up each ingredient's CAS number in resistance database
2. For each glove material, find the WORST rating among all ingredients
3. Recommend the material with the best worst-case rating
4. Calculate minimum required thickness based on ingredients
"""
materials = ['nitrile', 'butyl', 'neoprene', 'pvc', 'viton', 'latex']
material_scores = {m: [] for m in materials}
rating_values = {'VG': 4, 'G': 3, 'F': 2, 'P': 1, 'NR': 0}
for ingredient in composition:
if not ingredient.cas_number:
continue
resistance = db.query(PPEGloveChemicalResistance).filter(
PPEGloveChemicalResistance.cas_number == ingredient.cas_number
).first()
if resistance:
for material in materials:
rating = getattr(resistance, f'{material}_rating')
if rating:
material_scores[material].append(rating_values.get(rating, 0))
# Find best material (highest minimum score across all ingredients)
best_material = None
best_min_score = -1
for material, scores in material_scores.items():
if scores:
min_score = min(scores)
if min_score > best_min_score:
best_min_score = min_score
best_material = material
score_to_rating = {4: 'VG', 3: 'G', 2: 'F', 1: 'P', 0: 'NR'}
return GloveRecommendation(
material=best_material,
rating=score_to_rating.get(best_min_score, 'Unknown'),
thickness_mil=8 if best_min_score <= 2 else 4,
confidence=best_min_score / 4.0 if best_min_score >= 0 else 0
)
# ============================================================================
# EYE PROTECTION RECOMMENDATION ALGORITHM
# ============================================================================
def get_eye_protection_recommendation(
hazard_info: SDSHazardInfo,
composition: List[SDSComposition]
) -> EyeProtectionRecommendation:
"""
Recommend eye protection based on GHS hazard statements and chemical properties.
Algorithm:
1. Check for eye-specific hazard codes (H314, H318, H319)
2. Check for corrosive/irritant ingredients
3. Determine if liquid splash or vapor exposure is likely
4. Recommend minimum ANSI marking based on hazards
"""
# Priority levels for eye protection
protection_priority = {
'full_face_respirator': 5,
'indirect_vent_goggles': 4,
'splash_goggles': 3,
'safety_glasses_side_shields': 2,
'safety_glasses': 1,
}
recommended_protection = 'safety_glasses'
face_shield_needed = False
reasoning = []
# Check GHS hazard statements
hazard_statements = hazard_info.hazard_statements or []
for statement in hazard_statements:
# Look up hazard code in mapping table
mapping = db.query(PPEGHSHazardMapping).filter(
PPEGHSHazardMapping.ghs_code == extract_hazard_code(statement)
).first()
if mapping and mapping.eye_protection_min:
if protection_priority.get(mapping.eye_protection_min, 0) > \
protection_priority.get(recommended_protection, 0):
recommended_protection = mapping.eye_protection_min
reasoning.append(f"Required for {mapping.ghs_code}: {mapping.hazard_statement}")
if mapping.eye_protection_recommended == 'face_shield':
face_shield_needed = True
# Check for corrosive chemicals in composition
for ingredient in composition:
if ingredient.is_hazardous and 'corrosive' in (ingredient.ingredient_hazards or []):
if protection_priority.get('indirect_vent_goggles', 0) > \
protection_priority.get(recommended_protection, 0):
recommended_protection = 'indirect_vent_goggles'
reasoning.append(f"Corrosive ingredient: {ingredient.chemical_name}")
face_shield_needed = True
# Determine ANSI marking
ansi_marking_map = {
'safety_glasses': 'Z87+',
'safety_glasses_side_shields': 'Z87+',
'splash_goggles': 'Z87+ D3',
'indirect_vent_goggles': 'Z87+ D4',
'full_face_respirator': 'Z87.1 + NIOSH',
}
return EyeProtectionRecommendation(
protection_type=recommended_protection,
ansi_marking=ansi_marking_map.get(recommended_protection, 'Z87+'),
face_shield_required=face_shield_needed,
reasoning='; '.join(reasoning) or 'Standard eye protection for chemical handling',
confidence=0.8 if reasoning else 0.5
)
# ============================================================================
# RESPIRATORY PROTECTION RECOMMENDATION ALGORITHM
# ============================================================================
def get_respiratory_recommendation(
hazard_info: SDSHazardInfo,
composition: List[SDSComposition],
exposure_controls: SDSExposureControls
) -> RespiratoryRecommendation:
"""
Recommend respiratory protection based on chemical hazards and exposure limits.
Algorithm:
1. Check for inhalation hazard codes (H330, H331, H332, H334, H335)
2. Determine if particulate or vapor hazard
3. Look up cartridge requirements for each ingredient
4. Calculate required APF based on exposure limits
5. Recommend appropriate respirator type
"""
# Priority levels for respirators
respirator_priority = {
'none': 0,
'n95': 1,
'half_mask_ov': 2,
'half_mask_multi': 3,
'full_face': 4,
'papr': 5,
'scba': 6,
}
recommended_type = 'none'
cartridge_type = None
filter_rating = None
min_apf = 1
reasoning = []
# Check GHS hazard statements for inhalation hazards
hazard_statements = hazard_info.hazard_statements or []
for statement in hazard_statements:
code = extract_hazard_code(statement)
# Map hazard codes to respirator requirements
if code in ['H330', 'H331']: # Fatal/Toxic if inhaled
recommended_type = 'full_face'
min_apf = 50
reasoning.append(f"{code}: Toxic inhalation hazard - full-face respirator required")
elif code in ['H332', 'H334', 'H335']: # Harmful/Irritating if inhaled
if respirator_priority.get('half_mask_ov', 0) > respirator_priority.get(recommended_type, 0):
recommended_type = 'half_mask_ov'
min_apf = 10
reasoning.append(f"{code}: Inhalation hazard - respirator recommended")
# Determine cartridge type based on chemical composition
cartridge_requirements = set()
for ingredient in composition:
if not ingredient.cas_number:
continue
resp_ref = db.query(PPERespiratoryReference).filter(
PPERespiratoryReference.cas_number == ingredient.cas_number
).first()
if resp_ref:
if resp_ref.cartridge_type:
cartridge_requirements.add(resp_ref.cartridge_type)
if resp_ref.is_particulate:
filter_rating = 'P100' if resp_ref.oil_present else 'N95'
if resp_ref.requires_supplied_air:
recommended_type = 'scba'
min_apf = 10000
reasoning.append(f"Supplied air required for {ingredient.chemical_name}")
# Determine final cartridge type
if 'organic_vapor' in cartridge_requirements and 'acid_gas' in cartridge_requirements:
cartridge_type = 'multi_gas' # Olive
elif 'organic_vapor' in cartridge_requirements:
cartridge_type = 'organic_vapor' # Black
elif 'acid_gas' in cartridge_requirements:
cartridge_type = 'acid_gas' # White
elif 'ammonia' in cartridge_requirements:
cartridge_type = 'ammonia' # Green
# Check SDS-specified respiratory requirements
if exposure_controls and exposure_controls.ppe:
sds_resp = exposure_controls.ppe.respiratory_protection
if sds_resp and 'SCBA' in sds_resp.upper():
recommended_type = 'scba'
reasoning.append("SDS specifies SCBA")
elif sds_resp and 'supplied air' in sds_resp.lower():
recommended_type = 'scba'
reasoning.append("SDS specifies supplied air")
return RespiratoryRecommendation(
respirator_type=recommended_type,
cartridge_type=cartridge_type,
filter_rating=filter_rating,
min_apf=min_apf,
reasoning='; '.join(reasoning) or 'Standard respiratory protection assessment',
confidence=0.8 if reasoning else 0.5
)
# ============================================================================
# BODY PROTECTION RECOMMENDATION ALGORITHM
# ============================================================================
def get_body_protection_recommendation(
hazard_info: SDSHazardInfo,
composition: List[SDSComposition]
) -> BodyProtectionRecommendation:
"""
Recommend body/skin protection based on GHS hazards and chemical properties.
Algorithm:
1. Check for skin hazard codes (H310, H311, H312, H314, H315, H317)
2. Determine severity of skin contact hazard
3. Look up material resistance for each ingredient
4. Recommend appropriate protection level
"""
# Priority levels for body protection
protection_priority = {
'none': 0,
'lab_coat': 1,
'chemical_apron': 2,
'tyvek_coverall': 3,
'tychem_qc_coverall': 4,
'tychem_2000_coverall': 5,
'tychem_4000_coverall': 6,
'level_b_suit': 7,
'level_a_suit': 8,
}
recommended_protection = 'lab_coat'
recommended_material = 'cotton'
protection_level = 'D'
reasoning = []
# Check GHS hazard statements
hazard_statements = hazard_info.hazard_statements or []
for statement in hazard_statements:
code = extract_hazard_code(statement)
mapping = db.query(PPEGHSHazardMapping).filter(
PPEGHSHazardMapping.ghs_code == code
).first()
if mapping and mapping.body_protection_min:
current_priority = protection_priority.get(recommended_protection, 0)
new_priority = protection_priority.get(mapping.body_protection_min, 0)
if new_priority > current_priority:
recommended_protection = mapping.body_protection_min
reasoning.append(f"{code}: {mapping.hazard_statement}")
# Check material resistance for composition
for ingredient in composition:
if not ingredient.cas_number:
continue
body_resistance = db.query(PPEBodyMaterialResistance).filter(
PPEBodyMaterialResistance.cas_number == ingredient.cas_number
).first()
if body_resistance:
# Find best-rated material
materials = ['tyvek', 'tychem_qc', 'tychem_2000', 'tychem_4000', 'tychem_6000']
rating_values = {'VG': 4, 'G': 3, 'F': 2, 'P': 1, 'NR': 0}
for material in materials:
rating = getattr(body_resistance, f'{material}_rating', None)
if rating and rating_values.get(rating, 0) >= 3:
recommended_material = material
break
# Map protection to level
if 'level_a' in recommended_protection or 'level_b' in recommended_protection:
protection_level = recommended_protection.split('_')[1].upper()
elif 'tychem' in recommended_protection:
protection_level = 'C'
else:
protection_level = 'D'
return BodyProtectionRecommendation(
protection_type=recommended_protection,
material=recommended_material,
protection_level=protection_level,
reasoning='; '.join(reasoning) or 'Standard body protection for chemical handling',
confidence=0.8 if reasoning else 0.5
)
# ============================================================================
# COMBINED PPE RECOMMENDATION
# ============================================================================
def get_full_ppe_recommendation(sds_detail: SDSDocumentDetail) -> PPERecommendation:
"""
Generate comprehensive PPE recommendations for all protection types.
"""
glove_rec = get_glove_recommendation(sds_detail.composition)
eye_rec = get_eye_protection_recommendation(sds_detail.hazard_info, sds_detail.composition)
resp_rec = get_respiratory_recommendation(
sds_detail.hazard_info,
sds_detail.composition,
sds_detail.exposure_controls
)
body_rec = get_body_protection_recommendation(sds_detail.hazard_info, sds_detail.composition)
# Calculate overall confidence
confidences = [glove_rec.confidence, eye_rec.confidence, resp_rec.confidence, body_rec.confidence]
overall_confidence = sum(confidences) / len(confidences)
return PPERecommendation(
gloves=glove_rec,
eye_protection=eye_rec,
respiratory=resp_rec,
body_protection=body_rec,
overall_confidence=overall_confidence
)
Phase 2: AI-Powered Recommendations
Use an LLM to generate contextual recommendations with explanations:
async def get_ai_ppe_recommendations(sds_detail: SDSDocumentDetail) -> PPERecommendation:
"""
Use LLM to generate specific PPE product recommendations
based on SDS composition and PPE requirements.
"""
composition_text = "\n".join([
f"- {c.chemical_name} (CAS: {c.cas_number}, {c.concentration_text or 'unknown %'})"
for c in sds_detail.composition
])
prompt = f"""
Based on this SDS information, recommend specific PPE products:
Product: {sds_detail.product_name}
Manufacturer: {sds_detail.manufacturer}
Composition:
{composition_text}
Current PPE Requirements from SDS:
- Eye: {sds_detail.exposure_controls.ppe.eye_protection or 'Not specified'}
- Hand: {sds_detail.exposure_controls.ppe.hand_protection or 'Not specified'}
- Respiratory: {sds_detail.exposure_controls.ppe.respiratory_protection or 'Not specified'}
- Skin: {sds_detail.exposure_controls.ppe.skin_protection or 'Not specified'}
Hazard Statements:
{', '.join(sds_detail.hazard_info.hazard_statements or [])}
Please provide specific recommendations in JSON format:
{{
"gloves": {{
"material": "nitrile/butyl/neoprene/etc",
"thickness_mil": 8,
"reasoning": "explanation based on chemical composition"
}},
"eye_protection": {{
"type": "safety glasses/chemical splash goggles/face shield",
"reasoning": "explanation"
}},
"respiratory": {{
"type": "N95/P100/organic vapor cartridge/etc",
"reasoning": "explanation"
}},
"body_protection": {{
"type": "lab coat/chemical-resistant apron/full coverall",
"reasoning": "explanation"
}}
}}
"""
# Call Claude/OpenAI API
response = await llm_client.complete(prompt)
return parse_ppe_recommendation(response)
Phase 3: Product Catalog Integration
Partner with PPE distributors who have APIs:
| Supplier | API Availability | Notes |
|---|---|---|
| Grainger | Yes | Large industrial supplier, extensive catalog |
| Uline | Limited | Good for bulk purchases |
| Amazon Business | Yes | Wide selection, Prime shipping |
| MSC Direct | Yes | Industrial supplies, technical support |
| Fisher Scientific | Yes | Lab-grade PPE |
This enables:
- Real-time product availability
- Price comparison across suppliers
- Direct purchase links
- Inventory management integration
UI Design
SDSInfoCard Enhancement
Add a "PPE Shopping Guide" section to the existing SDS card:
{/* PPE Shopping Guide */}
{hasPPE && ppeRecommendations && (
<div className="mt-4 pt-4 border-t border-border-light">
<div className="flex items-center justify-between mb-3">
<p className="field-label font-medium flex items-center gap-2">
<ShoppingCart className="w-4 h-4 text-accent-green" />
PPE Shopping Guide
</p>
<button className="text-xs text-accent-blue hover:underline">
View All Recommendations
</button>
</div>
{/* Glove Recommendation Card */}
<div className="p-4 bg-surface-light rounded-lg">
<div className="flex items-start gap-3">
<Hand className="w-8 h-8 text-accent-orange" />
<div className="flex-1">
<p className="font-medium">
Recommended: {ppeRecommendations.gloves.material} Gloves
({ppeRecommendations.gloves.thickness_mil}+ mil)
</p>
<p className="text-sm text-text-muted mt-1">
{ppeRecommendations.gloves.reasoning}
</p>
<div className="mt-2 flex gap-2 flex-wrap">
<span className="badge-success text-xs">
{ppeRecommendations.gloves.rating} Resistance
</span>
<span className="badge-info text-xs">
ANSI Level {ppeRecommendations.gloves.ansi_level}
</span>
</div>
{/* Product suggestions */}
{ppeRecommendations.gloves.products && (
<div className="mt-3 space-y-2">
<p className="text-xs font-semibold text-text-muted uppercase">
Suggested Products
</p>
{ppeRecommendations.gloves.products.map(product => (
<a
key={product.id}
href={product.purchase_url}
target="_blank"
className="flex items-center justify-between p-2 bg-white rounded border hover:border-accent-blue"
>
<div>
<p className="text-sm font-medium">{product.name}</p>
<p className="text-xs text-text-muted">{product.manufacturer}</p>
</div>
<div className="text-right">
<p className="text-sm font-medium text-accent-green">
{product.price_range}
</p>
<ExternalLink className="w-3 h-3 text-text-muted" />
</div>
</a>
))}
</div>
)}
</div>
</div>
</div>
{/* Similar cards for eye, respiratory, body protection */}
</div>
)}
Dedicated PPE Recommendations Page
Create a full page at /chemiq/inventory/{id}/ppe with:
- Summary Card - Overall protection level needed
- Detailed Recommendations - Per-category with reasoning
- Product Comparison Table - Compare suggested products
- Purchase Integration - Add to cart / bulk order
- Training Materials - Links to proper donning/doffing procedures
Implementation Roadmap
Phase 1: Static Recommendations (2-3 sprints)
Deliverables:
- Chemical resistance lookup table (~500 common chemicals)
- Basic recommendation algorithm
- UI showing "Recommended Glove Material" based on composition
- Info tooltips explaining material properties
Data Sources:
- Parse OSHA Glove Selection Chart PDF
- Digitize Ansell Chemical Resistance Guide
- Import from PubChem GHS data
Phase 2: Product Catalog (2 sprints)
Deliverables:
- Curated PPE product database (50-100 products)
- Link materials to specific products
- Show "Suggested Products" with purchase links
- Admin interface to manage product catalog
Phase 3: AI Enhancement (2 sprints)
Deliverables:
- LLM-powered contextual explanations
- Handle complex mixtures intelligently
- Generate custom training materials
- Natural language Q&A about PPE requirements
Phase 4: Supplier Integration (3+ sprints)
Deliverables:
- API integration with 1-2 suppliers
- Real-time pricing and availability
- Purchase workflow integration
- Order tracking and history
Data Sources
Free/Open Data
| Source | URL | Content |
|---|---|---|
| PubChem GHS | https://pubchem.ncbi.nlm.nih.gov/ghs/ | GHS classifications for millions of chemicals |
| OSHA | https://www.osha.gov/personal-protective-equipment | Exposure limits, PPE requirements |
| NIOSH | https://www.cdc.gov/niosh/ | Occupational exposure data |
Manufacturer Data (Requires Parsing)
| Source | Content |
|---|---|
| Ansell Chemical Resistance Guide | Comprehensive glove material ratings |
| North/Honeywell Charts | Additional chemical coverage |
| Showa Glove Guide | Asian chemical coverage |
| Best Manufacturing | Specialty chemicals |
Commercial APIs
| Service | Cost | Features |
|---|---|---|
| ChemRADAR | Subscription | GHS classification search |
| 3E Company | Enterprise | Full SDS management |
| VelocityEHS | Enterprise | Chemical management platform |
Chemical Database Integration
PPE recommendations are significantly enhanced when combined with external chemical database information. See Chemical Database Integration for the complete implementation strategy.
How Enriched Data Improves PPE Selection
| PPE Type | SDS Data Only | With Chemical Database Enrichment |
|---|---|---|
| Gloves | "Chemical resistant gloves" → Generic recommendation | CAS → chemical_family → Specific material (e.g., Butyl for ketones) |
| Respiratory | "Use respiratory protection" → Generic respirator | vapor_pressure + osha_pel → Calculated APF → Specific cartridge type |
| Eye | "Wear eye protection" → Safety glasses | ghs_classification → H-codes → Goggles vs face shield decision |
| Body | "Protective clothing" → Generic recommendation | Chemical toxicity data → Protection level A/B/C/D |
Data Flow: SDS Parsing → Chemical Enrichment → PPE
SDS Upload ──► Parse Section 3 ──► Extract CAS Numbers
│
▼
Check PubChem Cache ──► Miss? ──► Queue Background Fetch
│
▼
Get Enriched Data:
• chemical_family (for glove material)
• vapor_pressure_mmhg (for respiratory)
• osha_pel_ppm (for APF calculation)
• niosh_idlh_ppm (for urgency level)
│
▼
Enhanced PPE Recommendation
Integration During SDS Processing (Background Job)
When an SDS is uploaded and processed by the background job:
- Parse SDS - Extract all 16 sections via LLM
- Store Composition - Save CAS numbers to
chemiq_sds_composition - Queue Chemical Enrichment - For each CAS number not in cache:
- Add to
chemiq_chemical_fetch_queue - Background worker fetches from PubChem
- Store in
chemiq_pubchem_cache
- Add to
- Generate PPE Recommendations - Using both SDS data and enriched chemical data
- Store Recommendations - Save to
ppe_sds_recommendationstable
# Simplified integration in SDS processing service
async def process_sds_with_ppe(db: Session, sds_id: UUID):
# 1. Parse SDS (existing)
parsed_data = await parse_sds_document(sds_id)
# 2. Extract composition (existing)
composition = parsed_data.get('section3_composition', [])
# 3. Enrich chemicals (NEW - see chemical_database_integration.md)
enrichment_service = ChemicalEnrichmentService(db)
cas_numbers = [c.cas_number for c in composition if c.cas_number]
enriched_data = await enrichment_service.get_bulk_chemical_data(cas_numbers)
# 4. Generate enhanced PPE recommendations
ppe_recommendation = await get_full_ppe_recommendation(
sds_detail=parsed_data,
enriched_chemicals=enriched_data # NEW: chemical database data
)
# 5. Store recommendations
await save_ppe_recommendations(db, sds_id, ppe_recommendation)
Chemical Family to Glove Material Mapping
The chemical_family field from PubChem enables precise glove selection:
| Chemical Family | Primary Material | Secondary Material | Avoid |
|---|---|---|---|
| ketone | Butyl | Viton | Nitrile, Latex |
| alcohol | Nitrile | Neoprene | PVC |
| aromatic | Viton | Nitrile (limited) | Latex, Neoprene |
| chlorinated | Viton | Butyl | Nitrile, Neoprene |
| inorganic_acid | Butyl | Neoprene | Leather |
| organic_acid | Nitrile | Neoprene | Latex |
| amine | Butyl | Neoprene | Latex |
| isocyanate | Butyl | Viton | All others |
| epoxy | Nitrile | Butyl | Latex |
| petroleum | Nitrile | Neoprene | Latex |
Regulatory Limits for Respiratory Selection
The osha_pel_ppm and vapor_pressure_mmhg fields enable APF calculation:
def calculate_required_apf(
vapor_pressure_mmhg: float,
osha_pel_ppm: float,
expected_concentration_ppm: float = None
) -> int:
"""
Calculate minimum APF based on chemical properties.
Higher vapor pressure = more airborne = higher APF needed
Lower PEL = more toxic = higher APF needed
"""
if expected_concentration_ppm and osha_pel_ppm:
# Direct calculation if concentration known
hazard_ratio = expected_concentration_ppm / osha_pel_ppm
if hazard_ratio <= 10:
return 10 # Half-mask APR
elif hazard_ratio <= 50:
return 50 # Full-face APR
elif hazard_ratio <= 1000:
return 1000 # PAPR
else:
return 10000 # SCBA
# Heuristic based on vapor pressure when concentration unknown
if vapor_pressure_mmhg > 100: # High volatility
return 50 # Full-face minimum
elif vapor_pressure_mmhg > 10: # Moderate volatility
return 10 # Half-mask acceptable
else:
return 10 # Low volatility, half-mask okay
Important Considerations
Legal Disclaimer
Per CloudSDS research on AI limitations, any PPE recommendations must include:
Disclaimer: These recommendations are provided as guidance only and do not replace professional judgment. Actual PPE selection depends on specific use conditions, duration of exposure, and task requirements. Consult with an EHS professional for workplace-specific requirements. Always follow manufacturer guidelines and applicable OSHA regulations.
Limitations
- Cannot replace EHS professionals - Hazard classification judgments require expertise
- Regulations change - Must be updated as OSHA/GHS standards evolve
- Site-specific factors - Ventilation, exposure duration, task type matter
- Mixture complexity - Some combinations have synergistic effects
Quality Assurance
- Human review of AI-generated recommendations before display
- Regular audits of chemical resistance database
- User feedback mechanism for incorrect recommendations
- Version tracking for regulatory compliance
References
Hand Protection
- OSHA Personal Protective Equipment Overview
- OSHA Glove Selection Chart
- Ansell Chemical Glove Resistance Guide
- ANSI/ISEA 105 Hand Protection Standard
Eye Protection
Respiratory Protection
- OSHA Respiratory Protection Standard (29 CFR 1910.134)
- OSHA Respirator Selection eTool
- OSHA Assigned Protection Factors Publication
- NIOSH Respirator Requirements for Selected Chemicals
- 3M Respirator Selection Guide
Body Protection
General Resources
- PubChem GHS Classification Summary
- ChemRADAR GHS Classification Tool
- CloudSDS - Myths of SDS Automation
Related Internal Documents
- Chemical Database Integration - PubChem caching strategy and SDS parsing integration