Skip to content
Back to skills

Subcontractor Prequalification

ASecurity

Prequalify subcontractors based on safety, financial, and performance criteria.

  • 299 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added February 7, 2026
businesspythonperformance

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill subcontractor-prequalification --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Subcontractor Prequalification?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Subcontractor Prequalification
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/datadrivenconstruction-subcontractor-prequalification/badge)](https://www.skillsdirectory.com/skills/datadrivenconstruction-subcontractor-prequalification)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
slug: "subcontractor-prequalification"
display_name: "Subcontractor Prequalification"
description: "Prequalify subcontractors based on safety, financial, and performance criteria."
---

# Subcontractor Prequalification

## Technical Implementation

```python
import pandas as pd
from datetime import date
from typing import Dict, Any, List
from dataclasses import dataclass, field
from enum import Enum


class QualificationStatus(Enum):
    PENDING = "pending"
    QUALIFIED = "qualified"
    CONDITIONALLY_QUALIFIED = "conditionally_qualified"
    NOT_QUALIFIED = "not_qualified"


@dataclass
class PrequalificationCriteria:
    name: str
    weight: float
    min_score: int
    max_score: int = 10


@dataclass
class SubcontractorApplication:
    app_id: str
    company_name: str
    trade: str
    contact_email: str
    years_in_business: int
    annual_revenue: float
    bonding_capacity: float
    emr_rate: float  # Experience Modification Rate
    status: QualificationStatus
    scores: Dict[str, int] = field(default_factory=dict)
    documents_received: List[str] = field(default_factory=list)
    notes: str = ""

    @property
    def total_score(self) -> float:
        return sum(self.scores.values())


class SubcontractorPrequalification:
    def __init__(self, project_name: str):
        self.project_name = project_name
        self.applications: Dict[str, SubcontractorApplication] = {}
        self.criteria = self._default_criteria()
        self._counter = 0

    def _default_criteria(self) -> List[PrequalificationCriteria]:
        return [
            PrequalificationCriteria("Safety Record", 0.25, 6),
            PrequalificationCriteria("Financial Stability", 0.20, 5),
            PrequalificationCriteria("Experience", 0.20, 6),
            PrequalificationCriteria("References", 0.15, 5),
            PrequalificationCriteria("Capacity", 0.10, 5),
            PrequalificationCriteria("Insurance/Bonding", 0.10, 7)
        ]

    def add_application(self, company_name: str, trade: str, contact_email: str,
                       years_in_business: int, annual_revenue: float,
                       bonding_capacity: float, emr_rate: float) -> SubcontractorApplication:
        self._counter += 1
        app_id = f"PQ-{self._counter:03d}"

        app = SubcontractorApplication(
            app_id=app_id,
            company_name=company_name,
            trade=trade,
            contact_email=contact_email,
            years_in_business=years_in_business,
            annual_revenue=annual_revenue,
            bonding_capacity=bonding_capacity,
            emr_rate=emr_rate,
            status=QualificationStatus.PENDING
        )
        self.applications[app_id] = app
        return app

    def score_application(self, app_id: str, scores: Dict[str, int]):
        if app_id not in self.applications:
            return
        app = self.applications[app_id]
        app.scores = scores
        self._evaluate_qualification(app)

    def _evaluate_qualification(self, app: SubcontractorApplication):
        passed = True
        for criteria in self.criteria:
            score = app.scores.get(criteria.name, 0)
            if score < criteria.min_score:
                passed = False
                break

        if passed and app.total_score >= 60:
            app.status = QualificationStatus.QUALIFIED
        elif app.total_score >= 50:
            app.status = QualificationStatus.CONDITIONALLY_QUALIFIED
        else:
            app.status = QualificationStatus.NOT_QUALIFIED

    def get_qualified(self, trade: str = None) -> List[SubcontractorApplication]:
        qualified = [a for a in self.applications.values()
                    if a.status in [QualificationStatus.QUALIFIED,
                                   QualificationStatus.CONDITIONALLY_QUALIFIED]]
        if trade:
            qualified = [a for a in qualified if a.trade.lower() == trade.lower()]
        return qualified

    def export_register(self, output_path: str):
        data = [{
            'ID': a.app_id,
            'Company': a.company_name,
            'Trade': a.trade,
            'Years': a.years_in_business,
            'Revenue': a.annual_revenue,
            'EMR': a.emr_rate,
            'Status': a.status.value,
            'Score': a.total_score
        } for a in self.applications.values()]
        pd.DataFrame(data).to_excel(output_path, index=False)
```

## Quick Start

```python
prequal = SubcontractorPrequalification("Office Tower")

app = prequal.add_application("ABC Electric", "Electrical", "info@abc.com",
                              years_in_business=15, annual_revenue=10000000,
                              bonding_capacity=5000000, emr_rate=0.85)

prequal.score_application(app.app_id, {
    "Safety Record": 8, "Financial Stability": 7, "Experience": 8,
    "References": 7, "Capacity": 8, "Insurance/Bonding": 9
})

qualified = prequal.get_qualified("Electrical")
```

## Resources
- **DDC Book**: Chapter 3.4 - Procurement

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.