subcontractor-prequalification

v2026.09.24

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

GitHub
安装命令
npx skhub add datadrivenconstruction/subcontractor-prequalification
Markdown
SKILL.md

Subcontractor Prequalification

Technical Implementation

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

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
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

1_DDC_Toolkit/Procurement/subcontractor-prequalification

默认分支

main

最新提交

ce45bbf

Tree SHA

e20d7ee