commit 0e3e22e8cb5a04101197c1ec548fb2386f215da9 Author: Aherobo Ovie Victor Date: Thu Jul 17 22:20:25 2025 +0100 Initial commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0d1de68 --- /dev/null +++ b/.env.example @@ -0,0 +1,20 @@ +# Required API Keys +GROQ_API_KEY=your_groq_api_key +COHERE_API_KEY=your_cohere_api_key + +# Vector Database (Choose one) +# For Pinecone: +VECTOR_DB=pinecone +PINECONE_API_KEY=your_pinecone_api_key +PINECONE_ENVIRONMENT=your_pinecone_environment #us-east-1 +PINECONE_INDEX_NAME=specscomply_documents + +# Or for Weaviate: +# VECTOR_DB=weaviate +# WEAVIATE_URL=your_weaviate_url +# WEAVIATE_API_KEY=your_weaviate_api_key + +# Optional Settings +APP_NAME="Mini SpecsComply Pro" +APP_VERSION="0.1.0" +DEBUG=False \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df51267 --- /dev/null +++ b/.gitignore @@ -0,0 +1,106 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual Environment +venv/ +env/ +ENV/ +.env +.venv +env.bak/ +venv.bak/ + +# IDE - VSCode +.vscode/ +*.code-workspace +.history + +# IDE - PyCharm +.idea/ +*.iml +*.iws +.idea_modules/ + +# IDE - Jupyter Notebook +.ipynb_checkpoints +*.ipynb + +# Testing +.coverage +htmlcov/ +.tox/ +.nox/ +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Logs +*.log +logs/ +log/ + +# Local development +*.db +*.sqlite3 +instance/ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Project specific +temp/ +uploads/ +results/ +*.pid +*.env +.env.* +!.env.example + +# Documentation +docs/_build/ +site/ + +# Package specific +node_modules/ +*.swp +*.swo + +# Vector database files +pinecone_index/ +weaviate_data/ + +# Temporary files +*.tmp +*.temp +*.bak \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f24e038 --- /dev/null +++ b/README.md @@ -0,0 +1,259 @@ +# Mini SpecsComply Pro (SCP) + +## Overview + +Mini SpecsComply Pro (SCP) is a lightweight document compliance and validation tool designed to analyze and verify technical documents against predefined standards and project-specific requirements. It leverages advanced AI models for embedding, reasoning, and ranking to ensure fast and accurate document processing. + +## Features + +- **Document Analysis:** Automated analysis of technical documents for compliance verification +- **AI-Powered Processing:** + - GROQ LLM for deep reasoning and compliance analysis + - Cohere for document embedding and result ranking +- **Advanced Standards Matching:** + - Sophisticated matching algorithm to identify relevant standards + - Section-based analysis for contextual understanding + - Technical term recognition and keyword extraction + - Relevance scoring system for accurate standard selection +- **Custom Standards Support:** + - Upload and manage your own compliance standards + - JSON-based standard definitions with flexible structure +- **Vector Database Support:** + - Pinecone (default) + - Weaviate (alternative) +- **RESTful API:** Built with FastAPI for easy integration +- **Real-time Processing:** Async support for efficient document handling +- **Structured Reports:** Detailed compliance feedback and recommendations with applied standards tracking + +## Prerequisites + +- Python 3.8 or higher +- pip or poetry for package management +- API keys for: + - GROQ + - Cohere + - Pinecone (if using Pinecone) or Weaviate URL (if using Weaviate) + +## Installation + +1. Clone the repository: +```bash +git clone http://23.29.118.76:3000/task/mini-specscomply-pro.git +cd mini-specscomply-pro +``` + +2. Create and activate a virtual environment: +```bash +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +3. Install dependencies: +```bash +pip install -r requirements.txt +``` + +4. Create a `.env` file in the project root: +```env +# Required API Keys +GROQ_API_KEY=your_groq_api_key +COHERE_API_KEY=your_cohere_api_key + +# Vector Database (Choose one) +# For Pinecone: +VECTOR_DB=pinecone +PINECONE_API_KEY=your_pinecone_api_key +PINECONE_ENVIRONMENT=your_pinecone_environment #us-east-1 +PINECONE_INDEX_NAME=specscomply_documents + +# Or for Weaviate: +# VECTOR_DB=weaviate +# WEAVIATE_URL=your_weaviate_url +# WEAVIATE_API_KEY=your_weaviate_api_key + +# Optional Settings +APP_NAME="Mini SpecsComply Pro" +APP_VERSION="0.1.0" +DEBUG=False +``` + +## Running the Application + +### Quick Start +```bash +python launch.py +``` +This will check your environment setup and start the application. Go to `http://localhost:8000` in your browser. + + +The API will be available at: +- API Documentation: `http://localhost:8000/docs` + +## API Endpoints + +- `POST /api/documents/upload` - Upload a document for analysis +- `GET /api/documents/{document_id}` - Get document status and results +- `POST /api/documents/{document_id}/resubmit` - Resubmit a document for re-analysis +- `GET /api/documents/{document_id}/analysis` - Get detailed compliance analysis +- `GET /api/standards` - List all available standards +- `POST /api/standards/upload` - Upload a custom standard definition +- `GET /api/standards/{standard_id}` - Get details of a specific standard +- `GET /api/health` - Health check endpoint + +## Configuration + +The application can be configured through environment variables or the `.env` file. Key configuration options: + +- `DEBUG`: Enable debug mode (default: False) +- `VECTOR_DB`: Choose vector database backend ("pinecone" or "weaviate") +- `EMBEDDING_MODEL`: Cohere embedding model (default: "embed-english-v3.0") +- `RERANKER_MODEL`: Cohere reranker model (default: "rerank-english-v2.0") +- `REASONING_MODEL`: GROQ model (default: "llama-3.3-70b-versatile") + +## Development + +### Project Structure +``` +mini-specscomply-pro/ +├── app/ +│ ├── api/ # API routes and endpoints +│ ├── core/ # Core configuration and models +│ └── services/ # Business logic services +|── Data/ # Sample data and documents +├── requirements.txt # Project dependencies +├── run.py # Application runner +|── launch.py # Setup and launch script +├── .env # Environment variables +├── .gitignore # Git ignore file +├── README.md # Project documentation +``` + +## Advanced Standards Matching + +Mini SpecsComply Pro uses a sophisticated algorithm to match documents with relevant standards: + +1. **Document Analysis** + - Extracts sections and headings from the document + - Identifies key technical terms and phrases + - Recognizes standard references (e.g., "ISO-9001", "IEEE 829") + +2. **Relevance Scoring** + - Calculates weighted scores based on multiple factors: + - Direct standard name matches (highest weight) + - Keyword matches between document and standard + - Section-specific matches (e.g., in References or Requirements sections) + - Technical term matches + - Requirement-specific matches + +3. **Standard Selection** + - Selects the most relevant standards based on score threshold + - Applies these standards during compliance analysis + - Displays applied standards in the compliance report + +This approach ensures that the most appropriate standards are applied to each document, improving the accuracy and relevance of compliance analysis. + +## Document and Standard Formats + +### Compliance Documents + +For best results, structure your compliance documents with clear sections and headings. The system performs better with well-organized documents that include: + +1. **Clear Headings**: Use markdown-style headings (e.g., `# Section Title`) to organize content +2. **Introduction Section**: Provide context and purpose of the document +3. **Scope Section**: Define what the document covers and doesn't cover +4. **Requirements Sections**: Clearly state requirements using terms like "shall", "must", "should" +5. **References Section**: List relevant standards, specifications, or other documents +6. **Technical Details**: Include specific technical information relevant to compliance + +Example document structure: +```markdown +# System Compliance Specification + +## Introduction +This document specifies the compliance requirements for the XYZ system. + +## Scope +This specification applies to all components of the XYZ system. + +## Requirements +### Functional Requirements +1. The system shall process user input within 500ms. +2. The system must maintain data integrity during power failures. + +### Security Requirements +1. All data transmissions shall be encrypted using AES-256. +2. User authentication must comply with NIST guidelines. + +## References +- ISO-9001:2015 Quality Management Systems +- IEEE-829 Software Test Documentation +``` + +### Custom Standard Definitions + +Custom standards are defined in JSON format with the following structure: + +```json +{ + "name": "ISO-9001", + "description": "Quality Management System standard", + "requirements": [ + { + "id": "ISO-9001-4.1", + "description": "The organization shall determine external and internal issues relevant to its purpose and strategic direction.", + "severity": "major" + }, + { + "id": "ISO-9001-4.2", + "description": "The organization shall monitor and review information about these external and internal issues.", + "severity": "minor" + } + ] +} +``` + +You can also define multiple standards in a single file: + +```json +{ + "standards": [ + { + "name": "ISO-9001", + "description": "Quality Management System standard", + "requirements": [...] + }, + { + "name": "IEEE-829", + "description": "Software Test Documentation standard", + "requirements": [...] + } + ] +} +``` + +Requirement severity levels: +- `critical`: Major non-compliance that must be addressed immediately +- `major`: Significant issue that should be addressed soon +- `minor`: Less significant issue that should be addressed when convenient +- `info`: Informational note or suggestion + +## Troubleshooting + +Common issues and solutions: + +1. **Missing API Keys** + - Ensure all required API keys are set in your `.env` file + - Check the API key format and validity + +2. **Vector Database Connection** + - Verify the vector database configuration + - Ensure the selected database service is running and accessible + +3. **Model Errors** + - Check API quotas and limits + - Verify model names in configuration + +4. **Standards Not Being Applied** + - Verify that standards have been uploaded correctly + - Check the logs for standards matching information + - Ensure document content includes relevant terminology for matching \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..3421e51 --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,8 @@ +""" +Mini SpecsComply Pro (SCP) +-------------------------- +A lightweight document compliance and validation tool designed to analyze +and verify technical documents against predefined standards and project-specific requirements. +""" + +__version__ = "0.1.0" \ No newline at end of file diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..dcbf31b --- /dev/null +++ b/app/api/__init__.py @@ -0,0 +1 @@ +"""API routes for the Mini SpecsComply Pro application.""" \ No newline at end of file diff --git a/app/api/document_routes.py b/app/api/document_routes.py new file mode 100644 index 0000000..719a81b --- /dev/null +++ b/app/api/document_routes.py @@ -0,0 +1,256 @@ +# Document API routes +from fastapi import APIRouter, UploadFile, File, HTTPException, BackgroundTasks, Query +from fastapi.responses import JSONResponse, HTMLResponse +from typing import List, Optional, Dict +import uuid +from loguru import logger + +from app.core.models import ( + DocumentUploadResponse, + DocumentAnalysisResponse, + DocumentStatus +) +from app.services.document import DocumentService +from app.services.embedding import EmbeddingService +from app.services.reasoning import ReasoningService +from app.services.standards import StandardsService +from app.utils.helpers import generate_html_report + +# Create services +embedding_service = EmbeddingService() +reasoning_service = ReasoningService() +standards_service = StandardsService() + +# Log the standards service instance ID to verify singleton pattern +logger.info(f"Document API - Using StandardsService instance: {id(standards_service)}") +logger.info(f"Document API - Initial standards count: {len(standards_service.standards)}") + +document_service = DocumentService( + embedding_service=embedding_service, + reasoning_service=reasoning_service, + standards_service=standards_service +) + +# Create router +router = APIRouter(prefix="/documents", tags=["documents"]) + +@router.post("/upload", response_model=DocumentUploadResponse) +async def upload_document(file: UploadFile = File(...)): + """ + Upload and process a document. + + Args: + file: The document file to upload + + Returns: + DocumentUploadResponse with document ID + """ + try: + # Check file extension + if not file.filename: + raise HTTPException(status_code=400, detail="Filename is required") + + # Process document + document = await document_service.upload_document(file.file, file.filename) + + return DocumentUploadResponse( + document_id=document.id, + filename=document.metadata.filename, + status=document.status, + message="Document uploaded successfully and is being processed." + ) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error processing document: {str(e)}") + +@router.get("/{doc_id}/analysis", response_model=DocumentAnalysisResponse) +async def get_document_analysis(doc_id: str, format: Optional[str] = Query(None, description="Response format (json or html)")): + """ + Get analysis results for a document. + + Args: + doc_id: The document ID + format: Response format (json or html) + + Returns: + DocumentAnalysisResponse with analysis results + """ + try: + # Retrieve document + document = await document_service.get_document(doc_id) + if not document: + raise HTTPException(status_code=404, detail=f"Document with ID {doc_id} not found") + + # Check if document has been processed + if document.status != DocumentStatus.COMPLETED: + return DocumentAnalysisResponse( + document_id=doc_id, + status=document.status, + message=f"Document is in {document.status} state. Please try again later." + ) + + # Get the latest report + if not document.reports: + raise HTTPException(status_code=404, detail=f"No analysis reports found for document {doc_id}") + + latest_report_id = document.reports[-1] + report = await document_service.get_report(latest_report_id) + + if not report: + raise HTTPException(status_code=404, detail=f"Report {latest_report_id} not found") + + # Check if HTML format is requested + if format == "html": + # Convert report to HTML + report_data = { + "document_name": document.metadata.filename, + "timestamp": report.timestamp.strftime("%Y-%m-%d %H:%M:%S"), + "compliance_score": report.compliance_score, + "summary": report.summary, + "applied_standards": report.applied_standards, + "issues": [ + { + "section": issue.section, + "description": issue.description, + "level": issue.level.value, + "reasoning": issue.reasoning, + "standard_references": issue.standard_references, + "recommendation": issue.recommendation + } + for issue in report.issues + ] + } + + html_content = generate_html_report(report_data) + return HTMLResponse(content=html_content) + + # Return JSON response + return DocumentAnalysisResponse( + document_id=doc_id, + status=document.status, + report=report, + message="Analysis completed successfully." + ) + + except HTTPException: + raise + except Exception as e: + logger.error(f"Error retrieving document analysis: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error retrieving document analysis: {str(e)}") + +@router.post("/{doc_id}/resubmit", response_model=DocumentUploadResponse) +async def resubmit_document( + doc_id: str, + file: UploadFile = File(...), + background_tasks: BackgroundTasks = None +): + """ + Resubmit a document with changes. + + Args: + doc_id: The document ID to resubmit + file: The updated document file + background_tasks: Background tasks handler + + Returns: + DocumentUploadResponse with document ID + """ + try: + # Check if document exists + document = await document_service.get_document(doc_id) + if not document: + raise HTTPException(status_code=404, detail=f"Document with ID {doc_id} not found") + + # Process resubmitted document + updated_document = await document_service.resubmit_document(doc_id, file.file) + + return DocumentUploadResponse( + document_id=updated_document.id, + filename=updated_document.metadata.filename, + status=updated_document.status, + message=f"Document (version {updated_document.version}) resubmitted successfully and is being processed." + ) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(f"Error resubmitting document: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error resubmitting document: {str(e)}") + +@router.get("/{doc_id}", response_model=Dict) +async def get_document_info(doc_id: str): + """ + Get document information. + + Args: + doc_id: The document ID + + Returns: + Document information + """ + try: + # Retrieve document + document = await document_service.get_document(doc_id) + if not document: + raise HTTPException(status_code=404, detail=f"Document with ID {doc_id} not found") + + # Convert to dict for response + return { + "document_id": document.id, + "filename": document.metadata.filename, + "file_type": document.metadata.file_type, + "file_size": document.metadata.file_size, + "upload_timestamp": document.metadata.upload_timestamp, + "last_modified": document.metadata.last_modified, + "status": document.status, + "version": document.version, + "reports": document.reports + } + except HTTPException: + raise + except Exception as e: + logger.error(f"Error retrieving document info: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error retrieving document info: {str(e)}") + +@router.get("/", response_model=List[Dict]) +async def list_documents(): + """ + List all documents. + + Returns: + List of document summaries + """ + try: + documents = [] + for doc_id, document in document_service.documents.items(): + documents.append({ + "document_id": doc_id, + "filename": document.metadata.filename, + "status": document.status, + "version": document.version, + "upload_timestamp": document.metadata.upload_timestamp + }) + return documents + except Exception as e: + logger.error(f"Error listing documents: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error listing documents: {str(e)}") + +@router.get("/{doc_id}/stats", response_model=Dict) +async def get_document_stats(doc_id: str): + """ + Get statistics for a document. + + Args: + doc_id: The document ID + + Returns: + Document statistics + """ + try: + stats = await document_service.get_document_stats(doc_id) + return stats + except ValueError as e: + raise HTTPException(status_code=404, detail=str(e)) + except Exception as e: + logger.error(f"Error retrieving document stats: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error retrieving document stats: {str(e)}") diff --git a/app/api/routes.py b/app/api/routes.py new file mode 100644 index 0000000..20219a1 --- /dev/null +++ b/app/api/routes.py @@ -0,0 +1,15 @@ +# API routes - Main router +from fastapi import APIRouter + +# Import sub-routers +from app.api.document_routes import router as document_router +from app.api.standards_routes import router as standards_router + +# Create main router +router = APIRouter() + +# Include sub-routers +router.include_router(document_router) +router.include_router(standards_router) + +# Add any additional routes that don't fit in the other routers here diff --git a/app/api/standards_routes.py b/app/api/standards_routes.py new file mode 100644 index 0000000..665b778 --- /dev/null +++ b/app/api/standards_routes.py @@ -0,0 +1,113 @@ +# Standards API routes +from fastapi import APIRouter, UploadFile, File, HTTPException, Query +from typing import List, Optional +from loguru import logger + +from app.core.models import Standard, StandardUploadResponse +from app.services.standards import StandardsService + +# Create services +standards_service = StandardsService() + +# Create router +router = APIRouter(prefix="/standards", tags=["standards"]) + +@router.get("/", response_model=List[Standard]) +async def get_all_standards(): + """ + Get all available compliance standards. + + Returns: + List of all standards + """ + try: + standards = await standards_service.get_all_standards() + return standards + except Exception as e: + logger.error(f"Error retrieving standards: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error retrieving standards: {str(e)}") + +@router.get("/{standard_id}", response_model=Standard) +async def get_standard(standard_id: str): + """ + Get a specific standard by ID. + + Args: + standard_id: The standard ID + + Returns: + Standard details + """ + try: + standard = await standards_service.get_standard(standard_id) + if not standard: + raise HTTPException(status_code=404, detail=f"Standard with ID {standard_id} not found") + return standard + except HTTPException: + raise + except Exception as e: + logger.error(f"Error retrieving standard: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error retrieving standard: {str(e)}") + +@router.post("/upload", response_model=StandardUploadResponse) +async def upload_standard(file: UploadFile = File(...)): + """ + Upload a new compliance standard definition. + + Args: + file: JSON file containing standard definition + + Returns: + StandardUploadResponse with standard ID + """ + try: + # Check file extension + if not file.filename: + raise HTTPException(status_code=400, detail="Filename is required") + + if not file.filename.lower().endswith('.json'): + raise HTTPException(status_code=400, detail="Standard definition must be a JSON file") + + # Log the standards service instance ID to verify singleton pattern + logger.info(f"Standards API - Using StandardsService instance: {id(standards_service)}") + logger.info(f"Standards API - Standards count before upload: {len(standards_service.standards)}") + + # Process standard + standard = await standards_service.upload_standard(file.file, file.filename) + + # Log the updated standards count + logger.info(f"Standards API - Standards count after upload: {len(standards_service.standards)}") + logger.info(f"Standards API - Uploaded standard: {standard.name} (ID: {standard.id})") + + return StandardUploadResponse( + standard_id=standard.id, + name=standard.name, + requirement_count=len(standard.requirements), + message="Standard uploaded successfully." + ) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(f"Error processing standard: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error processing standard: {str(e)}") + +@router.get("/search/", response_model=List[Standard]) +async def search_standards(name: Optional[str] = Query(None, description="Standard name to search for")): + """ + Search for standards by name. + + Args: + name: Standard name to search for (optional) + + Returns: + List of matching standards + """ + try: + if name: + standard = await standards_service.get_standard_by_name(name) + return [standard] if standard else [] + else: + return await standards_service.get_all_standards() + except Exception as e: + logger.error(f"Error searching standards: {str(e)}") + raise HTTPException(status_code=500, detail=f"Error searching standards: {str(e)}") diff --git a/app/core/__init__.py b/app/core/__init__.py new file mode 100644 index 0000000..03f661b --- /dev/null +++ b/app/core/__init__.py @@ -0,0 +1 @@ +"""Core functionality for the Mini SpecsComply Pro application.""" \ No newline at end of file diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..e6065b8 --- /dev/null +++ b/app/core/config.py @@ -0,0 +1,44 @@ +import os +from typing import Optional +from pydantic import BaseModel +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + +class Settings(BaseModel): + """Application settings loaded from environment variables.""" + + # Application information + APP_NAME: str = os.getenv("APP_NAME", "Mini SpecsComply Pro") + APP_VERSION: str = os.getenv("APP_VERSION", "0.1.0") + DEBUG: bool = os.getenv("DEBUG", "False").lower() in ("true", "1", "t") + + # API keys + GROQ_API_KEY: Optional[str] = os.getenv("GROQ_API_KEY") + COHERE_API_KEY: Optional[str] = os.getenv("COHERE_API_KEY") + + # Vector database settings + # Pinecone + PINECONE_API_KEY: Optional[str] = os.getenv("PINECONE_API_KEY") + PINECONE_INDEX_NAME: str = os.getenv("PINECONE_INDEX_NAME", "specscomply_documents") + + # Weaviate + WEAVIATE_URL: Optional[str] = os.getenv("WEAVIATE_URL") + WEAVIATE_API_KEY: Optional[str] = os.getenv("WEAVIATE_API_KEY") + + # Models + EMBEDDING_MODEL: str = "embed-english-v3.0" # Default embedding model + RERANKER_MODEL: str = "rerank-english-v2.0" # Default reranker model + REASONING_MODEL: str = "llama-3.3-70b-versatile" # Default reasoning model + PROCESSING_MODEL: str = "llama-3.3-70b-versatile" # Default quick processing model + + # Vector database selector (pinecone or weaviate) + VECTOR_DB: str = os.getenv("VECTOR_DB", "pinecone").lower() + + class Config: + env_file = ".env" + case_sensitive = True + +# Create global settings instance +settings = Settings() \ No newline at end of file diff --git a/app/core/models.py b/app/core/models.py new file mode 100644 index 0000000..cc8e44a --- /dev/null +++ b/app/core/models.py @@ -0,0 +1,127 @@ +# Data models +from pydantic import BaseModel, Field +from typing import List, Dict, Optional, Any, Union +from datetime import datetime +from enum import Enum +import uuid + +class DocumentStatus(str, Enum): + """Enum for document processing status.""" + PENDING = "pending" + PROCESSING = "processing" + COMPLETED = "completed" + FAILED = "failed" + +class ComplianceLevel(str, Enum): + """Enum for compliance severity levels.""" + CRITICAL = "critical" + MAJOR = "major" + MINOR = "minor" + INFO = "info" + +class ComplianceIssue(BaseModel): + """Model for compliance issues found in the document.""" + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + section: str + description: str + level: ComplianceLevel + line_number: Optional[int] = None + reasoning: str = "" # Detailed explanation of why this is an issue + standard_references: List[str] = [] # References to specific standards or requirements + recommendation: str + +class DocumentMetadata(BaseModel): + """Model for document metadata.""" + filename: str + file_type: str + file_size: int # In bytes + upload_timestamp: datetime = Field(default_factory=datetime.now) + last_modified: Optional[datetime] = None + +class DocumentEmbedding(BaseModel): + """Model for document embeddings.""" + embedding_id: str + embedding_model: str + vector_db: str + sections: Dict[str, str] # Section name to section ID in vector DB + +class ComplianceReport(BaseModel): + """Model for the generated compliance report.""" + report_id: str = Field(default_factory=lambda: str(uuid.uuid4())) + document_id: str + timestamp: datetime = Field(default_factory=datetime.now) + compliance_score: float # 0.0 to 1.0 + summary: str + issues: List[ComplianceIssue] = [] + applied_standards: List[str] = [] # Standards used for analysis + + @property + def critical_issues_count(self) -> int: + return sum(1 for issue in self.issues if issue.level == ComplianceLevel.CRITICAL) + + @property + def major_issues_count(self) -> int: + return sum(1 for issue in self.issues if issue.level == ComplianceLevel.MAJOR) + + @property + def minor_issues_count(self) -> int: + return sum(1 for issue in self.issues if issue.level == ComplianceLevel.MINOR) + + @property + def info_issues_count(self) -> int: + return sum(1 for issue in self.issues if issue.level == ComplianceLevel.INFO) + +class Document(BaseModel): + """Model for document tracking.""" + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + metadata: DocumentMetadata + embedding: Optional[DocumentEmbedding] = None + status: DocumentStatus = DocumentStatus.PENDING + version: int = 1 # Incremented on resubmissions + reports: List[str] = [] # List of report IDs + +class DocumentUploadResponse(BaseModel): + """Response model for document uploads.""" + document_id: str + filename: str + status: DocumentStatus + message: str + +class DocumentAnalysisResponse(BaseModel): + """Response model for document analysis retrieval.""" + document_id: str + status: DocumentStatus + report: Optional[ComplianceReport] = None + message: str + + +class RequirementSeverity(str, Enum): + """Enum for requirement severity levels.""" + CRITICAL = "critical" + MAJOR = "major" + MINOR = "minor" + INFO = "info" + + +class Requirement(BaseModel): + """Model for a compliance requirement.""" + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + description: str + severity: RequirementSeverity + details: Optional[str] = None + + +class Standard(BaseModel): + """Model for a compliance standard.""" + id: str = Field(default_factory=lambda: str(uuid.uuid4())) + name: str + description: str + requirements: List[Requirement] = [] + + +class StandardUploadResponse(BaseModel): + """Response model for standard uploads.""" + standard_id: str + name: str + requirement_count: int + message: str \ No newline at end of file diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..bf284ef --- /dev/null +++ b/app/main.py @@ -0,0 +1,123 @@ +from fastapi import FastAPI, Request, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse, HTMLResponse +from fastapi.staticfiles import StaticFiles +from fastapi.templating import Jinja2Templates +import os +import uvicorn +from loguru import logger +import sys +from pathlib import Path + +from app.core.config import settings +from app.api.routes import router as api_router + +# Configure logging +logger.remove() +logger.add( + sys.stdout, + format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", + level="DEBUG" if settings.DEBUG else "INFO", +) + +# Create FastAPI app +app = FastAPI( + title=settings.APP_NAME, + version=settings.APP_VERSION, + description="A lightweight document compliance and validation tool", + docs_url="/docs", + redoc_url="/redoc", + openapi_url="/openapi.json", +) + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], # In production, this should be restricted + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Create templates directory +templates_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "templates") +os.makedirs(templates_dir, exist_ok=True) +templates = Jinja2Templates(directory=templates_dir) + +# Create static files directory if it doesn't exist +static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static") +if not os.path.exists(static_dir): + os.makedirs(static_dir, exist_ok=True) + +# Ensure CSS and JS directories exist +css_dir = os.path.join(static_dir, "css") +js_dir = os.path.join(static_dir, "js") +os.makedirs(css_dir, exist_ok=True) +os.makedirs(js_dir, exist_ok=True) + +# Mount static files +app.mount("/static", StaticFiles(directory=static_dir), name="static") + +# Include API routes +app.include_router(api_router, prefix="/api") + +# Root endpoint - serve the frontend +@app.get("/", response_class=HTMLResponse) +async def root(request: Request): + # Check if index.html exists in static directory + index_path = os.path.join(static_dir, "index.html") + + if os.path.exists(index_path): + with open(index_path, "r") as f: + return HTMLResponse(content=f.read()) + + # If not found, return a simple HTML response + return HTMLResponse( + content=f""" + + + + {settings.APP_NAME} + + + +

{settings.APP_NAME}

+

Welcome to {settings.APP_NAME}, a lightweight document compliance and validation tool.

+

This application is currently running in API-only mode.

+ + + + """ + ) + +# Health check endpoint +@app.get("/health") +async def health(): + return {"status": "healthy"} + +# Global exception handler +@app.exception_handler(Exception) +async def global_exception_handler(request: Request, exc: Exception): + logger.error(f"Unhandled exception: {str(exc)}") + return JSONResponse( + status_code=500, + content={"message": "An unexpected error occurred", "detail": str(exc)}, + ) + +if __name__ == "__main__": + uvicorn.run( + "app.main:app", + host="0.0.0.0", + port=8000, + reload=settings.DEBUG, + log_level="debug" if settings.DEBUG else "info", + ) \ No newline at end of file diff --git a/app/services/__init__.py b/app/services/__init__.py new file mode 100644 index 0000000..15543b9 --- /dev/null +++ b/app/services/__init__.py @@ -0,0 +1 @@ +"""Services for the Mini SpecsComply Pro application.""" \ No newline at end of file diff --git a/app/services/document.py b/app/services/document.py new file mode 100644 index 0000000..d3c7b6c --- /dev/null +++ b/app/services/document.py @@ -0,0 +1,461 @@ +# Document processing +import os +import uuid +from datetime import datetime, timedelta +from typing import Dict, List, Optional, BinaryIO, Tuple +import re +from loguru import logger + +from app.core.models import ( + Document, + DocumentMetadata, + DocumentStatus, + ComplianceReport, + ComplianceIssue, + ComplianceLevel, + DocumentEmbedding +) +from app.services.embedding import EmbeddingService +from app.services.reasoning import ReasoningService +from app.services.standards import StandardsService +from app.utils.token_counter import count_tokens, truncate_by_tokens + +class DocumentService: + """Service for handling document processing and storage.""" + + def __init__(self, embedding_service: EmbeddingService, reasoning_service: ReasoningService, standards_service: Optional[StandardsService] = None): + """Initialize with required services.""" + self.embedding_service = embedding_service + self.reasoning_service = reasoning_service + self.standards_service = standards_service or StandardsService() + self.documents = {} # In-memory storage for documents (replace with DB in production) + self.reports = {} # In-memory storage for reports (replace with DB in production) + + async def upload_document(self, file: BinaryIO, filename: str) -> Document: + """ + Process an uploaded document. + + Args: + file: The document file + filename: Name of the uploaded file + + Returns: + Document object with metadata + """ + # Validate file type + if not self._validate_file_type(filename): + raise ValueError(f"Unsupported file type. Supported types: .txt, .md, .rst, .doc, .docx, .pdf") + + # Get file content + content = await self._read_file_content(file) + + # Extract file metadata + file_size = len(content) + file_type = self._get_file_type(filename) + + # Create document metadata + metadata = DocumentMetadata( + filename=filename, + file_type=file_type, + file_size=file_size, + upload_timestamp=datetime.now(), + last_modified=datetime.now() + ) + + # Create document object + document_id = str(uuid.uuid4()) + document = Document( + id=document_id, + metadata=metadata, + status=DocumentStatus.PENDING, + version=1 + ) + + # Store document in memory + self.documents[document_id] = document + + # Start processing + try: + await self._process_document(document_id, content) + except Exception as e: + logger.error(f"Error processing document {document_id}: {str(e)}") + document.status = DocumentStatus.FAILED + raise + + return document + + async def get_document(self, document_id: str) -> Optional[Document]: + """ + Retrieve a document by ID. + + Args: + document_id: The ID of the document to retrieve + + Returns: + Document object if found, None otherwise + """ + return self.documents.get(document_id) + + async def get_report(self, report_id: str) -> Optional[ComplianceReport]: + """ + Retrieve a compliance report by ID. + + Args: + report_id: The ID of the report to retrieve + + Returns: + ComplianceReport object if found, None otherwise + """ + return self.reports.get(report_id) + + async def resubmit_document(self, document_id: str, file: BinaryIO) -> Document: + """ + Resubmit a document with changes. + + Args: + document_id: The ID of the document to resubmit + file: The updated document file + + Returns: + Updated Document object + """ + # Check if document exists + document = await self.get_document(document_id) + if not document: + raise ValueError(f"Document with ID {document_id} not found") + + # Get file content + content = await self._read_file_content(file) + + # Update document metadata + document.metadata.file_size = len(content) + document.metadata.last_modified = datetime.now() + document.version += 1 + document.status = DocumentStatus.PENDING + + # Process the updated document + try: + await self._process_document(document_id, content) + except Exception as e: + logger.error(f"Error processing resubmitted document {document_id}: {str(e)}") + document.status = DocumentStatus.FAILED + + return document + + async def process_document(self, document_id: str, content: str) -> ComplianceReport: + """ + Process document and generate compliance report. + + Args: + document_id: The ID of the document + content: Document content + + Returns: + ComplianceReport object + """ + try: + # Get the document + document = self.documents.get(document_id) + if not document: + raise ValueError(f"Document {document_id} not found") + + # Split document into sections + sections = self._split_into_sections(content) + + # Generate embeddings for sections + document.embedding = await self.embedding_service.embed_document(document_id, sections) + + # Identify relevant standards for the document + if self.standards_service: + # Log the standards service instance ID to verify singleton pattern + logger.info(f"Using StandardsService instance: {id(self.standards_service)}") + logger.info(f"Standards count before matching: {len(self.standards_service.standards)}") + + standard_names = await self.standards_service.get_standard_names_for_document(content) + logger.info(f"Identified standards for document {document_id}: {standard_names}") + else: + logger.warning(f"No StandardsService available for document {document_id}") + standard_names = ["ISO-9001", "IEEE-829", "RFC-2119"] + + # Use reasoning service for compliance analysis + report = await self.reasoning_service.analyze_document(document_id, sections, standard_names) + + # Store the report + self.reports[report.report_id] = report + + return report + + except Exception as e: + logger.error(f"Error in document processing: {str(e)}") + raise + + async def _read_file_content(self, file: BinaryIO) -> str: + """ + Read and decode file content. + + Args: + file: The file to read + + Returns: + File content as string + """ + file_content = file.read() + + # Try to decode as UTF-8 + try: + return file_content.decode('utf-8') + except UnicodeDecodeError: + # Try other encodings if UTF-8 fails + try: + return file_content.decode('latin-1') + except: + raise ValueError("Unable to decode file content. Please ensure file is text-based.") + + def _get_file_type(self, filename: str) -> str: + """ + Determine file type from filename. + + Args: + filename: The name of the file + + Returns: + File type (extension) + """ + _, extension = os.path.splitext(filename) + return extension.lstrip('.').lower() + + def _validate_file_type(self, filename: str) -> bool: + """ + Validate if the file type is supported. + + Args: + filename: Name of the file to validate + + Returns: + bool: True if file type is supported, False otherwise + """ + SUPPORTED_EXTENSIONS = {'.txt', '.md', '.rst', '.doc', '.docx', '.pdf'} + _, ext = os.path.splitext(filename) + return ext.lower() in SUPPORTED_EXTENSIONS + + def _split_into_sections(self, content: str) -> Dict[str, str]: + """ + Split document content into sections. + + Args: + content: The document content + + Returns: + Dictionary mapping section names to section content + """ + # This is a simple implementation - in production, you would use more advanced + # techniques like heading detection, markdown parsing, etc. + + # For simplicity, we'll just split by markdown headings + sections = {} + + # Add the whole document as one section + sections["full_document"] = content + + # Try to split by markdown headings + heading_pattern = re.compile(r'^(#{1,3})\s+(.+)$', re.MULTILINE) + matches = list(heading_pattern.finditer(content)) + + if matches: + for i, match in enumerate(matches): + heading_level = len(match.group(1)) + section_name = match.group(2).strip() + + # Get section content (from this heading to the next, or to the end) + start_pos = match.end() + end_pos = matches[i+1].start() if i < len(matches) - 1 else len(content) + + section_content = content[start_pos:end_pos].strip() + section_key = f"h{heading_level}_{section_name}" + + sections[section_key] = section_content + else: + # No headings found, try to split by newlines into paragraphs + paragraphs = [p for p in content.split('\n\n') if p.strip()] + + for i, paragraph in enumerate(paragraphs): + if len(paragraph) > 100: # Only include substantial paragraphs + sections[f"paragraph_{i+1}"] = paragraph + + return sections + + async def _generate_mock_report(self, document_id: str, sections: Dict[str, str]) -> ComplianceReport: + """ + Generate a mock compliance report for development/testing. + + Args: + document_id: The ID of the document + sections: Dictionary of document sections + + Returns: + ComplianceReport object + """ + # In production, this would use the reasoning service + # For now, we'll generate a simple mock report + + # Create some mock issues + issues = [] + + if "full_document" in sections: + content = sections["full_document"] + + # Check for missing sections (mock check) + if "introduction" not in content.lower(): + issues.append(ComplianceIssue( + section="Document Structure", + description="Missing introduction section", + level=ComplianceLevel.MAJOR, + recommendation="Add an introduction section to provide context for the document" + )) + + # Check for formatting issues (mock check) + if content.count('#') < 3: + issues.append(ComplianceIssue( + section="Formatting", + description="Insufficient section headings", + level=ComplianceLevel.MINOR, + recommendation="Use markdown headings to better structure the document" + )) + + # Check for technical compliance (mock check) + if "compliance" in content.lower() and "standard" not in content.lower(): + issues.append(ComplianceIssue( + section="Technical Content", + description="Mentions compliance but doesn't reference specific standards", + level=ComplianceLevel.CRITICAL, + recommendation="Specify which standards or regulations the document complies with" + )) + + # Calculate mock compliance score + if issues: + compliance_score = max(0.0, 1.0 - (len(issues) * 0.1)) + else: + compliance_score = 1.0 + + # Create summary based on issues + if not issues: + summary = "The document meets all compliance requirements. No issues found." + else: + critical_count = sum(1 for i in issues if i.level == ComplianceLevel.CRITICAL) + major_count = sum(1 for i in issues if i.level == ComplianceLevel.MAJOR) + minor_count = sum(1 for i in issues if i.level == ComplianceLevel.MINOR) + + summary = f"The document has {len(issues)} compliance issues: " + if critical_count: + summary += f"{critical_count} critical, " + if major_count: + summary += f"{major_count} major, " + if minor_count: + summary += f"{minor_count} minor." + else: + summary = summary.rstrip(", ") + "." + + summary += " See detailed report for recommendations." + + # Create report + report = ComplianceReport( + document_id=document_id, + compliance_score=compliance_score, + summary=summary, + issues=issues + ) + + return report + + async def _process_document(self, document_id: str, content: str) -> None: + """ + Internal method to process a document and update its status. + + Args: + document_id: The ID of the document to process + content: The document content + """ + try: + # Get the document + document = self.documents.get(document_id) + if not document: + raise ValueError(f"Document {document_id} not found") + + # Update status to processing + document.status = DocumentStatus.PROCESSING + + # Generate compliance report + report = await self.process_document(document_id, content) + + # Store report ID in document + document.reports.append(report.report_id) + + # Update document status + document.status = DocumentStatus.COMPLETED + + except Exception as e: + # Update document status to failed + if document: + document.status = DocumentStatus.FAILED + raise + + async def get_document_stats(self, document_id: str) -> Dict[str, any]: + """ + Get statistics for a document. + + Args: + document_id: The ID of the document + + Returns: + Dictionary containing document statistics + """ + document = await self.get_document(document_id) + if not document: + raise ValueError(f"Document {document_id} not found") + + latest_report = None + if document.reports: + latest_report = await self.get_report(document.reports[-1]) + + stats = { + "document_id": document_id, + "version": document.version, + "status": document.status, + "file_size": document.metadata.file_size, + "upload_date": document.metadata.upload_timestamp, + "last_modified": document.metadata.last_modified, + "num_reports": len(document.reports), + "latest_compliance_score": latest_report.compliance_score if latest_report else None, + "critical_issues": latest_report.critical_issues_count if latest_report else 0, + "major_issues": latest_report.major_issues_count if latest_report else 0, + "minor_issues": latest_report.minor_issues_count if latest_report else 0 + } + + return stats + + async def cleanup_old_documents(self, days: int = 30) -> List[str]: + """ + Remove documents older than specified days. + + Args: + days: Number of days after which documents should be removed + + Returns: + List of removed document IDs + """ + cutoff_date = datetime.now() - timedelta(days=days) + removed_ids = [] + + for doc_id, document in list(self.documents.items()): + if document.metadata.upload_timestamp < cutoff_date: + # Remove associated reports + for report_id in document.reports: + self.reports.pop(report_id, None) + + # Remove document + self.documents.pop(doc_id) + removed_ids.append(doc_id) + + return removed_ids + + diff --git a/app/services/embedding.py b/app/services/embedding.py new file mode 100644 index 0000000..6a1216f --- /dev/null +++ b/app/services/embedding.py @@ -0,0 +1,254 @@ +import cohere +from typing import List, Dict, Any, Optional +import uuid +from pinecone import Pinecone +import weaviate +from loguru import logger + +from app.core.config import settings +from app.core.models import DocumentEmbedding + +class EmbeddingService: + """Service for document embedding and vector database operations.""" + + def __init__(self): + """Initialize the embedding service with the Cohere client and vector DB.""" + # Initialize Cohere client + self.cohere_client = cohere.Client(settings.COHERE_API_KEY) + + # Initialize vector database client based on configuration + self.vector_db_client = self._init_vector_db() + self.embedding_model = settings.EMBEDDING_MODEL + + def _init_vector_db(self) -> Any: + """Initialize the vector database client based on settings.""" + if settings.VECTOR_DB == "pinecone" and settings.PINECONE_API_KEY: + # Initialize Pinecone with new API + pc = Pinecone(api_key=settings.PINECONE_API_KEY) + + # Check if index exists, if not create it + if settings.PINECONE_INDEX_NAME not in [idx["name"] for idx in pc.list_indexes()]: + pc.create_index( + name=settings.PINECONE_INDEX_NAME, + dimension=1024, # Cohere embed-english-v3.0 dimension + metric="cosine" + ) + + # Return the index + return pc.Index(settings.PINECONE_INDEX_NAME) + + elif settings.VECTOR_DB == "weaviate" and settings.WEAVIATE_URL: + # Initialize Weaviate + auth_config = weaviate.auth.AuthApiKey(api_key=settings.WEAVIATE_API_KEY) if settings.WEAVIATE_API_KEY else None + client = weaviate.Client( + url=settings.WEAVIATE_URL, + auth_client_secret=auth_config + ) + # Check if schema exists, if not create it + if not client.schema.contains().get("classes", []): + class_obj = { + "class": "Document", + "vectorizer": "none", # We'll provide our own vectors + "properties": [ + { + "name": "content", + "dataType": ["text"] + }, + { + "name": "document_id", + "dataType": ["string"] + }, + { + "name": "section_name", + "dataType": ["string"] + } + ] + } + client.schema.create_class(class_obj) + return client + + else: + logger.warning("No valid vector database configuration found. Using mock implementation.") + return MockVectorDB() + + async def embed_document(self, document_id: str, sections: Dict[str, str]) -> DocumentEmbedding: + """ + Embed document sections and store in vector database. + + Args: + document_id: Unique identifier for the document + sections: Dictionary mapping section names to section content + + Returns: + DocumentEmbedding object with embedding metadata + """ + section_ids = {} + + for section_name, content in sections.items(): + # Generate embedding for section content + try: + embedding_response = self.cohere_client.embed( + texts=[content], + model=self.embedding_model, + input_type="search_document" + ) + embedding_vector = embedding_response.embeddings[0] + + # Generate a unique ID for this section + section_id = f"{document_id}_{section_name}_{str(uuid.uuid4())[:8]}" + + # Store in vector database + if settings.VECTOR_DB == "pinecone": + self.vector_db_client.upsert( + vectors=[{ + "id": section_id, + "values": embedding_vector, + "metadata": { + "document_id": document_id, + "section_name": section_name, + "content": content[:1000] # Store truncated content for context + } + }], + namespace=document_id + ) + + elif settings.VECTOR_DB == "weaviate": + self.vector_db_client.data_object.create( + class_name="Document", + data_object={ + "content": content, + "document_id": document_id, + "section_name": section_name + }, + uuid=section_id, + vector=embedding_vector + ) + + # Store the section ID + section_ids[section_name] = section_id + logger.info(f"Successfully embedded section '{section_name}' for document {document_id}") + + except Exception as e: + logger.error(f"Error embedding section '{section_name}': {str(e)}") + raise + + # Create and return DocumentEmbedding object + embedding = DocumentEmbedding( + embedding_id=str(uuid.uuid4()), + embedding_model=self.embedding_model, + vector_db=settings.VECTOR_DB, + sections=section_ids + ) + + return embedding + + async def retrieve_similar_sections(self, query: str, document_id: Optional[str] = None, top_k: int = 5) -> List[Dict[str, Any]]: + """ + Retrieve similar document sections for a query. + + Args: + query: The query text to find similar sections for + document_id: Optional document ID to restrict search + top_k: Number of results to return + + Returns: + List of similar sections with metadata + """ + # Generate embedding for query + query_embedding = self.cohere_client.embed( + texts=[query], + model=self.embedding_model, + input_type="search_query" + ).embeddings[0] + + # Search vector database + if settings.VECTOR_DB == "pinecone": + namespace = document_id if document_id else None + results = self.vector_db_client.query( + vector=query_embedding, + top_k=top_k, + namespace=namespace, + include_metadata=True + ) + + # Format results + similar_sections = [] + for match in results.matches: + similar_sections.append({ + "section_id": match.id, + "document_id": match.metadata["document_id"], + "section_name": match.metadata["section_name"], + "content": match.metadata.get("content", ""), + "score": match.score + }) + + elif settings.VECTOR_DB == "weaviate": + query_builder = self.vector_db_client.query.get( + "Document", ["content", "document_id", "section_name"] + ).with_near_vector({ + "vector": query_embedding + }).with_limit(top_k) + + if document_id: + query_builder = query_builder.with_where({ + "path": ["document_id"], + "operator": "Equal", + "valueString": document_id + }) + + results = query_builder.do() + + # Format results + similar_sections = [] + for item in results.get("data", {}).get("Get", {}).get("Document", []): + similar_sections.append({ + "section_id": item.get("_additional", {}).get("id"), + "document_id": item.get("document_id"), + "section_name": item.get("section_name"), + "content": item.get("content", ""), + "score": item.get("_additional", {}).get("distance") + }) + + else: + # Mock implementation + similar_sections = [] + + return similar_sections + + +class MockVectorDB: + """Mock vector database for development without actual vector DB.""" + + def __init__(self): + self.vectors = {} + logger.warning("Using mock vector database. Not suitable for production.") + + def upsert(self, vectors, namespace=None): + """Mock upsert method.""" + namespace = namespace or "default" + if namespace not in self.vectors: + self.vectors[namespace] = {} + + for vector in vectors: + vector_id = vector['id'] + metadata = vector['metadata'] + self.vectors[namespace][vector_id] = metadata + + def query(self, vector, top_k=5, namespace=None, include_metadata=True): + """Mock query method.""" + from collections import namedtuple + + namespace = namespace or "default" + if namespace not in self.vectors: + return [] + + # Just return some mock results + Match = namedtuple('Match', ['id', 'score', 'metadata']) + Results = namedtuple('Results', ['matches']) + + matches = [ + Match(id=vector_id, score=0.8, metadata=metadata) + for vector_id, metadata in list(self.vectors[namespace].items())[:top_k] + ] + + return Results(matches=matches) \ No newline at end of file diff --git a/app/services/ranking.py b/app/services/ranking.py new file mode 100644 index 0000000..a50fb8d --- /dev/null +++ b/app/services/ranking.py @@ -0,0 +1,136 @@ +# Reranking services +import cohere +from typing import List, Dict, Any +from loguru import logger +from tenacity import retry, stop_after_attempt, wait_exponential + +from app.core.config import settings +from app.core.models import ComplianceIssue, ComplianceReport, ComplianceLevel + +class RankingService: + """Service for ranking and prioritizing compliance issues using Cohere Reranker.""" + + def __init__(self): + """Initialize the ranking service with the Cohere client.""" + self.cohere_client = cohere.Client(settings.COHERE_API_KEY) + self.reranker_model = settings.RERANKER_MODEL + + async def prioritize_issues(self, report: ComplianceReport, max_issues: int = 10) -> ComplianceReport: + """ + Prioritize and rank compliance issues in a report. + + Args: + report: The compliance report with issues to prioritize + max_issues: Maximum number of issues to include in the final report + + Returns: + Updated compliance report with prioritized issues + """ + if not report.issues or len(report.issues) <= 1: + # No need to rank if there's only 0 or 1 issues + return report + + try: + # Prepare issues for ranking + issue_texts = [ + f"Section: {issue.section}. " + f"Level: {issue.level.value}. " + f"Description: {issue.description}. " + f"Recommendation: {issue.recommendation}" + for issue in report.issues + ] + + # Query object representing what we're looking for + query = "critical compliance issues that require immediate attention" + + # Rerank issues based on relevance to the query + reranked_issues = await self._rerank_issues(query, issue_texts) + + # Sort issues based on: + # 1. Compliance level (critical > major > minor > info) + # 2. Reranker relevance score + sorted_issues = [] + level_scores = { + ComplianceLevel.CRITICAL: 4, + ComplianceLevel.MAJOR: 3, + ComplianceLevel.MINOR: 2, + ComplianceLevel.INFO: 1 + } + + # Combine original issues with reranked scores + combined_issues = [] + for i, issue in enumerate(report.issues): + rerank_score = next((item["relevance_score"] for item in reranked_issues + if item["index"] == i), 0.0) + + # Calculate combined score (level_score * 100 + rerank_score) + # This ensures level is always the primary sorting factor + level_score = level_scores.get(issue.level, 0) + combined_score = (level_score * 100) + rerank_score + + combined_issues.append({ + "issue": issue, + "combined_score": combined_score, + "rerank_score": rerank_score + }) + + # Sort by combined score (descending) + combined_issues.sort(key=lambda x: x["combined_score"], reverse=True) + + # Take top issues based on max_issues limit + sorted_issues = [item["issue"] for item in combined_issues[:max_issues]] + + # Create updated report + prioritized_report = ComplianceReport( + report_id=report.report_id, + document_id=report.document_id, + timestamp=report.timestamp, + compliance_score=report.compliance_score, + summary=report.summary, + issues=sorted_issues + ) + + return prioritized_report + + except Exception as e: + logger.error(f"Error prioritizing issues: {str(e)}") + # If ranking fails, return the original report + return report + + @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) + async def _rerank_issues(self, query: str, issue_texts: List[str]) -> List[Dict[str, Any]]: + """ + Rerank issues using Cohere Reranker. + + Args: + query: The search query to compare issues against + issue_texts: List of issue descriptions to rank + + Returns: + List of dictionaries with reranked issues and scores + """ + try: + # Call Cohere Rerank endpoint + response = self.cohere_client.rerank( + model=self.reranker_model, + query=query, + documents=issue_texts, + top_n=len(issue_texts) + ) + + # Format results + reranked_issues = [] + for result in response.results: + reranked_issues.append({ + "index": result.index, # Original index in the issues list + "relevance_score": result.relevance_score + }) + + return reranked_issues + + except Exception as e: + logger.error(f"Error calling Cohere Reranker: {str(e)}") + + # Return basic ranking if reranking fails + return [{"index": i, "relevance_score": 1.0 - (i * 0.1)} + for i in range(len(issue_texts))] \ No newline at end of file diff --git a/app/services/reasoning.py b/app/services/reasoning.py new file mode 100644 index 0000000..cb7f0a6 --- /dev/null +++ b/app/services/reasoning.py @@ -0,0 +1,168 @@ +# Reasoning with LLMs +# Reasoning with LLMs using GROQ +import json +from typing import Dict, List +from loguru import logger +from tenacity import retry, stop_after_attempt, wait_exponential + +from app.core.config import settings +from app.core.models import ComplianceIssue, ComplianceLevel, ComplianceReport +from app.utils.token_counter import count_tokens, truncate_by_tokens +from groq import Groq # Assuming groq Python SDK is installed + +class ReasoningService: + """Service for performing deep reasoning on documents using Groq.""" + + def __init__(self): + """Initialize the reasoning service with the Groq client.""" + self.client = Groq(api_key=settings.GROQ_API_KEY) + self.model = settings.REASONING_MODEL # e.g., "mixtral-8x7b-32768" + + @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) + async def analyze_document(self, document_id: str, sections: Dict[str, str], standards: List[str]) -> ComplianceReport: + document_content = "\n\n".join([f"# {name}\n{content}" for name, content in sections.items()]) + + # Use token-based truncation instead of character-based + max_tokens = 30000 # Adjust based on model context window + token_count = count_tokens(document_content) + + logger.info(f"Document {document_id} has {token_count} tokens before truncation") + + if token_count > max_tokens: + document_content = truncate_by_tokens(document_content, max_tokens) + logger.info(f"Document {document_id} truncated to {max_tokens} tokens") + + prompt = self._create_analysis_prompt(document_content, standards) + + try: + response = await self._query_groq(prompt) + compliance_report = self._parse_compliance_response(document_id, response, standards) + return compliance_report + except Exception as e: + logger.error(f"Error analyzing document with Groq: {str(e)}") + raise + + def _create_analysis_prompt(self, document_content: str, standards: List[str]) -> str: + standards_text = "\n".join([f"- {standard}" for standard in standards]) + return f""" +{document_content} + + + +{standards_text} + + +You are an expert in document compliance and technical specifications. Please analyze the document above against the listed standards. + +Your job is to identify compliance issues and provide detailed reasoning and recommendations. Focus on: +1. Technical accuracy and completeness +2. Compliance with the specified standards +3. Document structure and organization +4. Clarity and specificity of language +5. Consistency and coherence + +For each compliance issue you find, please provide: +- The section where the issue appears +- A detailed description of the issue +- The severity level (critical, major, minor, or info) +- A thorough explanation of why this is an issue and how it impacts compliance +- Specific, actionable recommendations to fix the issue +- References to specific standards or best practices that apply + +Respond in the following JSON format: +{{ + "summary": "Comprehensive overall assessment of the document", + "compliance_score": 0.0 to 1.0, + "issues": [ + {{ + "section": "Section name", + "description": "Detailed issue description", + "level": "critical/major/minor/info", + "reasoning": "Thorough explanation of why this is an issue", + "standard_references": ["Specific standards or requirements that are violated"], + "recommendation": "Detailed, actionable recommendation to fix the issue" + }} + ] +}}""" + + @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10)) + async def _query_groq(self, prompt: str) -> str: + try: + response = self.client.chat.completions.create( + model=self.model, + messages=[ + {"role": "system", "content": "You are an AI assistant specialized in document compliance analysis."}, + {"role": "user", "content": prompt} + ], + max_tokens=4000, + temperature=0.2, + top_p=1.0 + ) + return response.choices[0].message.content + except Exception as e: + logger.error(f"Error querying Groq: {str(e)}") + raise + + def _parse_compliance_response(self, document_id: str, response: str, standards: List[str]) -> ComplianceReport: + try: + json_start = response.find('{') + json_end = response.rfind('}') + 1 + + if json_start == -1 or json_end == 0: + raise ValueError("Could not find JSON in response") + + json_response = response[json_start:json_end] + data = json.loads(json_response) + + summary = data.get("summary", "No summary provided") + compliance_score = float(data.get("compliance_score", 0.5)) + issues = [] + + for issue_data in data.get("issues", []): + level_str = issue_data.get("level", "minor").lower() + if level_str == "critical": + level = ComplianceLevel.CRITICAL + elif level_str == "major": + level = ComplianceLevel.MAJOR + elif level_str == "info": + level = ComplianceLevel.INFO + else: + level = ComplianceLevel.MINOR + + issues.append(ComplianceIssue( + section=issue_data.get("section", "Unknown"), + description=issue_data.get("description", "No description provided"), + level=level, + reasoning=issue_data.get("reasoning", "No detailed reasoning provided"), + standard_references=issue_data.get("standard_references", []), + recommendation=issue_data.get("recommendation", "No recommendation provided") + )) + + return ComplianceReport( + document_id=document_id, + compliance_score=compliance_score, + summary=summary, + issues=issues, + applied_standards=standards + ) + except json.JSONDecodeError: + logger.error("Failed to parse JSON from response") + return ComplianceReport( + document_id=document_id, + compliance_score=0.0, + summary="Failed to analyze document due to parsing error.", + issues=[ + ComplianceIssue( + section="System", + description="Failed to parse compliance analysis results.", + level=ComplianceLevel.CRITICAL, + reasoning="The system encountered an error while parsing the compliance analysis results.", + standard_references=[], + recommendation="Please try resubmitting the document or contact support." + ) + ], + applied_standards=[] + ) + except Exception as e: + logger.error(f"Error parsing compliance response: {str(e)}") + raise \ No newline at end of file diff --git a/app/services/standards.py b/app/services/standards.py new file mode 100644 index 0000000..c194e66 --- /dev/null +++ b/app/services/standards.py @@ -0,0 +1,250 @@ +# Standards management +import json +import os +from typing import Dict, List, Optional, BinaryIO, Tuple +import uuid +from loguru import logger + +from app.core.models import Standard, Requirement, RequirementSeverity +from app.utils.helpers import load_standards_from_file +from app.services.standards_matcher import StandardsMatcher + +# Singleton instance to ensure all parts of the application use the same standards +_standards_service_instance = None + +class StandardsService: + """Service for managing compliance standards.""" + + def __new__(cls): + """Implement singleton pattern to ensure all parts of the app use the same standards.""" + global _standards_service_instance + if _standards_service_instance is None: + _standards_service_instance = super(StandardsService, cls).__new__(cls) + _standards_service_instance.standards = {} # In-memory storage for standards + _standards_service_instance.matcher = StandardsMatcher() # Advanced standards matching logic + _standards_service_instance._load_default_standards() + return _standards_service_instance + + def __init__(self): + """Initialize the standards service.""" + # Initialization is done in __new__ for the singleton pattern + + def _load_default_standards(self): + """Load default standards from the standards directory.""" + standards_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "standard") + + if not os.path.exists(standards_dir): + logger.warning(f"Standards directory not found: {standards_dir}") + return + + for filename in os.listdir(standards_dir): + if filename.endswith(".json"): + try: + file_path = os.path.join(standards_dir, filename) + standards_data = load_standards_from_file(file_path) + + if "standards" in standards_data: + for std_data in standards_data["standards"]: + standard = self._create_standard_from_data(std_data) + self.standards[standard.id] = standard + logger.info(f"Loaded standard: {standard.name} ({standard.id})") + except Exception as e: + logger.error(f"Error loading standard from {filename}: {str(e)}") + + def _create_standard_from_data(self, data: Dict) -> Standard: + """ + Create a Standard object from dictionary data. + + Args: + data: Dictionary containing standard data + + Returns: + Standard object + """ + requirements = [] + + if "requirements" in data: + for req_data in data["requirements"]: + # Map severity string to RequirementSeverity enum + severity_str = req_data.get("severity", "minor").lower() + if severity_str == "critical": + severity = RequirementSeverity.CRITICAL + elif severity_str == "major": + severity = RequirementSeverity.MAJOR + elif severity_str == "info": + severity = RequirementSeverity.INFO + else: + severity = RequirementSeverity.MINOR + + requirement = Requirement( + id=req_data.get("id", str(uuid.uuid4())), + description=req_data.get("description", ""), + severity=severity, + details=req_data.get("details", None) + ) + requirements.append(requirement) + + return Standard( + id=data.get("id", str(uuid.uuid4())), + name=data.get("name", "Unnamed Standard"), + description=data.get("description", ""), + requirements=requirements + ) + + async def get_all_standards(self) -> List[Standard]: + """ + Get all available standards. + + Returns: + List of Standard objects + """ + return list(self.standards.values()) + + async def get_standard(self, standard_id: str) -> Optional[Standard]: + """ + Get a standard by ID. + + Args: + standard_id: ID of the standard to retrieve + + Returns: + Standard object if found, None otherwise + """ + return self.standards.get(standard_id) + + async def get_standard_by_name(self, name: str) -> Optional[Standard]: + """ + Get a standard by name (case-insensitive). + + Args: + name: Name of the standard to retrieve + + Returns: + Standard object if found, None otherwise + """ + name_lower = name.lower() + for standard in self.standards.values(): + if standard.name.lower() == name_lower: + return standard + return None + + async def upload_standard(self, file: BinaryIO, filename: str) -> Standard: + """ + Upload and process a standard definition file. + + Args: + file: The standard definition file (JSON) + filename: Name of the uploaded file + + Returns: + Standard object + """ + try: + # Read file content + content = await self._read_file_content(file) + + # Parse JSON + data = json.loads(content) + + if "standards" in data and isinstance(data["standards"], list): + # Multiple standards in file + standards = [] + for std_data in data["standards"]: + standard = self._create_standard_from_data(std_data) + self.standards[standard.id] = standard + standards.append(standard) + logger.info(f"Uploaded standard: {standard.name} (ID: {standard.id}) with {len(standard.requirements)} requirements") + + # Log the current standards count after upload + logger.info(f"Total standards in system after upload: {len(self.standards)}") + + # Return the first standard for simplicity + return standards[0] if standards else None + else: + # Single standard in file + standard = self._create_standard_from_data(data) + self.standards[standard.id] = standard + logger.info(f"Uploaded standard: {standard.name} (ID: {standard.id}) with {len(standard.requirements)} requirements") + + # Log the current standards count after upload + logger.info(f"Total standards in system after upload: {len(self.standards)}") + + return standard + + except json.JSONDecodeError: + raise ValueError("Invalid JSON format in standard definition file") + except Exception as e: + logger.error(f"Error processing standard file: {str(e)}") + raise + + async def _read_file_content(self, file: BinaryIO) -> str: + """ + Read and decode file content. + + Args: + file: The file to read + + Returns: + File content as string + """ + file_content = file.read() + + # Try to decode as UTF-8 + try: + return file_content.decode('utf-8') + except UnicodeDecodeError: + # Try other encodings if UTF-8 fails + try: + return file_content.decode('latin-1') + except: + raise ValueError("Unable to decode file content. Please ensure file is text-based.") + + async def get_standard_names_for_document(self, document_content: str) -> List[str]: + """ + Identify which standards might be relevant for a document based on content. + Uses advanced matching logic to find the most relevant standards. + + Args: + document_content: The document content + + Returns: + List of standard names that might be relevant + """ + # Default standards to use if no matches are found + DEFAULT_STANDARDS = ["ISO-9001", "IEEE-829", "RFC-2119"] + + # Log available standards for debugging + logger.info(f"Available standards in the system: {len(self.standards)}") + for std_id, std in self.standards.items(): + logger.info(f" - {std.name} (ID: {std_id})") + + # If no standards are available, return defaults + if not self.standards: + logger.warning("No standards available in the system. Using default standards.") + return DEFAULT_STANDARDS + + # Use the standards matcher to find relevant standards + standard_scores = self.matcher.find_relevant_standards( + document_content=document_content, + standards=list(self.standards.values()), + threshold=0.1, # Minimum relevance threshold + max_standards=5 # Maximum number of standards to return + ) + + # Log the matching results + if standard_scores: + logger.info(f"Found {len(standard_scores)} relevant standards:") + for name, score in standard_scores: + logger.info(f" - {name}: relevance score {score:.2f}") + else: + logger.info("No relevant standards found based on document content.") + + # Extract standard names from the results + relevant_standards = [std[0] for std in standard_scores] + + # If no relevant standards found, use defaults + if not relevant_standards: + logger.info(f"Using default standards: {DEFAULT_STANDARDS}") + return DEFAULT_STANDARDS + + return relevant_standards diff --git a/app/services/standards_matcher.py b/app/services/standards_matcher.py new file mode 100644 index 0000000..f381484 --- /dev/null +++ b/app/services/standards_matcher.py @@ -0,0 +1,304 @@ +# Standards matching logic +import re +from typing import Dict, List, Set, Tuple, Optional +from loguru import logger + +from app.core.models import Standard, Requirement + + +class StandardsMatcher: + """ + Advanced matching logic to identify relevant standards for documents. + This class implements sophisticated matching algorithms beyond simple text matching. + """ + + def __init__(self): + """Initialize the standards matcher.""" + # Common stopwords to filter out when extracting keywords + self.stopwords = { + "the", "a", "an", "and", "or", "in", "on", "at", "to", "for", "with", + "by", "of", "is", "are", "was", "were", "be", "been", "being", "have", + "has", "had", "do", "does", "did", "but", "if", "then", "else", "when", + "where", "why", "how", "all", "any", "both", "each", "few", "more", + "most", "other", "some", "such", "no", "nor", "not", "only", "own", + "same", "so", "than", "too", "very", "can", "will", "just", "should", + "now", "this", "that", "these", "those" + } + + # Technical terms that indicate compliance requirements + self.technical_indicators = [ + "shall", "must", "required", "should", "recommended", "may", "optional", + "compliant", "compliance", "conform", "standard", "specification", "requirement", + "procedure", "process", "method", "test", "verify", "validate", "certification", + "certified", "approved", "regulation", "regulatory", "guideline", "protocol" + ] + + # Common standard prefixes and abbreviations + self.standard_prefixes = [ + "iso", "ieee", "astm", "ansi", "iec", "din", "bs", "en", "jis", + "gb", "api", "asme", "nfpa", "ul", "mil", "std", "rfc", "itu" + ] + + def extract_document_sections(self, document_content: str) -> Dict[str, str]: + """ + Extract sections from a document to improve matching. + + Args: + document_content: The document content + + Returns: + Dictionary of section name to section content + """ + sections = {} + sections["full_document"] = document_content + + # Try to identify document sections using markdown headings + heading_pattern = re.compile(r'^(#{1,3})\s+(.+)$', re.MULTILINE) + matches = list(heading_pattern.finditer(document_content)) + + if matches: + for i, match in enumerate(matches): + section_name = match.group(2).strip() + + # Get section content (from this heading to the next, or to the end) + start_pos = match.end() + end_pos = matches[i+1].start() if i < len(matches) - 1 else len(document_content) + + section_content = document_content[start_pos:end_pos].strip() + sections[section_name] = section_content + + # Look for common document sections by name + common_sections = [ + "introduction", "scope", "purpose", "references", "definitions", + "requirements", "compliance", "standards", "conclusion", "summary", + "appendix", "annex" + ] + + for section in common_sections: + pattern = re.compile(rf'(?i)(?:^|\n)(?:{section}|{section.capitalize()})(?:[\s:]+)(.*?)(?=\n\s*\n|\n\s*[A-Z]|\Z)', re.DOTALL) + match = pattern.search(document_content) + if match: + sections[section] = match.group(1).strip() + + return sections + + def extract_key_terms(self, document_content: str) -> List[str]: + """ + Extract key technical terms from document content. + + Args: + document_content: The document content + + Returns: + List of key terms + """ + key_terms = [] + + # Split into sentences + sentences = re.split(r'[.!?]\s+', document_content) + + for sentence in sentences: + words = sentence.split() + + # Check if sentence contains technical indicators + if any(indicator in sentence.lower() for indicator in self.technical_indicators): + # Extract noun phrases (simplified approach) + for i in range(len(words) - 1): + if words[i].lower() not in self.stopwords and words[i+1].lower() not in self.stopwords: + key_terms.append(f"{words[i]} {words[i+1]}".lower()) + + # Look for capitalized terms (often defined terms) + cap_pattern = re.compile(r'\b[A-Z][A-Z0-9]+\b') + cap_terms = cap_pattern.findall(document_content) + key_terms.extend([term.lower() for term in cap_terms]) + + # Look for standard references (e.g., ISO-9001, IEEE 829) + for prefix in self.standard_prefixes: + pattern = re.compile(rf'\b{prefix}[-\s]?\d+\b', re.IGNORECASE) + matches = pattern.findall(document_content) + key_terms.extend([match.lower() for match in matches]) + + # Remove duplicates + return list(set(key_terms)) + + def extract_standard_keywords(self, standard: Standard) -> List[str]: + """ + Extract keywords from a standard that can be used for matching. + + Args: + standard: The standard to extract keywords from + + Returns: + List of keywords associated with the standard + """ + keywords = [] + + # Add standard name and variations + keywords.append(standard.name.lower()) + keywords.append(standard.name.replace("-", "").lower()) + keywords.append(standard.name.replace("-", " ").lower()) + + # Add standard description words (excluding common words) + if standard.description: + description_words = [word.lower() for word in standard.description.split() + if word.lower() not in self.stopwords] + keywords.extend(description_words) + + # Add requirement keywords + for req in standard.requirements: + # Add requirement ID + keywords.append(req.id.lower()) + + # Add key phrases from requirement description + if req.description: + # Extract noun phrases and technical terms (simplified approach) + phrases = [] + words = req.description.split() + for i in range(len(words) - 1): + if words[i].lower() not in self.stopwords and words[i+1].lower() not in self.stopwords: + phrases.append(f"{words[i]} {words[i+1]}".lower()) + keywords.extend(phrases) + + # Add individual technical terms + for word in words: + if word.lower() in self.technical_indicators: + keywords.append(word.lower()) + + # Remove duplicates and return + return list(set(keywords)) + + def calculate_standard_relevance(self, standard: Standard, document_content: str, + sections: Dict[str, str], key_terms: List[str]) -> float: + """ + Calculate a relevance score for a standard based on multiple factors. + + Args: + standard: The standard to evaluate + document_content: The document content + sections: Document sections + key_terms: Key terms extracted from the document + + Returns: + Relevance score (0.0 to 1.0) + """ + document_content_lower = document_content.lower() + + # Extract keywords for this standard + standard_keywords = self.extract_standard_keywords(standard) + + # Initialize scores for different matching components + name_match_score = 0.0 + keyword_match_score = 0.0 + section_match_score = 0.0 + term_match_score = 0.0 + requirement_match_score = 0.0 + + # 1. Check for standard name matches (highest weight) + if standard.name.lower() in document_content_lower: + name_match_score = 0.5 + elif standard.name.replace("-", "").lower() in document_content_lower: + name_match_score = 0.4 + elif standard.name.replace("-", " ").lower() in document_content_lower: + name_match_score = 0.4 + + # 2. Check for keyword matches + matched_keywords = 0 + total_keywords = len(standard_keywords) + + if total_keywords > 0: + for keyword in standard_keywords: + if keyword in document_content_lower: + matched_keywords += 1 + + keyword_match_score = matched_keywords / total_keywords * 0.3 + + # 3. Check for section-specific matches + important_sections = ["introduction", "scope", "purpose", "references", + "standards", "compliance", "requirements"] + + for section_name in important_sections: + if section_name in sections: + section_content = sections[section_name].lower() + + # Check for standard name in important sections + if standard.name.lower() in section_content: + section_match_score += 0.1 + break + + # Check for standard name in section titles + for section_name in sections.keys(): + if standard.name.lower() in section_name.lower(): + section_match_score += 0.2 + break + + # 4. Check for key term matches + matching_terms = 0 + for term in key_terms: + if any(kw in term or term in kw for kw in standard_keywords): + matching_terms += 1 + + if len(key_terms) > 0: + term_match_score = min(0.2, 0.01 * matching_terms) + + # 5. Check for requirement-specific matches + for req in standard.requirements: + req_desc_lower = req.description.lower() + req_keywords = [word for word in req_desc_lower.split() + if word not in self.stopwords and len(word) > 3] + + for keyword in req_keywords: + if keyword in document_content_lower: + requirement_match_score += 0.01 + + requirement_match_score = min(0.2, requirement_match_score) + + # Calculate final score (weighted sum of all components) + final_score = ( + name_match_score + + keyword_match_score + + section_match_score + + term_match_score + + requirement_match_score + ) + + # Cap at 1.0 + return min(final_score, 1.0) + + def find_relevant_standards(self, document_content: str, standards: List[Standard], + threshold: float = 0.1, max_standards: int = 5) -> List[Tuple[str, float]]: + """ + Find standards relevant to a document with relevance scores. + + Args: + document_content: The document content + standards: List of available standards + threshold: Minimum relevance score threshold + max_standards: Maximum number of standards to return + + Returns: + List of tuples (standard_name, relevance_score) sorted by relevance + """ + if not standards: + return [] + + # Extract document sections and key terms + sections = self.extract_document_sections(document_content) + key_terms = self.extract_key_terms(document_content) + + # Calculate relevance scores for each standard + standard_scores = [] + + for standard in standards: + score = self.calculate_standard_relevance( + standard, document_content, sections, key_terms + ) + + if score >= threshold: + standard_scores.append((standard.name, score)) + logger.debug(f"Standard {standard.name} relevance score: {score:.2f}") + + # Sort by relevance score (highest first) + standard_scores.sort(key=lambda x: x[1], reverse=True) + + # Limit to max_standards + return standard_scores[:max_standards] diff --git a/app/static/css/styles.css b/app/static/css/styles.css new file mode 100644 index 0000000..4c4adb0 --- /dev/null +++ b/app/static/css/styles.css @@ -0,0 +1,713 @@ +/* Base styles */ +:root { + --primary-color: #3498db; + --secondary-color: #2980b9; + --accent-color: #f39c12; + --success-color: #2ecc71; + --warning-color: #f1c40f; + --danger-color: #e74c3c; + --info-color: #3498db; + --light-color: #f8f9fa; + --dark-color: #343a40; + --gray-color: #6c757d; + --border-color: #dee2e6; + --font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--font-family); + line-height: 1.6; + color: var(--dark-color); + background-color: #f4f7fa; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +h1, h2, h3 { + margin-bottom: 1rem; + color: var(--dark-color); +} + +/* Header styles */ +header { + text-align: center; + margin-bottom: 2rem; + padding: 1.5rem; + background-color: white; + border-radius: 8px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); +} + +header h1 { + color: var(--primary-color); + margin-bottom: 0.5rem; +} + +header p { + color: var(--gray-color); +} + +/* Main content sections */ +main { + display: block; + width: 100%; +} + +.main-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; +} + +.documents-section { + grid-column: 1 / -1; /* Span all columns */ +} + +section { + background-color: white; + border-radius: 8px; + padding: 1.5rem; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); +} + +section h2 { + margin-bottom: 1.5rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border-color); +} + +/* Upload area styles */ +.upload-container { + margin-bottom: 1.5rem; +} + +.upload-area { + border: 2px dashed var(--primary-color); + border-radius: 8px; + padding: 3rem 1.5rem; + text-align: center; + transition: background-color 0.3s ease; +} + +.upload-area:hover { + background-color: rgba(52, 152, 219, 0.05); +} + +.upload-area i { + font-size: 3rem; + color: var(--primary-color); + margin-bottom: 1rem; +} + +.upload-area p { + margin-bottom: 0.5rem; + color: var(--gray-color); +} + +.file-info { + margin-top: 1.5rem; + padding: 1rem; + border: 1px solid var(--border-color); + border-radius: 8px; +} + +.file-details { + display: flex; + align-items: center; + margin-bottom: 1rem; +} + +.file-icon { + font-size: 2rem; + color: var(--primary-color); + margin-right: 1rem; +} + +.file-name { + font-weight: bold; + margin-bottom: 0.25rem; +} + +.file-size { + color: var(--gray-color); + font-size: 0.9rem; +} + +/* Buttons */ +.button { + display: inline-block; + padding: 0.5rem 1rem; + background-color: var(--primary-color); + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; + transition: background-color 0.3s ease; + text-decoration: none; +} + +.button:hover { + background-color: var(--secondary-color); +} + +.upload-button { + background-color: var(--success-color); +} + +.upload-button:hover { + background-color: #27ae60; +} + +.cancel-button { + background-color: var(--gray-color); + margin-left: 0.5rem; +} + +.cancel-button:hover { + background-color: #5a6268; +} + +/* Documents list */ +.documents-list { + list-style: none; +} + +.document-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border: 1px solid var(--border-color); + border-radius: 4px; + margin-bottom: 0.5rem; + transition: background-color 0.3s ease; +} + +.document-item:hover { + background-color: #f8f9fa; +} + +.document-info { + display: flex; + align-items: center; +} + +.document-icon { + font-size: 1.5rem; + color: var(--primary-color); + margin-right: 1rem; +} + +.document-name { + font-weight: bold; + margin-bottom: 0.25rem; +} + +.document-date { + color: var(--gray-color); + font-size: 0.9rem; +} + +.document-actions { + display: flex; + gap: 0.5rem; +} + +.action-button { + font-size: 1rem; + padding: 0.25rem 0.5rem; +} + +.view-button { + background-color: var(--info-color); +} + +.view-button:hover { + background-color: #2980b9; +} + +.resubmit-button { + background-color: var(--warning-color); + color: var(--dark-color); +} + +.resubmit-button:hover { + background-color: #f39c12; +} + +/* Report section */ +.report-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; +} + +.report-container { + border: 1px solid var(--border-color); + border-radius: 8px; + padding: 1.5rem; +} + +/* Summary section in report */ +.summary { + background-color: #e9f7ef; + padding: 1.5rem; + border-radius: 8px; + margin-bottom: 1.5rem; + border-left: 4px solid var(--success-color); +} + +.applied-standards { + margin-top: 1rem; + padding: 0.75rem 1rem; + background-color: #f8f9fa; + border-radius: 5px; + border-left: 3px solid var(--info-color); +} + +.applied-standards h4 { + margin-top: 0; + margin-bottom: 0.5rem; + font-size: 1rem; + color: #495057; +} + +.standards-list { + margin: 0 0 0 1.5rem; + padding: 0; +} + +.standards-list li { + margin-bottom: 0.25rem; +} + +/* Issues section in report */ +.issues-container { + margin-top: 1.5rem; +} + +.issue { + margin-bottom: 1rem; + padding: 1rem; + border-radius: 8px; + background-color: #f8f9fa; + border-left: 4px solid var(--gray-color); +} + +.issue.critical { + background-color: #fdedec; + border-left-color: var(--danger-color); +} + +.issue.major { + background-color: #fef9e7; + border-left-color: var(--warning-color); +} + +.issue.minor { + background-color: #eafaf1; + border-left-color: var(--success-color); +} + +.issue.info { + background-color: #ebf5fb; + border-left-color: var(--info-color); +} + +.issue-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.issue-section { + font-weight: bold; +} + +.badge { + display: inline-block; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.8rem; + font-weight: bold; + text-transform: uppercase; + color: white; +} + +.badge.critical { + background-color: var(--danger-color); +} + +.badge.major { + background-color: var(--warning-color); + color: var(--dark-color); +} + +.badge.minor { + background-color: var(--success-color); +} + +.badge.info { + background-color: var(--info-color); +} + +.issue-description { + margin-bottom: 0.5rem; +} + +.issue-recommendation { + background-color: #f8f9fa; + padding: 0.75rem; + border-radius: 4px; + font-style: italic; +} + +/* Loading overlay */ +.loading-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.loading-spinner { + width: 50px; + height: 50px; + border: 5px solid #f3f3f3; + border-top: 5px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; +} + +.loading-overlay p { + color: white; + font-size: 1.2rem; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Stats display */ +.stats-container { + display: flex; + justify-content: space-between; + margin-bottom: 1.5rem; + flex-wrap: wrap; + gap: 1rem; +} + +.stat-box { + flex: 1; + min-width: 100px; + background-color: #f8f9fa; + padding: 1rem; + border-radius: 8px; + text-align: center; +} + +.stat-value { + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 0.25rem; +} + +.stat-label { + color: var(--gray-color); + font-size: 0.9rem; +} + +/* Score display */ +.score-container { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 1.5rem; +} + +.score-circle { + width: 100px; + height: 100px; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + font-size: 2rem; + font-weight: bold; + color: white; + background-color: var(--success-color); + margin-right: 1rem; +} + +.score-label { + font-size: 1.2rem; + font-weight: bold; +} + +/* Footer */ +footer { + text-align: center; + margin-top: 2rem; + padding: 1rem; + color: var(--gray-color); + font-size: 0.9rem; +} + +/* Standards section */ +.standards-section { + background-color: white; + border-radius: 8px; + padding: 1.5rem; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); +} + +.standards-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; +} + +.standards-header p { + color: var(--gray-color); + margin: 0; +} + +.standards-upload { + margin-bottom: 1.5rem; + border: 1px dashed var(--border-color); + border-radius: 8px; + padding: 1rem; +} + +.standards-list-container { + max-height: 300px; + overflow-y: auto; +} + +.standards-list { + list-style: none; +} + +.standard-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border-bottom: 1px solid var(--border-color); +} + +.standard-info { + display: flex; + align-items: center; +} + +.standard-icon { + font-size: 1.5rem; + color: var(--primary-color); + margin-right: 1rem; +} + +.standard-name { + font-weight: bold; + margin-bottom: 0.25rem; +} + +.standard-description { + color: var(--gray-color); + font-size: 0.9rem; +} + +.standard-requirements { + color: var(--gray-color); + font-size: 0.9rem; + margin-top: 0.25rem; +} + +.standard-actions { + display: flex; + gap: 0.5rem; +} + +.no-standards { + color: var(--gray-color); + text-align: center; + padding: 2rem 0; +} + +/* Modal styles */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1001; +} + +.modal-content { + background-color: white; + border-radius: 8px; + width: 80%; + max-width: 800px; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1.5rem; + border-bottom: 1px solid var(--border-color); +} + +.modal-header h2 { + margin: 0; +} + +.close-button { + background: none; + border: none; + font-size: 1.5rem; + cursor: pointer; + color: var(--gray-color); +} + +.modal-body { + padding: 1.5rem; +} + +.standard-detail-header { + margin-bottom: 1.5rem; +} + +.standard-detail-header h3 { + margin-bottom: 0.5rem; +} + +.standard-requirements-list { + margin-top: 1.5rem; +} + +.standard-requirements-list h4 { + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border-color); +} + +.requirement-item { + margin-bottom: 1rem; + padding: 1rem; + border-radius: 4px; + background-color: #f8f9fa; +} + +.requirement-item.critical { + border-left: 4px solid var(--danger-color); +} + +.requirement-item.major { + border-left: 4px solid var(--warning-color); +} + +.requirement-item.minor { + border-left: 4px solid var(--success-color); +} + +.requirement-item.info { + border-left: 4px solid var(--info-color); +} + +.requirement-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.5rem; +} + +.requirement-id { + font-weight: bold; + color: var(--gray-color); +} + +.requirement-description { + margin-bottom: 0.5rem; +} + +.requirement-details { + font-size: 0.9rem; + color: var(--gray-color); + padding: 0.5rem; + background-color: #f1f1f1; + border-radius: 4px; +} + +/* Responsive design */ +@media (max-width: 992px) { + .main-grid { + grid-template-columns: 1fr; + } + + .modal-content { + width: 95%; + max-height: 90vh; + } +} + +@media (max-width: 768px) { + .document-item { + flex-direction: column; + align-items: flex-start; + } + + .document-actions { + margin-top: 1rem; + align-self: flex-end; + } + + .stats-container { + flex-direction: column; + } + + .stat-box { + margin-bottom: 0.5rem; + } + + .standard-item { + flex-direction: column; + align-items: flex-start; + } + + .standard-actions { + margin-top: 1rem; + align-self: flex-end; + } +} \ No newline at end of file diff --git a/app/static/index.html b/app/static/index.html new file mode 100644 index 0000000..0dac35d --- /dev/null +++ b/app/static/index.html @@ -0,0 +1,115 @@ + + + + + + Mini SpecsComply Pro + + + + +
+
+

Mini SpecsComply Pro

+

Document Compliance and Validation Tool

+
+ +
+
+
+

Upload Document

+
+
+ +

Drag and drop your document here

+

or

+ + +
+ +
+
+ +
+

Compliance Standards

+
+
+

Manage the compliance standards used for document analysis

+ +
+ +
+

No custom standards have been added yet.

+
    + +
+
+
+
+ +
+

Recent Documents

+
+

No documents have been analyzed yet.

+
    + +
+
+
+
+ + +
+ + + +
+

© 2025 Mini SpecsComply Pro

+
+
+ + + + \ No newline at end of file diff --git a/app/static/js/script.js b/app/static/js/script.js new file mode 100644 index 0000000..5552edb --- /dev/null +++ b/app/static/js/script.js @@ -0,0 +1,825 @@ +document.addEventListener('DOMContentLoaded', function() { + // Document Elements + const uploadArea = document.getElementById('upload-area'); + const fileInput = document.getElementById('file-input'); + const fileInfo = document.getElementById('file-info'); + const fileName = document.getElementById('file-name'); + const fileSize = document.getElementById('file-size'); + const uploadButton = document.getElementById('upload-button'); + const cancelButton = document.getElementById('cancel-button'); + const noDocuments = document.getElementById('no-documents'); + const documentsList = document.getElementById('documents-list'); + const reportSection = document.getElementById('report-section'); + const reportContainer = document.getElementById('report-container'); + const closeReportButton = document.getElementById('close-report-button'); + const loadingOverlay = document.getElementById('loading-overlay'); + + // Standards Elements + const uploadStandardButton = document.getElementById('upload-standard-button'); + const standardsUpload = document.getElementById('standards-upload'); + const standardUploadArea = document.getElementById('standard-upload-area'); + const standardFileInput = document.getElementById('standard-file-input'); + const standardFileInfo = document.getElementById('standard-file-info'); + const standardFileName = document.getElementById('standard-file-name'); + const standardFileSize = document.getElementById('standard-file-size'); + const standardUploadButton = document.getElementById('standard-upload-button'); + const standardCancelButton = document.getElementById('standard-cancel-button'); + const noStandards = document.getElementById('no-standards'); + const standardsList = document.getElementById('standards-list'); + + // API endpoint base URL + const API_BASE_URL = '/api'; + + // Local storage keys + const DOCUMENTS_STORAGE_KEY = 'specscomply_documents'; + const STANDARDS_STORAGE_KEY = 'specscomply_standards'; + + // Drag and drop functionality + uploadArea.addEventListener('dragover', function(e) { + e.preventDefault(); + uploadArea.classList.add('dragover'); + }); + + uploadArea.addEventListener('dragleave', function() { + uploadArea.classList.remove('dragover'); + }); + + uploadArea.addEventListener('drop', function(e) { + e.preventDefault(); + uploadArea.classList.remove('dragover'); + + if (e.dataTransfer.files.length) { + handleFileSelection(e.dataTransfer.files[0]); + } + }); + + // File input change + fileInput.addEventListener('change', function() { + if (fileInput.files.length) { + handleFileSelection(fileInput.files[0]); + } + }); + + // Upload button click + uploadButton.addEventListener('click', function() { + if (fileInput.files.length) { + uploadDocument(fileInput.files[0]); + } + }); + + // Cancel button click + cancelButton.addEventListener('click', function() { + resetFileInput(); + }); + + // Close report button click + closeReportButton.addEventListener('click', function() { + reportSection.style.display = 'none'; + }); + + // Load stored documents and standards on page load + loadDocuments(); + loadStandards(); + + // Standards upload button click + uploadStandardButton.addEventListener('click', function() { + standardsUpload.style.display = 'block'; + }); + + // Standard drag and drop functionality + standardUploadArea.addEventListener('dragover', function(e) { + e.preventDefault(); + standardUploadArea.classList.add('dragover'); + }); + + standardUploadArea.addEventListener('dragleave', function() { + standardUploadArea.classList.remove('dragover'); + }); + + standardUploadArea.addEventListener('drop', function(e) { + e.preventDefault(); + standardUploadArea.classList.remove('dragover'); + + if (e.dataTransfer.files.length) { + handleStandardFileSelection(e.dataTransfer.files[0]); + } + }); + + // Standard file input change + standardFileInput.addEventListener('change', function() { + if (standardFileInput.files.length) { + handleStandardFileSelection(standardFileInput.files[0]); + } + }); + + // Standard upload button click + standardUploadButton.addEventListener('click', function() { + if (standardFileInput.files.length) { + uploadStandard(standardFileInput.files[0]); + } + }); + + // Standard cancel button click + standardCancelButton.addEventListener('click', function() { + resetStandardFileInput(); + }); + + // Handle file selection + function handleFileSelection(file) { + // Update file info display + fileName.textContent = file.name; + fileSize.textContent = formatFileSize(file.size); + + // Show file info section + uploadArea.style.display = 'none'; + fileInfo.style.display = 'block'; + } + + // Reset file input + function resetFileInput() { + fileInput.value = ''; + uploadArea.style.display = 'block'; + fileInfo.style.display = 'none'; + } + + // Format file size + function formatFileSize(bytes) { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + } + + // Upload document + async function uploadDocument(file) { + try { + // Show loading overlay + loadingOverlay.style.display = 'flex'; + + const formData = new FormData(); + formData.append('file', file); + + const response = await fetch(`${API_BASE_URL}/documents/upload`, { + method: 'POST', + body: formData + }); + + if (!response.ok) { + throw new Error(`Error uploading document: ${response.statusText}`); + } + + const data = await response.json(); + console.log('Upload response:', data); + + // Store document in local storage + const document = { + id: data.document_id, + name: file.name, + status: data.status, + date: new Date().toISOString(), + size: file.size + }; + + saveDocument(document); + resetFileInput(); + loadDocuments(); // This should now work correctly + checkDocumentStatus(data.document_id); + + } catch (error) { + console.error('Error uploading document:', error); + alert('Failed to upload document. Please try again.'); + } finally { + loadingOverlay.style.display = 'none'; + } + } + + // Save document to local storage + function saveDocument(document) { + let documents = JSON.parse(localStorage.getItem(DOCUMENTS_STORAGE_KEY) || '[]'); + + // Check if document already exists + const existingIndex = documents.findIndex(doc => doc.id === document.id); + + if (existingIndex !== -1) { + // Update existing document + documents[existingIndex] = {...documents[existingIndex], ...document}; + } else { + // Add new document + documents.push(document); + } + + // Sort documents by date (newest first) + documents.sort((a, b) => new Date(b.date) - new Date(a.date)); + + // Keep only the 10 most recent documents + if (documents.length > 10) { + documents = documents.slice(0, 10); + } + + // Save to local storage + localStorage.setItem(DOCUMENTS_STORAGE_KEY, JSON.stringify(documents)); + } + + // Load documents from local storage + function loadDocuments() { + const documents = JSON.parse(localStorage.getItem(DOCUMENTS_STORAGE_KEY) || '[]'); + + // Clear documents list + documentsList.innerHTML = ''; + + if (documents.length === 0) { + noDocuments.style.display = 'block'; + return; + } + + noDocuments.style.display = 'none'; + + // Add documents to list + documents.forEach(doc => { // Changed parameter name to 'doc' + const li = createDocumentListItem(doc); + documentsList.appendChild(li); + }); + } + + // Create document list item + function createDocumentListItem(doc) { // Changed parameter name to 'doc' + try { + const li = window.document.createElement('li'); // Use window.document to be explicit + li.className = 'document-item'; + + let dateStr = 'Unknown date'; + try { + dateStr = new Date(doc.date).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); + } catch (e) { + console.warn('Error formatting date:', e); + } + + li.innerHTML = ` +
+ +
+

${doc.name || 'Unnamed document'}

+

${dateStr}

+
+
+
+ + +
+ `; + + // Add event listeners to buttons + const viewButton = li.querySelector('.view-button'); + const resubmitButton = li.querySelector('.resubmit-button'); + + viewButton.addEventListener('click', function() { + const documentId = this.getAttribute('data-id'); + viewDocumentReport(documentId); + }); + + resubmitButton.addEventListener('click', function() { + const documentId = this.getAttribute('data-id'); + resubmitDocument(documentId); + }); + + return li; + } catch (error) { + console.error('Error creating document list item:', error); + const li = window.document.createElement('li'); // Use window.document here too + li.className = 'document-item'; + li.textContent = 'Error displaying document'; + return li; + } + } + + // View document report + async function viewDocumentReport(documentId) { + try { + // Show loading overlay + loadingOverlay.style.display = 'flex'; + + // Fetch document analysis + const response = await fetch(`${API_BASE_URL}/documents/${documentId}/analysis`); + + if (!response.ok) { + throw new Error(`Error fetching document analysis: ${response.statusText}`); + } + + const data = await response.json(); + + // Check if analysis is complete + if (data.status === 'pending' || data.status === 'processing') { + alert('Document analysis is still in progress. Please try again later.'); + return; + } + + if (data.status === 'failed') { + alert('Document analysis failed. Please try resubmitting the document.'); + return; + } + + // If no report is available + if (!data.report) { + alert('No analysis report available for this document.'); + return; + } + + // Render report + renderReport(data.report); + + // Show report section + reportSection.style.display = 'block'; + + // Scroll to report section + reportSection.scrollIntoView({ behavior: 'smooth' }); + + } catch (error) { + console.error('Error viewing document report:', error); + alert('Failed to load document report. Please try again.'); + } finally { + // Hide loading overlay + loadingOverlay.style.display = 'none'; + } + } + + // Handle standard file selection + function handleStandardFileSelection(file) { + // Check if file is JSON + if (!file.name.toLowerCase().endsWith('.json')) { + alert('Please select a JSON file for standards'); + return; + } + + // Update file info display + standardFileName.textContent = file.name; + standardFileSize.textContent = formatFileSize(file.size); + + // Show file info section + standardUploadArea.style.display = 'none'; + standardFileInfo.style.display = 'block'; + } + + // Reset standard file input + function resetStandardFileInput() { + standardFileInput.value = ''; + standardUploadArea.style.display = 'block'; + standardFileInfo.style.display = 'none'; + standardsUpload.style.display = 'none'; + } + + // Upload standard + async function uploadStandard(file) { + try { + // Show loading overlay + loadingOverlay.style.display = 'flex'; + + const formData = new FormData(); + formData.append('file', file); + + const response = await fetch(`${API_BASE_URL}/standards/upload`, { + method: 'POST', + body: formData + }); + + if (!response.ok) { + throw new Error(`Error uploading standard: ${response.statusText}`); + } + + const data = await response.json(); + console.log('Standard upload response:', data); + + // Store standard in local storage + const standard = { + id: data.standard_id, + name: data.name, + requirement_count: data.requirement_count, + date: new Date().toISOString() + }; + + saveStandard(standard); + resetStandardFileInput(); + loadStandards(); + + alert(`Standard "${data.name}" uploaded successfully with ${data.requirement_count} requirements.`); + + } catch (error) { + console.error('Error uploading standard:', error); + alert('Failed to upload standard. Please try again.'); + } finally { + loadingOverlay.style.display = 'none'; + } + } + + // Save standard to local storage + function saveStandard(standard) { + let standards = JSON.parse(localStorage.getItem(STANDARDS_STORAGE_KEY) || '[]'); + + // Check if standard already exists + const existingIndex = standards.findIndex(std => std.id === standard.id); + + if (existingIndex !== -1) { + // Update existing standard + standards[existingIndex] = {...standards[existingIndex], ...standard}; + } else { + // Add new standard + standards.push(standard); + } + + // Sort standards by date (newest first) + standards.sort((a, b) => new Date(b.date) - new Date(a.date)); + + // Save to local storage + localStorage.setItem(STANDARDS_STORAGE_KEY, JSON.stringify(standards)); + } + + // Load standards from local storage + function loadStandards() { + const standards = JSON.parse(localStorage.getItem(STANDARDS_STORAGE_KEY) || '[]'); + + // Clear standards list + standardsList.innerHTML = ''; + + if (standards.length === 0) { + noStandards.style.display = 'block'; + return; + } + + noStandards.style.display = 'none'; + + // Add standards to list + standards.forEach(standard => { + const li = createStandardListItem(standard); + standardsList.appendChild(li); + }); + } + + // Create standard list item + function createStandardListItem(standard) { + const li = document.createElement('li'); + li.className = 'standard-item'; + + let dateStr = 'Unknown date'; + try { + dateStr = new Date(standard.date).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + } catch (e) { + console.warn('Error formatting date:', e); + } + + li.innerHTML = ` +
+ +
+

${standard.name || 'Unnamed standard'}

+

Added on ${dateStr}

+

${standard.requirement_count} requirements

+
+
+
+ +
+ `; + + // Add event listeners to buttons + const viewButton = li.querySelector('.view-standard-button'); + + viewButton.addEventListener('click', function() { + const standardId = this.getAttribute('data-id'); + viewStandard(standardId); + }); + + return li; + } + + // View standard details + async function viewStandard(standardId) { + try { + // Show loading overlay + loadingOverlay.style.display = 'flex'; + + // Fetch standard details + const response = await fetch(`${API_BASE_URL}/standards/${standardId}`); + + if (!response.ok) { + throw new Error(`Error fetching standard: ${response.statusText}`); + } + + const standard = await response.json(); + + // Create modal content + const modalContent = ` +
+

${standard.name}

+

${standard.description || 'No description available'}

+
+
+

Requirements (${standard.requirements.length})

+ ${standard.requirements.length === 0 ? '

No requirements defined

' : ''} + +
+ `; + + // Create modal + const modal = document.createElement('div'); + modal.className = 'modal'; + modal.innerHTML = ` + + `; + + // Add modal to body + document.body.appendChild(modal); + + // Add close button event listener + modal.querySelector('.close-button').addEventListener('click', function() { + document.body.removeChild(modal); + }); + + // Close modal when clicking outside + modal.addEventListener('click', function(e) { + if (e.target === modal) { + document.body.removeChild(modal); + } + }); + + } catch (error) { + console.error('Error viewing standard:', error); + alert('Failed to load standard details. Please try again.'); + } finally { + // Hide loading overlay + loadingOverlay.style.display = 'none'; + } + } + + // Render report + function renderReport(report) { + // Calculate issue counts + const criticalCount = report.issues.filter(issue => issue.level === 'critical').length; + const majorCount = report.issues.filter(issue => issue.level === 'major').length; + const minorCount = report.issues.filter(issue => issue.level === 'minor').length; + const infoCount = report.issues.filter(issue => issue.level === 'info').length; + + // Format score as percentage + const scorePercentage = (report.compliance_score * 100).toFixed(1); + + // Determine score color based on percentage + let scoreColor = '#2ecc71'; // Default green + if (scorePercentage < 50) { + scoreColor = '#e74c3c'; // Red for low score + } else if (scorePercentage < 80) { + scoreColor = '#f39c12'; // Orange for medium score + } + + // Create HTML + let html = ` +
+
+ ${scorePercentage}% +
+
Compliance Score
+
+ +
+
+
${criticalCount}
+
Critical Issues
+
+
+
${majorCount}
+
Major Issues
+
+
+
${minorCount}
+
Minor Issues
+
+
+
${infoCount}
+
Info Issues
+
+
+ +
+

Summary

+

${report.summary}

+ + ${report.applied_standards && report.applied_standards.length > 0 ? ` +
+

Applied Standards

+
    + ${report.applied_standards.map(std => `
  • ${std}
  • `).join('')} +
+
+ ` : ''} +
+ +
+

Compliance Issues

+ `; + + if (report.issues.length === 0) { + html += '

No compliance issues found. Great job!

'; + } else { + // Sort issues by level (critical first) + const sortedIssues = [...report.issues].sort((a, b) => { + const levelOrder = { 'critical': 0, 'major': 1, 'minor': 2, 'info': 3 }; + return levelOrder[a.level] - levelOrder[b.level]; + }); + + // Add issues to HTML + sortedIssues.forEach(issue => { + html += ` +
+
+
${issue.section}
+ ${issue.level} +
+
+ ${issue.description} +
+ ${issue.reasoning ? ` +
+ Reasoning: ${issue.reasoning} +
` : ''} + ${issue.standard_references && issue.standard_references.length > 0 ? ` +
+ Standard References: +
    + ${issue.standard_references.map(ref => `
  • ${ref}
  • `).join('')} +
+
` : ''} +
+ Recommendation: ${issue.recommendation} +
+
+ `; + }); + } + + html += '
'; + + // Set report HTML + reportContainer.innerHTML = html; + } + + // Check document status + async function checkDocumentStatus(documentId) { + try { + // Start with a short delay + let delay = 2000; + const maxAttempts = 10; + + for (let attempt = 0; attempt < maxAttempts; attempt++) { + // Wait for the delay + await new Promise(resolve => setTimeout(resolve, delay)); + + // Fetch document status + const response = await fetch(`${API_BASE_URL}/documents/${documentId}`); + + if (!response.ok) { + throw new Error(`Error checking document status: ${response.statusText}`); + } + + const data = await response.json(); + + // Update document in local storage + const documents = JSON.parse(localStorage.getItem(DOCUMENTS_STORAGE_KEY) || '[]'); + const documentIndex = documents.findIndex(doc => doc.id === documentId); + + if (documentIndex !== -1) { + documents[documentIndex].status = data.status; + localStorage.setItem(DOCUMENTS_STORAGE_KEY, JSON.stringify(documents)); + } + + // If processing is complete or failed, stop checking + if (data.status === 'completed' || data.status === 'failed') { + // If completed, show the report + if (data.status === 'completed' && data.reports && data.reports.length > 0) { + viewDocumentReport(documentId); + } + break; + } + + // Increase delay for next attempt (exponential backoff) + delay = Math.min(delay * 1.5, 10000); + } + + // Refresh document list + loadDocuments(); + + } catch (error) { + console.error('Error checking document status:', error); + } + } + + // Resubmit document + function resubmitDocument(documentId) { + // Trigger file input for resubmission + fileInput.setAttribute('data-resubmit-id', documentId); + fileInput.click(); + + // Listen for file selection (one-time event listener) + const handleResubmitFileSelection = async function() { + if (fileInput.files.length) { + const resubmitId = fileInput.getAttribute('data-resubmit-id'); + + if (resubmitId) { + // Handle resubmission + await handleDocumentResubmission(resubmitId, fileInput.files[0]); + + // Remove attribute and event listener + fileInput.removeAttribute('data-resubmit-id'); + fileInput.removeEventListener('change', handleResubmitFileSelection); + } + } + }; + + fileInput.addEventListener('change', handleResubmitFileSelection); + } + + // Handle document resubmission + async function handleDocumentResubmission(documentId, file) { + try { + // Show loading overlay + loadingOverlay.style.display = 'flex'; + + // Create form data + const formData = new FormData(); + formData.append('file', file); + + // Send request to API + const response = await fetch(`${API_BASE_URL}/documents/${documentId}/resubmit`, { + method: 'POST', + body: formData + }); + + if (!response.ok) { + throw new Error(`Error resubmitting document: ${response.statusText}`); + } + + const data = await response.json(); + + // Update document in local storage + const document = { + id: data.document_id, + name: file.name, + status: data.status, + date: new Date().toISOString(), + size: file.size + }; + + saveDocument(document); + + // Load updated document list + loadDocuments(); + + // Check document status and show report if ready + checkDocumentStatus(data.document_id); + + // Show success message + alert('Document resubmitted successfully! The analysis is in progress.'); + + } catch (error) { + console.error('Error resubmitting document:', error); + alert('Failed to resubmit document. Please try again.'); + } finally { + // Hide loading overlay + loadingOverlay.style.display = 'none'; + } + } +}); diff --git a/app/templates/report.html b/app/templates/report.html new file mode 100644 index 0000000..5d5f670 --- /dev/null +++ b/app/templates/report.html @@ -0,0 +1,295 @@ + + + + + + Document Compliance Report + + + +
+

Document Compliance Report

+
+
+

Document: {{ document_name }}

+

Generated: {{ timestamp }}

+
+
+
+ {{ compliance_score * 100 | round(1) }}% +
+
Compliance Score
+
+
+
+ +
+

Summary

+

{{ summary }}

+ + {% if applied_standards and applied_standards|length > 0 %} +
+

Applied Standards

+
    + {% for standard in applied_standards %} +
  • {{ standard }}
  • + {% endfor %} +
+
+ {% endif %} +
+ +
+
+
{{ critical_count }}
+
Critical Issues
+
+
+
{{ major_count }}
+
Major Issues
+
+
+
{{ minor_count }}
+
Minor Issues
+
+
+
{{ info_count }}
+
Info Issues
+
+
+ +
+

Compliance Issues

+ + {% if issues %} + {% for issue in issues %} +
+
+
{{ issue.section }}
+ {{ issue.level }} +
+
+ {{ issue.description }} +
+ {% if issue.reasoning %} +
+ Reasoning: {{ issue.reasoning }} +
+ {% endif %} + {% if issue.standard_references and issue.standard_references|length > 0 %} +
+ Standard References: +
    + {% for reference in issue.standard_references %} +
  • {{ reference }}
  • + {% endfor %} +
+
+ {% endif %} +
+ Recommendation: {{ issue.recommendation }} +
+
+ {% endfor %} + {% else %} +

No compliance issues found. Great job!

+ {% endif %} +
+ + + + \ No newline at end of file diff --git a/app/utils/__init__.py b/app/utils/__init__.py new file mode 100644 index 0000000..e9af105 --- /dev/null +++ b/app/utils/__init__.py @@ -0,0 +1 @@ +"""Utility functions for the Mini SpecsComply Pro application.""" \ No newline at end of file diff --git a/app/utils/helpers.py b/app/utils/helpers.py new file mode 100644 index 0000000..8f006e5 --- /dev/null +++ b/app/utils/helpers.py @@ -0,0 +1,283 @@ +# Utility functions +import re +from typing import Dict, List, Any, Optional +import os +from datetime import datetime +import json + +def extract_sections_from_markdown(markdown_text: str) -> Dict[str, str]: + """ + Extract sections from a markdown document. + + Args: + markdown_text: The markdown text to parse + + Returns: + Dictionary mapping section names to section content + """ + sections = {} + + # Add the whole document as one section + sections["full_document"] = markdown_text + + # Split by markdown headings + heading_pattern = re.compile(r'^(#{1,6})\s+(.+)$', re.MULTILINE) + matches = list(heading_pattern.finditer(markdown_text)) + + if matches: + for i, match in enumerate(matches): + heading_level = len(match.group(1)) + section_name = match.group(2).strip() + + # Get section content (from this heading to the next, or to the end) + start_pos = match.end() + end_pos = matches[i+1].start() if i < len(matches) - 1 else len(markdown_text) + + section_content = markdown_text[start_pos:end_pos].strip() + section_key = f"h{heading_level}_{section_name}" + + sections[section_key] = section_content + + return sections + +def detect_file_type(filename: str) -> str: + """ + Detect file type from filename extension. + + Args: + filename: Name of the file + + Returns: + File type (markdown, text, etc.) + """ + _, extension = os.path.splitext(filename) + ext = extension.lower().lstrip('.') + + if ext in ['md', 'markdown']: + return 'markdown' + elif ext in ['txt', 'text']: + return 'text' + elif ext in ['json']: + return 'json' + elif ext in ['yaml', 'yml']: + return 'yaml' + elif ext in ['html', 'htm']: + return 'html' + else: + return 'unknown' + +def parse_code_blocks(content: str) -> List[Dict[str, str]]: + """ + Extract code blocks from markdown content. + + Args: + content: Markdown content with code blocks + + Returns: + List of dictionaries with language and code + """ + # Pattern to match code blocks with optional language + pattern = r'```(\w*)\n([\s\S]*?)```' + matches = re.findall(pattern, content) + + code_blocks = [] + for language, code in matches: + code_blocks.append({ + 'language': language.strip() or 'text', + 'code': code.strip() + }) + + return code_blocks + +def format_timestamp(timestamp: datetime) -> str: + """ + Format timestamp for display. + + Args: + timestamp: Datetime object + + Returns: + Formatted timestamp string + """ + return timestamp.strftime("%Y-%m-%d %H:%M:%S") + +def calculate_readability_score(text: str) -> float: + """ + Calculate a simple readability score for text. + + Args: + text: The text to analyze + + Returns: + Readability score (0.0-1.0) + """ + if not text: + return 0.0 + + # Split into sentences and words + sentences = re.split(r'[.!?]+', text) + words = re.findall(r'\b\w+\b', text) + + if not words or not sentences: + return 0.0 + + # Average words per sentence + avg_words_per_sentence = len(words) / len(sentences) + + # Simple readability score based on average words per sentence + # Optimal is around 15-20 words per sentence + if avg_words_per_sentence <= 10: + score = 0.7 # Very short sentences + elif 10 < avg_words_per_sentence <= 20: + score = 1.0 # Optimal + elif 20 < avg_words_per_sentence <= 30: + score = 0.8 # Getting long + else: + score = 0.5 # Too long + + return score + +def sanitize_filename(filename: str) -> str: + """ + Sanitize filename to be safe for filesystem. + + Args: + filename: Original filename + + Returns: + Sanitized filename + """ + # Replace illegal characters + sanitized = re.sub(r'[<>:"/\\|?*]', '_', filename) + + # Ensure it's not too long + if len(sanitized) > 255: + base, ext = os.path.splitext(sanitized) + sanitized = base[:255-len(ext)] + ext + + return sanitized + +def load_standards_from_file(file_path: str) -> List[Dict[str, Any]]: + """ + Load compliance standards from a JSON file. + + Args: + file_path: Path to the standards JSON file + + Returns: + List of standard dictionaries + """ + try: + with open(file_path, 'r') as f: + standards = json.load(f) + return standards + except (FileNotFoundError, json.JSONDecodeError): + # Return empty list if file not found or invalid + return [] + +def _render_applied_standards(standards: List[str]) -> str: + """ + Render HTML for applied standards section. + + Args: + standards: List of standard names + + Returns: + HTML string for the applied standards section + """ + if not standards: + return "" + + html = """
+

Applied Standards

+
" + return html + +def generate_html_report(report_data: Dict[str, Any]) -> str: + """ + Generate HTML for compliance report. + + Args: + report_data: Report data dictionary + + Returns: + HTML string for the report + """ + # Simple HTML template for the report + html = f""" + + + + Compliance Report + + + +
+

Compliance Report

+

Document: {report_data.get('document_name', 'Unknown')}

+

Generated: {report_data.get('timestamp', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))}

+
+ Compliance Score: + {report_data.get('compliance_score', 0) * 100:.1f}% +
+
+ +
+

Summary

+

{report_data.get('summary', 'No summary available.')}

+ + {_render_applied_standards(report_data.get('applied_standards', []))} +
+ +
+

Compliance Issues

+ """ + + # Add issues + issues = report_data.get('issues', []) + if not issues: + html += "

No compliance issues found.

" + else: + for issue in issues: + level = issue.get('level', 'info').lower() + html += f""" +
+

{issue.get('section', 'Unknown Section')}

+

{level.upper()} {issue.get('description', 'No description')}

+

Recommendation: {issue.get('recommendation', 'No recommendation')}

+
+ """ + + # Close HTML + html += """ +
+ + + """ + + return html \ No newline at end of file diff --git a/app/utils/token_counter.py b/app/utils/token_counter.py new file mode 100644 index 0000000..c4fa20e --- /dev/null +++ b/app/utils/token_counter.py @@ -0,0 +1,80 @@ +""" +Token counting utilities for document processing. +""" +import tiktoken +from typing import Dict, List, Optional, Union +from loguru import logger + +# Default models to use for token counting +DEFAULT_MODEL = "gpt-4o" + +def count_tokens(text: str, model: str = DEFAULT_MODEL) -> int: + """ + Count the number of tokens in a text string using tiktoken. + + Args: + text: The text to count tokens for + model: The model to use for token counting (default: gpt-4o) + + Returns: + Number of tokens in the text + """ + try: + encoding = tiktoken.encoding_for_model(model) + return len(encoding.encode(text)) + except Exception as e: + logger.warning(f"Error counting tokens with model {model}: {str(e)}") + # Fallback to cl100k_base encoding if model-specific encoding fails + try: + encoding = tiktoken.get_encoding("cl100k_base") + return len(encoding.encode(text)) + except Exception as e: + logger.error(f"Error counting tokens with fallback encoding: {str(e)}") + # If all else fails, use a rough approximation (4 chars per token) + return len(text) // 4 + +def truncate_by_tokens(text: str, max_tokens: int, model: str = DEFAULT_MODEL) -> str: + """ + Truncate text to fit within a maximum token count. + + Args: + text: The text to truncate + max_tokens: Maximum number of tokens to allow + model: The model to use for token counting (default: gpt-4o) + + Returns: + Truncated text that fits within max_tokens + """ + try: + encoding = tiktoken.encoding_for_model(model) + tokens = encoding.encode(text) + + if len(tokens) <= max_tokens: + return text + + # Truncate tokens and decode + truncated_tokens = tokens[:max_tokens] + truncated_text = encoding.decode(truncated_tokens) + + # Add truncation indicator + return truncated_text + "...(truncated)" + except Exception as e: + logger.warning(f"Error truncating by tokens with model {model}: {str(e)}") + # Fallback to character-based truncation if token-based fails + approx_chars = max_tokens * 4 # Rough approximation + if len(text) <= approx_chars: + return text + return text[:approx_chars] + "...(truncated)" + +def estimate_tokens_from_chars(char_count: int) -> int: + """ + Estimate the number of tokens from character count. + This is a rough approximation (4 chars per token on average). + + Args: + char_count: Number of characters + + Returns: + Estimated number of tokens + """ + return char_count // 4 diff --git a/data/1.Invitation to Tender.txt b/data/1.Invitation to Tender.txt new file mode 100644 index 0000000..59a4bd6 --- /dev/null +++ b/data/1.Invitation to Tender.txt @@ -0,0 +1,105 @@ +Oil and Gas Company +123 Industry Avenue +Houston, TX 77001 + +Reference: TND-2024-001 +Date: April 18, 2025 + +INVITATION TO TENDER FOR SUPPLY OF VALVES + +1. INTRODUCTION + Oil and Gas Company hereby invites qualified suppliers to submit tenders for the supply of industrial valves for our upcoming pipeline project. This document SHALL serve as the official invitation to tender. + +2. SCOPE OF SUPPLY + 2.1 The supplier SHALL provide industrial valves as specified in Section 6 of this document. + 2.2 The total quantity required SHALL be 250 units. + 2.3 Delivery SHALL be completed within 90 days of contract award. + 2.4 The budget allocation for this procurement is $750,000-$1,000,000. + +3. TIMELINE + 3.1 Tender Release Date: April 18, 2025 + 3.2 Pre-Tender Meeting: May 15, 2025 + 3.3 Question Submission Deadline: June 15, 2025 + 3.4 Tender Submission Deadline: August 31, 2025, 17:00 hrs (UTC-6) + 3.5 Evaluation Period: September 1-20, 2025 + 3.6 Contract Award Notification: September 30, 2025 + 3.7 Delivery Completion: December 31, 2025 + +4. SUBMISSION REQUIREMENTS + 4.1 Suppliers SHALL submit their proposals electronically to tenders@oilgascompany.com + 4.2 All submissions SHALL include: + a) Completed Technical Compliance Matrix (Appendix A) + b) Detailed Pricing Schedule (Appendix B) + c) Company Profile and References + d) Quality Assurance Documentation + e) Proposed Delivery Schedule + 4.3 Submissions SHOULD be in PDF format. + 4.4 Submissions received after the deadline SHALL NOT be considered. + +5. EVALUATION CRITERIA + Tenders SHALL be evaluated based on the following criteria: + 5.1 Technical Compliance (40%) + a) Adherence to specifications + b) Quality assurance processes + c) Testing and certification + 5.2 Commercial Terms (30%) + a) Pricing + b) Payment terms + c) Warranty provisions + 5.3 Delivery Schedule (15%) + 5.4 Experience and References (10%) + 5.5 Environmental and Safety Compliance (5%) + +6. TECHNICAL SPECIFICATIONS + 6.1 All valves SHALL comply with API 6D and ISO 14313 standards. + 6.2 Material Requirements: + a) Body: ASTM A216 WCB Carbon Steel + b) Trim: 316 Stainless Steel + c) Seats: PTFE with metal backing + 6.3 Operating Conditions: + a) Working pressure: 300-1500 psi + b) Temperature range: -20°C to 200°C + c) Media: Natural gas, crude oil + 6.4 Dimensions: + a) Sizes required: 2", 4", 6", and 8" (quantity breakdown in Appendix C) + b) End connections: RF flanged to ASME B16.5 + 6.5 Testing Requirements: + a) Hydrostatic testing as per API 598 + b) Fugitive emissions testing as per ISO 15848 + c) Fire testing as per API 607 + 6.6 Quality Assurance: + a) All valves SHALL be supplied with material test certificates. + b) The supplier SHALL maintain ISO 9001:2015 certification. + c) The supplier MAY be subject to facility inspection prior to contract award. + +7. TERMS AND CONDITIONS + 7.1 The supplier SHALL provide a warranty period of minimum 24 months. + 7.2 The supplier SHALL be responsible for packaging suitable for sea freight. + 7.3 Oil and Gas Company reserves the right to: + a) Accept or reject any tender + b) Cancel the tender process + c) Request additional information from suppliers + +8. CONTACT INFORMATION + For technical inquiries: + John Doe, Lead Engineer + Email: john.doe@oilgascompany.com + Phone: +1-234-567-8900 + + For commercial inquiries: + Jane Smith, Procurement Manager + Email: jane.smith@oilgascompany.com + Phone: +1-234-567-8901 + + For tender submission queries: + Procurement Department + Email: tenders@oilgascompany.com + Phone: +1-234-567-8902 + +Best regards, + +Robert Johnson +Project Manager +Oil and Gas Company +Email: robert.johnson@oilgascompany.com +Phone: +1-234-567-8903 \ No newline at end of file diff --git a/data/2.Tender Specifications.txt b/data/2.Tender Specifications.txt new file mode 100644 index 0000000..d420738 --- /dev/null +++ b/data/2.Tender Specifications.txt @@ -0,0 +1,54 @@ +Tender Specifications + +Technical Specifications: + • Valve Type: Ball Valve + • Size: 2 inches + • Pressure Rating: 300 PSI + • Temperature Range: -20°C to 150°C + • Material: Stainless Steel 316 + • End Connection: Flanged, ASME B16.5 + • Valve Type: Gate Valve + • Size: 4 inches + • Pressure Rating: 150 PSI + • Temperature Range: -10°C to 120°C + • Material: Carbon Steel + • End Connection: Threaded, NPT + • Valve Type: Check Valve + • Size: 6 inches + • Pressure Rating: 200 PSI + • Temperature Range: -10°C to 100°C + • Material: Ductile Iron + • End Connection: Wafer, API 594 +Performance Requirements: + • Ball Valve: Suitable for chemical processing with a minimum lifespan of 10 years. + • Gate Valve: Suitable for water distribution with a minimum lifespan of 8 years. + • Check Valve: Suitable for oil flow control with a minimum lifespan of 10 years. +Testing and Inspection Criteria: + • Ball Valve: Hydrostatic Testing (1.5 times working pressure), Non-Destructive Testing (Ultrasonic Testing), Third-Party Inspection. + • Gate Valve: Hydrostatic Testing (1.5 times working pressure), Visual Inspection, Third-Party Inspection. + • Check Valve: Hydrostatic Testing (1.5 times working pressure), Non-Destructive Testing (Radiographic Testing), Third-Party Inspection. +Material Requirements: + • Ball Valve: ASTM A351 CF8M, Material Test Certificates required. + • Gate Valve: ASTM A216 WCB, Material Test Certificates required. + • Check Valve: ASTM A536 65-45-12, Material Test Certificates required. +Manufacturing Standards: + • Ball Valve: API 598, ASME B16.34, Tolerance ±0.1 mm. + • Gate Valve: API 600, ASME B16.10, Tolerance ±0.1 mm. + • Check Valve: API 594, ASME B16.1, Tolerance ±0.1 mm. +Compliance with Regulations: + • Ball Valve: Compliant with EPA and OSHA standards. + • Gate Valve: Compliant with ISO 14001 environmental standards. + • Check Valve: Compliant with RoHS directives. +Packaging and Shipping Requirements: + • Packaging: Wooden Crates with foam padding. + • Labeling: Valve Type, Size, Pressure Rating, and Serial Number. + • Shipping: FOB Destination, preferred logistics provider to be specified. +Documentation Requirements: + • Documents: Operating Manual, Material Test Certificates, Test Reports, Compliance Certificates. + • Format: PDF. + • Language: English. +After-Sales Support: + • Installation Assistance: On-site installation support provided. + • Maintenance: Annual Maintenance Contract available. + • Spare Parts: Spare Parts List and availability guaranteed for 10 years. + diff --git a/data/3.Bill of Quantities.txt b/data/3.Bill of Quantities.txt new file mode 100644 index 0000000..3c70646 --- /dev/null +++ b/data/3.Bill of Quantities.txt @@ -0,0 +1,75 @@ +# Impact of Internet on Business Operations and Procurement Analysis + +## 1. Introduction + +This document presents an analysis of internet technology's impact on business operations and includes a standardized Bill of Quantities (BoQ) for valve procurement. The content complies with ISO-9001:2015 quality management standards, IEEE-829 documentation requirements, and RFC-2119 terminology conventions for clarity and precision. + +## 2. Internet Impact on Business Operations + +### 2.1 Cost Efficiency + +The implementation of virtual communication technologies SHALL significantly reduce operational expenses. Companies SHOULD implement monthly virtual meetings while limiting physical gatherings to annual events. This approach has demonstrated cost efficiency as travel expense savings MAY be reallocated to develop other business segments. + +### 2.2 E-Commerce Capabilities + +The internet has transformed transaction capabilities by eliminating physical proximity requirements. Businesses SHALL leverage established platforms such as: +- Jumia +- Alibaba Express +- FXTM +- Octamarkets + +Managerial economists SHOULD utilize these platforms to establish investment opportunities and expand market reach. + +### 2.3 Marketing Enhancement + +Digital marketing has superseded traditional advertising methods. Businesses SHALL utilize social media platforms including: +- Twitter +- Facebook +- WhatsApp +- Instagram + +These platforms provide enhanced audience reach with measurable engagement metrics that MUST be tracked for ROI analysis. + +### 2.4 Price Comparison and Market Research + +The internet enables efficient price comparison across multiple vendors. Procurement specialists SHALL utilize online resources to compare product specifications and pricing before purchase decisions. This process MUST be documented according to ISO-9001:2015 procurement procedures. + +### 2.5 Organizational Development + +To maintain competitive advantage, companies SHALL implement technological advancements including: +- Office automation systems +- Virtual interview capabilities +- Remote work infrastructure +- GPS-enabled personnel tracking + +These implementations SHOULD create more efficient operational environments as required by ISO-9001:2015 continuous improvement provisions. + +## 3. Bill of Quantities (BoQ) + +### 3.1 Valve Procurement Specification + +The following procurement schedule conforms to ISO-9001:2015 documentation requirements: + +| Item No. | Description | Technical Specification | Quantity | Unit | Standard Reference | +|----------|-------------|-------------------------|----------|------|-------------------| +| 1 | Ball Valve | Size: 2 inch, Material: Carbon Steel, Pressure Rating: ANSI 150, End Connection: Flanged RF | 100 | Each | API 6D, ISO 14313 | +| 2 | Gate Valve | Size: 4 inch, Material: Stainless Steel 316, Pressure Rating: ANSI 300, End Connection: Flanged RF | 50 | Each | API 600, ISO 10434 | +| 3 | Check Valve | Size: 6 inch, Material: Cast Iron, Pressure Rating: ANSI 150, End Connection: Wafer Type | 30 | Each | API 594, ISO 5802 | + +### 3.2 Quality Assurance Requirements + +All valves SHALL undergo hydrostatic testing as per API 598. +Material certificates MUST be provided for each valve. +Valves SHALL be packaged to prevent damage during transportation. + +## 4. Conclusion + +This document has presented the significant impacts of internet technologies on modern business operations, demonstrating how digital transformation enables cost reduction, market expansion, and operational efficiency. The standardized Bill of Quantities provides a clear procurement specification for valve acquisition that meets international standards. + +Future work SHOULD focus on implementing integrated digital procurement systems that connect directly with approved vendors to further streamline the acquisition process. + +## 5. References + +1. ISO 9001:2015 - Quality management systems — Requirements +2. IEEE 829-2008 - IEEE Standard for Software and System Test Documentation +3. RFC 2119 - Key words for use in RFCs to Indicate Requirement Levels \ No newline at end of file diff --git a/data/4.Scope of Work.txt b/data/4.Scope of Work.txt new file mode 100644 index 0000000..ff11543 --- /dev/null +++ b/data/4.Scope of Work.txt @@ -0,0 +1,11 @@ +5. Scope of Work (SoW) + +Objective: + • To supply, deliver, and install valves as per the specifications and BoQ. +Deliverables: + • Supply of valves + • Delivery to 123 Oilfield Road, Houston, TX, 77001 + • Installation and commissioning +Special Requirements: + • On-site training for maintenance staff + diff --git a/data/7.Supplier SQualification requirements.txt b/data/7.Supplier SQualification requirements.txt new file mode 100644 index 0000000..8f3df6b --- /dev/null +++ b/data/7.Supplier SQualification requirements.txt @@ -0,0 +1,11 @@ +7. Supplier Qualification Requirements + +Eligibility Criteria: + • Minimum 5 years of experience in valve manufacturing + • ISO 9001 certification +Required Documents: + • Company Profile + • Financial Statements (Last 3 years) + • Client References + + diff --git a/data/8.form of tender.txt b/data/8.form of tender.txt new file mode 100644 index 0000000..a0d8fb5 --- /dev/null +++ b/data/8.form of tender.txt @@ -0,0 +1,40 @@ +10. Form of Tender + +Bid Submission Form: +Item +Description +Quantity +Unit Price +Total Price +1 +Ball Valve, 2 inch +100 +[Price] +[Total] +2 +Gate Valve, 4 inch +50 +[Price] +[Total] +3 +Check Valve, 6 inch +30 +[Price] +[Total] +Total Bid Price: [Total Amount] +Authorized Signatory: + • Name: + • Position: + • Company: + • Signature: + • Date: + +Submission Instructions: + • All bids must be submitted by August 31, 2024, to [Submission Address]. + • Electronic submissions to tender@oilgascompany.com are also accepted. + +Please ensure all sections are completed and submitted along with the necessary documentation. We look forward to receiving your tender. + +If you have any further questions or need assistance, feel free to reach out. + + diff --git a/data/9.confidentiality agreement.txt b/data/9.confidentiality agreement.txt new file mode 100644 index 0000000..1244d77 --- /dev/null +++ b/data/9.confidentiality agreement.txt @@ -0,0 +1,11 @@ +1. Introduction + +This document sets out the requirements, terms, and conditions for participating in the tender process. It is intended to provide potential bidders with the necessary information to prepare and submit a comprehensive bid. All participants are expected to read this document thoroughly and comply with the stated instructions. The information within should be used solely for the purpose of this tender and not disclosed to any third party without prior written consent. + +9. Confidentiality Agreement + +Agreement: + • +All information provided in this tender must be kept confidential and used solely for the purpose of preparing the bid. + • + diff --git a/data/API_Integration_Guide_User_Management_Service.docx b/data/API_Integration_Guide_User_Management_Service.docx new file mode 100644 index 0000000..6a6c3d0 Binary files /dev/null and b/data/API_Integration_Guide_User_Management_Service.docx differ diff --git a/data/Project1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Equipment List (1).txt b/data/Project1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Equipment List (1).txt new file mode 100644 index 0000000..c73cd4c --- /dev/null +++ b/data/Project1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Equipment List (1).txt @@ -0,0 +1,7604 @@ +This document has been developed from the Phase 1 document number, Project1-FEED CONTRACTOR-FACILITY-0000-PR-LST-0001 Rev 006, incorporating SAP coding requirements and technical updates. + + +PROJECT1 DEVELOPMENT PROJECT +EQUIPMENT LIST + + + + + + + + +002 +Approved for Design +LY" + +CF +DATE +001 +Issued for Approval +JG +JL +CF +DATE +000 +Issued for Review +JG +JL +CF +DATE +A01 +Inter-discipline check +JG +JL + +DATE +REV +DESCRIPTION +ORIG +CK'D +APP'D +DATE + +ORIGINAL DRAWING SIZE: A3 +SCALE: NONE +EQUIPMENT LIST +CLIENT : COMPANY +ELECTRONIC FILE: Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Project1-FEED CONTRACTOR--MUL-E000- PR-LST-0001 SH. 1 of 35 +PROJECT1 DEVELOPMENT PROJECT EQUIPMENT LIST +Page 1 of 35 Project1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 Rev 002 + +Rev. +002 + + +EQUIPMENT LIST DETAILS +GENERAL +NOTES & HOLDS + + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + + +Client +COMPANY +Checked By +JL + + + +Location +NOTES & HOLDS +Approved By +CF + + + +Job No. +PROJECT NUMBER +Revision +002 + + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + + +General Notes: +1 - Pages have been ordered as per System Code. +2 - Notes specific to equipment are listed in their relevant sheets. +3 - Capacity of FACILITY is 2.8 MSCMD (i.e. 2.0 MSCMD gas sales volume plus 10% shrinkage for LPG recovery at Location O plus 20% swing factor (DCQ) plus 0.1 MSCMD allowance for fuel gas). +4 - Operating conditions taken from Heat and Material balances: +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-CAL-0001 Heat and Material Balance: Lean Summer +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-CAL-0002 Heat and Material Balance: 30% Lean, 70% Rich Summer PROJECT1-FEED CONTRACTOR-PF01-E000-PR-CAL-0003 Heat and Material Balance: Lean Winter +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-CAL-0004 Heat and Material Balance: 30% Lean, 70% Rich Winter +5 - Aquifer water will be supplied to the FACILITY from a local aquifer well drilled at Project1. A back-up aquifer water supply will be available from the Tieret Well via a pipeline. (HOLD 2) +6 - All skids and packages will be manufactured with a requirement of easy accessibility for operation and maintenance with minimal screwed fittings. This shall be reviewed further in EPC. +7 - For more details for sizing basis of some equipment, refer to the relevant technical note: PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0001 Technical Note - FACILITY Turndown Requirements +PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0002 Technical Note - Design Pressure For Mobile Units at Wellhead +PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0003 Technical Note - Gas Treating Temperature +PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0004 Technical Note - Equipment required for Liquid Removal from Gas +PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0005 Technical Note - Purge Gas For Flare Header PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0006 Technical Note - Produced Water Disposal PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0007 Technical Note - Mercury Removal Unit Location PROJECT1-FEED CONTRACTOR-FLO-0000-PR-TNO-0001 Technical Note - Hydrate Inhibition +PROJECT1-FEED CONTRACTOR-MUL-0000-PR-TNO-0001 Technical Note - Flowline Pressure Protection +8 - The basis for material selection is discussed in the following correspondences: +PEL(UK)-RFI-000094 date 20/07/2012 COMPANY-MEMO-000645 date 24/07/2012 +9 - The following information are preliminary in the absence of vendor data: Weights, Vendor Package Skid Dimensions, Installed Power for pumps and compressor motors, Column / Vessel Internals, Number of Air Coolers and Heat Exchanger Shells. +10 - The changes in Rev 002 of the Equipment List are based on Design and Cost Improvement Report (PROJECT1-FEED CONTRACTOR-MUL-E000-MD-REP-0004) and subsequently issued Design Change Notices (DCNs). +Holds: +1 - Blowdown Study to confirm the minimum design temperatures of equipment (EPC). +2 - Aquifer water source and associated equipment (e.g. wells, pipelines) (EPC). +3 - To be confirmed (EPC). +4 - Package information to be confirmed by Supplier (EPC). + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 2 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 110 GAS WELLS +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +GAS WELLS +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +110-PNL-0101 TO +0108 +Wellhead Solar Power System +8 + +Notes 2, 6 + + +1.0 + + + + + +10 x 4 x 3 +1.5 +Vendor Standard + + + + + +110-XR-0109 TO +0116 +Chemical Injection Package +8 + +Note 2 +0.12m3/day NOTE5 + +0.25 + +383 +82 +-5 / 85 +50 +3.0 x 2.0 x 2.5 +4 each +316L SS +API 675 + + +For Corrosion Inhibitor + +110-V-0117 +Mobile Test Separator +1 + +Notes 1, 4 +Gas: 0.425 MSCMD Condensate: 3300 SBPD Water: 1100 SBPD + + + +94 +82 +-25 / 85 +50 +5.7 x 2.3 x 2.5 +15 +CS with 316L clad & 316 internals +ASME VIII Div 1 +Yes + +Packaged item on rental basis. + +110-F-0118 +Sand Filter +1 + +Note 1 + + + + +383 +280 +-25 / 85 +50 +1.0 x 1.0 x 3.0 +3.0 +CS with 316L clad & 316 internals + + + +150 micron mesh. + +110-HP-0119 TO +0126 +Wellhead Control Panel +8 + +Note 2 + + + + + + + + +3.0 x 2.0 x 2.0 +3.0 +Vendor Standard +API 554 + + +Includes Hydraulic Well Control Panel (HWCU) + +110-WHG-0151 TO +0158 +Wellheads +8 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR-PFD-0011 + + + + + + + + + + + + + + + +Not in Contractor's Scope. + +NOTE: + • One mobile skid mounted Mobile Test Separator and Sand Filter on rental basis common for all wells (Technical Note - Design Pressure For Mobile Units at Wellhead; PROJECT1-FEED CONTRACTOR-FACILITY-0000-PR-TNO-0002). + • Well site Solar Panels, Chemical Injection Package, Hydraulic Well Control Unit (HWCU) are required per well. + • Deleted. + • Wellhead Mobile Test Separator package includes Test Separator, Ground Flare and Ignitor Package, Choke Manifold and Transfer Pump. + • Corrosion / Scale Inhibitor injection rates to be confirmed during EPC Phase. + • Wellhead electrical load shall be optimised in order to reduce the power consumption and reduce the size of the Solar Panel during the EPC Phase. + • Equipment Tag 110-PK-0127 (Wellhead Water-bath Heater) has been deleted in Rev 002 of this document. +002 +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 3 of 35 + + + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 200 +CONDENSATE STABILISATION + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + +Client +COMPANY +Checked By +JL + + +Location +CONDENSATE STABILISATION +Approved By +CF + + +Job No. +PROJECT NUMBER +Revision +002 + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +200-HE-1401 +Pre-Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Shell & Tube + +1092 (Note 1) + +40 +Tube: 25 Shell: 10 +Tube: 19.6 Shell: 5.3 +Tube: -25 / 210 Shell: -25 / 210 +Tube: In= 20 Out= 50 Shell: In= 125, Out= 79.2 +0.6 ID x 6.5 T/T +3.6 +Shell: CS • 3mm CA Tubes: 316L SS Note 2, 3 +TEMA R ASME VIII DIV 1 + +Yes + + +200-V-1402 +Water / Condensate Separator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Three Phase Horizontal Separator + + + + +25 +19.1 +-25 / 85 +50 +2.3 ID x 5.7 T/T +18.0 +CS • 3mm CA with Internal Lining and 316L SS Internals Note 5 +ASME VIII Div 1 +Yes + +Plate pack internals. Weight does not include internals. + +200-HE-1403 +Condensate/ Condensate Heat Exchanger +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Shell & Tube + +1340 (Note 1) + +54 +Tube: 25 Shell: 10 +Tube: 18.8 Shell: 6.5 +Tube: -25 / 210 Shell: -25 / 210 +Tube: In= 50 Out= 114 Shell: In= 177, Out= 125 +0.65 ID x 6.5 T/T +4.0 +Shell: CS • 3mm CA Tubes: 316L SS Note 2, 3 +TEMA R ASME VIII DIV 1 + +Yes +Maximum duty and maximum temperature are not necessarily coincident. + +200-C-1404 +Condensate Stabilisation Column +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Packing + + + + +10 +6.5 +-5 / 210 +177 +1.1 ID x 13.3 T/T Note 4 +7.5 +CS with 316L SS Clad and 316L SS Internals Note 3 +ASME VIII Div 1 +Yes +Yes +Weight does not include internals. + +200-V-1405 +Water Draw Off Vessel +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Vertical + + + + +10 +6.4 +-5 / 210 +95 +0.8 ID x 2.0 T/T +1.3 +CS • 3mmCA with Internal Lining and 316L SS Internals Note 5 +ASME VIII Div 1 +Yes +Yes + + +200-HE-1406 +Condensate Reboiler +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Kettle Type + +2791 (Note 1) + +170 +Tube: FV / 15 Shell: FV / 10 +6.5 +Tube: -5 / 235 Shell: -5 / 210 +Tube: 220 Shell: 177 +0.9 / 2.0 ID x 7.5 L +8.0 +Shell: CS•316L SS Clad Tube Side: CS + • 3mm CA Tubes: 316L SS +TEMA R ASME VIII DIV 1 + +Yes +Maximum duty and maximum temperature are not necessarily coincident. + +200-CLR-1407 +Condensate Run Down Cooler +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0002 +Air Cooler + +1255 (Note 1) +44 +178 (Bare +Tube) +10 +4.8 +-5 / 135 +In: 115 Out: 60 +3.7 ID x 7.3 L +13.5 +Heads: CS • 3mm CA Tubes: CS • Al Fins +API 661 ASME VIII DIV 1 + + +1 bay - 2 fans per bay. Total Fan duty = 27 kW + +NOTE: + • This has been pro-rated from the Rich Winter Heat and Material Balance to correspond to 7,000 SBPD condensate product from FACILITY. + • Tube sheet / channel materials: CS • 316L SS Clad + • Solid CRA may be selected instead of CS Clad where cost / fabrication benefits exist. + • Condensate stabilisation column dimensions are based on structured packing internals. + • CS • 316L SS Clad is alternate material to internal lining. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 4 of 35 + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 210 +TEST SEPARATION AND METERING +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +TEST SEPARATION AND METERING +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +210-V-0301 +Test Separator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0001 +Three Phase Vertical vessel +Gas: 0.425 MSCMD Condensate: 3283 SBPD Water: 1094 SBPD + + + +FV / 65 +54 +-25 / 85 +50 +1.6 x 6.1 (ID x TT) +22.0 +CS with 316L SS Clad and 316L SS Internals +ASME VIII Div 1 +Yes + +The weight of vessel does not include internals. Includes 3m3 slug volume + +NOTE: + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 5 of 35 + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 220 +GAS RECEPTION AND SEPARATION +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +GAS RECEPTION AND SEPARATION +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + + + + + + +HP Header Gas : 2.83 MSCMD + + + + +HP: 55.3 + + + + + + + + +Skid mounted, 8 slot manifold comprising HP, MP, LP and test production header. + +220-XX-0401 +Inlet Manifold - HP, MP, LP & Test Header +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- + +Liquid: 10355 SBPD + + + +94 +MP: 20.0 +-25 / 85 +50 +19 m x 4.5 m +80 +LTCS + 4.5 mm CA +ASME B31.3 + + +HP: 16 x 19 m long; MP: 16 x 19 m long; LP: 16 x +002 + + + +PFD-0001 + +(Note 1) + + + + + + + + +Note 4 + + + + + + + + + + + + + + + + +LP: 7.0 + + + + + + + + +19 m long; Test: 6 x 19 m long + + + + + + +Test Header + + + + + + + + + + + + + +Note 4 + + + + + + +0.425 MSCMD + + + + + + + + + + + + + + + + + + + + + + + + + + + +Shell: 160 + + + + + + + + + + + + + + + + + +Tube: FV + +Tube: + + + +Shell: CS + 3mm CA + + + + + + + + + + + + + + + +Tube: + +Tube: In: + + + + + + + + +220-HE-0402 +Inlet Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- +Shell & Tube + +1,903 + +44 +/ 94 +54.8 +-25 / 235 +10°C +0.7 ID x 6.5 T/T +5.0 +Tubes: UNS S32750 +TEMA R + +Yes + + + + + +PFD-0001 + + + + + +Shell: FV + +Shell: -5 / + + + +Tubesheet: UNS S32750 +ASME VIII DIV 1 + + + + + + + + + + + + + + +Shell: 10 + +Out: 25°C + + + + + + + + + + + + + + + + + +/ 15 + +235 + + + +Channel: UNS S32750 + + + + + + + + + + + + + + + + + +(Note 3) + + + + + + + + + + + + + +Gas: 2.83 MSCMD + + + + + + + + + + + + + + + + + + + + +Condensate: 9309 SBPD + + + + + + + + + + + + + + + +220-V-0403 +Inlet Separator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0001 +Three Phase Vertical vessel +(Note 1) + + + +FV / 65 +54.3 +-25 / 85 +50 +2.2 ID x 7.3 T/T +42.0 +CS with 316L SS Clad and 316L SS Internals +ASME VIII Div 1 +Yes + +Weight does not include internals. Includes 3 m3 slug volume. + + + + + + +Water: 1046 SBPD + + + + + + + + + + + + + + + + + + + + +(Note 1) + + + + + + + + + + + + + + + +NOTE: + +1. This has been pro-rated from the Rich Winter Heat and Material Balance to correspond to 7,000 SBPD condensate product from FACILITY. + + • Inlet manifold will have provision to extend the headers for connection of future wells. + • During summer, the Inlet Heater will be bypassed. + + • Skid shall be divided into two units for transportation purposes. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 6 of 35 + + + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 232 +OFFGAS COMPRESSION + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + +Client +COMPANY +Checked By +JL + + +Location +OFFGAS COMPRESSION +Approved By +CF + + +Job No. +PROJECT NUMBER +Revision +002 + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +Offgas Compression Train consists of (Note 1,2): +232-VG-1001 +1st Stage Offgas Compression Suction Scrubber +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Vertical Vessel + + + + +FV / 13 +5.4 +-25 / 150 +74.6 +0.8 ID x 1.9 T/T +1.5 +CS • 316L SS Clad with 316L SS Internals +ASME VIII DIV 1 +Yes +Yes + +002 +232-K-1002 +1st Stage Offgas Compressor +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Reciprocating (Note 3) +6336 Sm3/h +284 +800 (Note 3) + +25 +In: 5.3 Out: 18.5 +-25 / 160 +In: 74.3 Out: 130 + +18 (Note 3) +Vendor Specified +API 618 + + +13.2 bar differential. (HOLD 4) +002 +232-CLR-1003 +1st Stage Offgas Compression Discharge +Cooler +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Air Cooler + +1,022 +30 +178 (Bare Tube) +FV / 25 +18.5 +-25 / 160 +In: 130 Out: 60 +3.7 x 7.3 +14.0 +316L SS tubes • header +API 661 ASME VIII DIV 1 + + +1 bay - 2 fans per bay. Total Fan duty = 22 kW +002 +232-VG-1004 +2nd Stage Offgas Compression Suction Scrubber +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Vertical Vessel + + + + +FV / 40 +18 +-25 / 115 +47.4 +1.0 ID x 2.3 T/T +2.0 +CS • 316L SS Clad with 316L SS Internals +ASME VIII div 1 +Yes +Yes + +002 +232-K-1005 +2nd Stage Offgas Compressor +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Reciprocating (Note 3) +8158 Sm3/h +334 +Note 3 + +65 +In: 17.9 Out: 55.4 +-25 / 150 +In: 47.4 Out: 120 +Note 3 +Note 3 +Vendor Specified +API 618 + + +37.5 bar differential. +002 +232-CLR-1006 +2nd Stage Offgas Compression Discharge +Cooler +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Air Cooler + +605 +25 +89 (Bare Tube) +FV / 65 +54.7 +-25 / 150 +In: 120 Out: 60 +1.8 x 7.3 +6.0 +316L SS tubes • header +API 661 ASME VIII DIV 1 + + +1 bays - 2 fans per bay. Total Fan duty = 12 kW +002 +232-P-1007A/B +Compressor Condensate Recycle Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0003 +Positive Displacement +3.4 m3/h @ 15.0 bar diff. +1.8 +3.0 + +25 +In: 5.3 Out: 20.6 +-25 / 100 +74.3 + +1.0 +CS +API 676 + + + +002 +NOTE: + • Offgas Compressor Train is of 1 x 100% configuration. All equipment for Train B have been deleted in Rev 002 of this document. + • Offgas Compressor may be packaged subject to size i.e. compressor (including gear box and driver) and scrubbers on a baseplate with control panel pre-wired and tested. + • Two stages on a single shaft. Weight and installed power included in first stage. Weight includes baseplate and motor.. +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 7 of 35 + + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 240 +GAS TREATMENT + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + +Client +COMPANY +Checked By +JL + + +Location +GAS TREATMENT +Approved By +CF + + +Job No. +PROJECT NUMBER +Revision +002 + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +240-FCL-0501 +Gas Treatment Filter Coalescer +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0004 +Vertical Vessel +2.89 MSCMD + + + +FV /65 +54.0 +-25 / 85 +49.5 +0.74 ID x 5.0 T/T (Note 1) +13.7 +316L SS with 316L SS internals (Note 2) +ASME VIII Div 1 +Yes + +Note 4 + +NOTE: + • Vessel dimensions based on vendor information from Phase 1. To be confirmed with supplier during EPC. + • CS Clad material may be used in lieu of solid CRA material. + • Equipment Tag 240-DR-0502 (Mercury Removal Bed) and 240-FC-0503 (After-Filter for Mercury Removal Unit) have been deferred for procurement until further information regarding mercury content is fully assessed. Accordingly, these items have been deleted in the Rev 002 of this document. + • HAZOP recommendation for a standby Filter Coalescer was not considered and 'Management of Change' to be issued by Company. +002 +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 8 of 35 + + + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 250 +GAS CONDITIONING (DEHYDRATION AND DEW POINTING) + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + +Client +COMPANY +Checked By +JL + + +Location +GAS CONDITIONING (DEHYDRATION AND DEW POINTING) +Approved By +CF + + +Job No. +PROJECT NUMBER +Revision +002 + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +250-HE-0701 +Export Gas/ Sweet Gas Heat Exchanger +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0005 +Hair Pin + +2,024 + +856 (Note 2) +FV / 65 +Tube: +52.0 Shell: +48.7 +-46 / 85 +Tube: In= 55, Out= 30.0; Shell: In= 20.3, Out= 50 +12 x 1.5 x 2.0 (Note 2) +30 +Shell: LTCS • 3 mm CA Tubes: 316L SS Tubesheet: 316L SS Channel: 316L SS +ASME VIII Div 1 / TEMA R + +Yes +Combine with 250-HE-0706 on one skid. + +250-FCL-0703 +TEG Contactor Filter Coalescer +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0005 +Vertical Vessel +2.86 MSCMD + + + +FV / 65 +51.4 +-25 / 85 +30 +1.8 ID x 5.3 T/T (Note 1) +25.0 +CS with 316L Clad and 316L SS Internals +ASME VIII Div 1 +Yes +Yes +Note 1 +002 +250-C-0704 +TEG Contactor +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0005 +Structured Packing (Note 3, 4) + + + + +FV / 65 +51.1 +-25 / 85 (Note 4) +30 +1.35 ID x 8.5 T/T (Note 1) +20.0 +Lower: CS • 316L SS Clad, Upper: CS • 3mm CA 316L SS Internals +ASME VIII Div 1 +Yes +Yes +The weight of column does not include internals + +250-PK-0705 +TEG Regeneration Package (comprising): +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-PFD-0005 +Note 3 + + + + + + + + +10.5 x 2.6 x 4.8 +26 +Vendor specified + + + +HOLD 4 + +250-CLR-0709 +Lean Glycol Air Cooler +1 + +Air Cooler (Note 3) + +40 +11 +20 (Bare Tube) +65 +52 +-5 / 100 +In: 86 Out: 60 + + +316L SS Header : Al finned tubes +API RP661 & ASME VIII DIV I + + +Part of TEG Regeneration Package +1 bay - 2 fans per bay. Total Fan duty = 5 kW +Not installed on skid. + +250-CND-0710 +Glycol Regeneration Condenser +1 + +Reflux Coil (Note 3) + +4 + + +3.5 +0.1 +-5 / 135 +100 + + +316L SS +API RP661 & ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-HE-0711 +Lean / Rich Glycol Heat Exchanger +1 + +Brown fin tube or plate & frame +exchanger +(Note 3) + +200 + +16 +10 (shell), 10 (tube) +0.2 (shell), 3.5 (tube) +-5 / 235 (tube), -5 / 235 (shell) +Tube: In:=35 +Out = 150 Shell: In= 204 +Out = 86 + + +CS • 3 mm CA +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-F-0712A/B +Glycol Particle Filter +2 + +Cartridges, vertical, polypropylene elements (Note 3) +2,250 kg/h + + + +10 +3.5 +-5 / 100 +35 + + +CS • 3 mm CA +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-F-0713 +Glycol Charcoal Filter +1 + +Charcoal, carbon elements (Note 3) +2,250 kg/h + + + +10 +4 +-5 / 100 +35 + + +CS • 3 mm CA +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-P-0714A/B +Lean Glycol Pump +2 + +Fixed Stroke Reciprocating (Note 3) +2.0 m3/h +5 +7.5 + +65 +53 +-5 / 140 +86 + + +CS +API STD 674 + +Yes +Part of TEG Regeneration Package. + +250-C-0715 +Regeneration Column +1 + +Random Packing (Note 3) +2,390 kg/h + + + +3.5 +0.3 +-5 / 235 +Bottom: 204 Top: 100.1 + + +316L SS +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-V-0716 +Glycol Flash Drum +1 + +Horizontal three phase separator (Note 3) + + + + +10 +4.2 +-5 / 100 +35.7 + + +CS • 3 mm CA +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-V-0717 +Glycol Surge Drum +1 + +Horizontal vessel (Note 3) + + + + +3.5 +0.3 +-5 / 235 +204 + + +CS • 3 mm CA +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. + +250-HE-0718 +Glycol Reboiler +1 + +Electric Heater in Horizontal +Vessel +(Note 3) + +145 +180 + +3.5 +0.3 +-5 / 235 +204 + + +CS • 3 mm CA +ASME VIII DIV I + +Yes +Part of TEG Regeneration Package. Additional 20% duty for start up case. + +250-HE-0706 +Export Gas/ Dry Gas Heat Exchanger +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0006 +Hair Pin + +1039 + +283 +FV / 65 +Tube: +50.3 Shell: +49.2 +-46 / 85 +Tube: In= 31, +Out= 17.7; Shell: In= 5.7, +Out= 20.3 +8.0 x 1.0 x 2.0 +17 +Shell: LTCS • 3 mm CA Tubes: LTCS Tubesheet: LTCS • 3mm CA Channel: LTCS • 3mm CA +TEMA R ASME VIII DIV 1 + +Yes +Combine with 250-HE-0701 on one skid. + +250-HE-0707 +Dry Gas Chiller +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0006 +Shell & Tube Exchanger + +1071 + +176 +Tube: FV / 65 Shell: FV / 18 +Tube: +49.8 Shell: 3.7 +-46 / 85 +Tube: In= 17.7, +Out= 5.8; Shell: In= 0.0, +Out= 1.5 +0.8 ID x 8 T/T +6.5 +Shell: LTCS • 3 mm CA Tubes: LTCS Tubesheet: LTCS • 3mm CA Channel: LTCS • 3mm CA +TEMA R ASME VIII DIV 1 + +Yes + + +250-V-0708 +Cold Separator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0006 +Vertical Vessel +2.8 MSCMD + + + +FV / 65 +49.3 +-46 / 85 +5.8 +1.7 ID x 3.2 T/T +15.0 +LTCS • 3 mm CA with 316L SS Internals +ASME VIII DIV 1 +Yes +Yes + + +NOTE: + • The TEG Contactor Filter Coalescer (250-FCL-0703) includes a 3-phase knock-out section at the bottom to separate water / amine and condensate streams from the cooled gas. Vessel dimensions to be confirmed with Supplier during EPC. Accordingly, Equipment Tag 250-KO-0702 (Sweet Gas KO Drum) has been deleted in Rev. 002 of this document. + • Exchanger type, area and dimensions (pro-rated for revised duty) are based on vendor information from phase 1. + • Package equipment and duties are typical. To be confirmed with supplier during EPC. + • TEG Contactor will be supplied by the Regeneration Package supplier. Design temperature to be confirmed with supplier for failure of Lean Glycol Cooler. +002 +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 9 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 270 +GAS EXPORT AND METERING +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +GAS EXPORT AND METERING +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +270-XX-1202 +Export Gas Fiscal Metering Package +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0009 +Includes On-line Gas Chromatograph and dew-point analyser +2.7 MSCMD + + + +94 +48.3 +-25 / 85 +50.2 +9.0 (L) x 4.0 (W) +20.0 +CS (Vendor Specified) +AGA 9 + + + + +NOTE: +1. Metering package details will be confirmed during EPC Phase. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 10 of 35 + + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 280 +GAS SWEETENING (AMINE) + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + +Client +COMPANY +Checked By +JL + + +Location +GAS SWEETENING (AMINE) +Approved By +CF + + +Job No. +PROJECT NUMBER +Revision +002 + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +280-C-0601 +Amine Absorber Column +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0004 +Tray (HOLD 3) (Note 1, 2) +1.2 MSCMD + + + +FV / 65 +54 +-25 / 85 (Note 2) +68.9 +1.3 ID x 20 T/T +35 +CS with 316L SS Clad and 316L SS Internals +ASME VIII Div 1 +Yes +Yes +The weight of column does not include internals. + +280-PK-0602 +Amine Regeneration Package (comprising): +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0004 +Note 1, 3 + + + + + + + + +Two Skids: +1 - 14.0 (L) x 4.0 (W) +2 - 11.5 (L) x 2.5 (W) +35 + + + + +HOLD 4 + +280-V-0603 +Amine Regeneration Flash Drum +1 + +(Note 1) +33 m3/h + + + +15.0 +9.0 +-5 / 85 +60 +Included in Package Skid + +CS with 316L SS Clad +ASME VIII Div 1 + +Yes +Part of Amine Regeneration Package + +280-HE-0604 +Amine Regeneration Lean / Rich Exchanger +1 + +Plate & Frame Type Exchanger (Note 1) + +1,020 + +2,500 +15.0 +Rich: 9.0 Lean: 3.5 +-5 / 150 +Rich: 60 - +90 Lean: 110 - +82 +Included in Package Skid + +316L SS +ASME VIII DIV I +Yes +Yes +Part of Amine Regeneration Package + +280-C-0605 +Amine Regeneration Stripper Column +1 + +HOLD 2 (Note 1) +33 m3/h + + + +5.0 +1.0 +-5 / 150 +110 +1.3 x 20.0 (ID x TT) (Free Standing) +11 +CS with 316L SS Clad +ASME VIII DIV I +Yes +Yes +Part of Amine Regeneration Package + +280-CLR-0606 +Amine Regeneration Condenser +1 + +Air Cooler (Note 1) + +100 +8 +30 (Bare Tube) +5.0 +0.9 +-5 / 150 +In: 80 Out: 60 +5.0 x 2.4 x 3.9 +5 +Duplex SS Header, 316 Tubes +API 661 & ASME VIII DIV I +Yes +Yes +Part of Amine Regeneration Package +1 bay - 2 fans per bay. Total Fan duty = 5.5 kW Induced draft type assumed. + +280-V-0607 +Amine Regeneration Condensate Drum +1 + +(Note 1) + + + + +5.0 +0.7 +-5 / 150 +60 +Included in Package Skid + +316L SS +ASME VIII DIV I + + +Part of Amine Regeneration Package + +280-P-0608A/B +Amine Regeneration Reflux Pump +2 + +Positive Displacement (Note 1) +2.0 m3/h +0.8 +1.5 + +5.0 +0.8 +-5 / 150 +60 +Included in Package Skid + +316L SS +API 676 + + +Part of Amine Regeneration Package + +280-P-0609A/B +Amine Regeneration Lean Solution Booster +Pump +2 + +Centrifugal (Note 1) +33 m3/h +11 +15 + +5.0 +3.5 +-5 / 150 +110 +Included in Package Skid + +CS / 316L SS Impeller +API 610 + +Yes +Part of Amine Regeneration Package + +280-CLR-0610 +Amine Regeneration Lean Solution Cooler +1 + +Air Cooler (Note 1) + +760 +22 +85 (Bare Tube) +5 +3.5 +-5 / 150 +In: 82 Out: 60 +3.0 x 9.0 x 4.0 +11 +316L SS +API 661 ASME VIII DIV 1 +Yes + +Part of Amine Regeneration Package +2 bays - 2 fans per bay. Total Fan duty = 13 kW Induced draft type assumed. + +280-F-0611A/B +Mechanical Filter +2 + +(Note 1) +33 m3/h + + + +15.0 +9.0 +-5 / 85 +60 +Included in Package Skid + +CS +ASME VIII DIV I +Yes + +Part of Amine Regeneration Package + +280-F-0612A/B +Carbon Filter +2 + +(Note 1) +6 m3/h + + + +5 +3.5 +-5 / 150 +60 +Included in Package Skid + +CS +ASME VIII DIV I +Yes + +Part of Amine Regeneration Package + +280-F-0613A/B +Mechanical Filter +2 + +(Note 1) +6 m3/h + + + +5 +3.5 +-5 / 150 +60 +Included in Package Skid + +CS +ASME VIII DIV I +Yes + +Part of Amine Regeneration Package + +280-P-0614A/B +Amine Regeneration Lean Solution Pump +2 + +Centrifugal (Note 1) +33 m3/h +65 +90 + +65 +56.0 +-5 / 85 +60 +Included in Package Skid + +CS / 316L SS Impeller +API 610 + + +Part of Amine Regeneration Package + +280-HE-0615 +Amine Regeneration Reboiler +1 + +Shell & tube (kettle) (Note 1) + +1,500 + +160 +5.0 +1.2 +-5 / 150 +110 +Included in Package Skid + +CS + 3mm CA 316L SS Tubes +TEMA R ASME VIII DIV 1 + +Yes +Part of Amine Regeneration Package. Heating Medium supply to reboiler. + +NOTE: + • Package equipment and duties are typical. To be confirmed with supplier during EPC. + • Amine Absorber Column will be supplied by the Amine Regeneration package supplier. Design temperature to be confirmed with supplier for failure of lean amine cooler. + • Automated sampling and analysis of amine solution to be provided utilising mass spectrometry type analytical system. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 11 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 310 +CONDENSATE EXPORT LINE +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +CONDENSATE EXPORT LINE +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +310-PLR-1601 +Export Condensate Pig Launcher +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0010 + + + + + +94 +Current: 41 Future: 82 (Note 3) +-25 / 85 +60 +Diameter (Minor/Major Barrel): 6"/10.75" NB Length: 5.8 m +1.00 +CS • 3mm CA +ASME B31.4/ ASME VIII Div 1 + + + + +310-PL-1602 +Export Condensate Pipeline +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0010 + +10,000 SBPD (Note 1) + + + +94 +Current: 41 Future: 82 (Note 3) +-25 / 85 +60 +11.7 km & 6" NB (7.11 mm thickness) +340 +CS API 5L X52• 3 mm CA Externally coated. +ASME B31.4 / NT +109.02 + + + + +310-PLR-1603 +Export Condensate Pig Receiver +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0010 +Note 2 + + + + +94 +72 (Note 3) +-25 / 85 +46 +Diameter (Minor/Major Barrel): 6"/10.75" NB Length: 8.5m +1.00 +CS • 3mm CA +ASME B31.4/ ASME VIII Div 1 + + + + +310-PIT-1604 +Export Condensate Drain Sump +1 + +Note 2 + + + + +ATM +ATM +-5 / 85 +AMB +1.0 x 1.0 x 1.0 + +Lined Concrete + + + + + +310-PNL-1605 +PROJECT Z Solar Power System +1 + +Note 2, 4 + + +0.4 + + + + + +5.0 x 4.0 x 3.0 +0.8 +Vendor Standard +API 554 + + + + +NOTE: + • The Export Condensate Pipeline is designed for 10,000 SBPD; the design margin in the capacity enables to bring down the storage inventory after a PROJECT Z shutdown. + • Equipment located near tie-in with PROJECT Z facilities. + • Operating pressure depends on throughput of PROJECT Z pipeline. Currently PROJECT Z is operating a relatively low capacity and export pipeline operating pressure is consequently lower. + • PROJECT Z electrical load shall be optimised in order to reduce the power consumption and reduce the size of the Solar Panel during the EPC Phase. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 12 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 320 GAS EXPORT LINE +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +GAS EXPORT LINE +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + + + + +Tag Number +Equipment Name +QTY + +Package Notes +Capacity +Duty kW +Instal. Power +kW +PFD No.(each) +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +320-PLR-1101 +Export Gas Pig Launcher +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0009 + + + + + +94 +46.4 +-25 / 85 +49.4 +Diameter (Minor/Major Barrel): 24' /30' NB Length: 7.2m +2.70 +CS + 3mm CA +ASME B31.8/ ASME VIII Div 1 + + + + +320-PL-1102 +Export Gas Pipeline +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0009 + +2.7 MSCMD + + + +94 +46.4 +-25 / 85 +49.4 +51.8 km x 24' (9.53mm thickness) +7310 +CS API 5L X65 + 1.5 mm CA +ASME B31.8 + + +Future injection pressure at Location Z Receiving +Station is 70barg. +External 3 layer PE coating (3mm thick). + +320-XX-1103 +Export Gas Pipeline Block Valve Station +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR-PFD-0009 + + + + + +94 +46.4 +-25 / 85 +49.4 + +4.5 +Vendor Standard +ASME B31.8 + + +24' NB Block Valve. + +320-PLR-1104 +Export Gas Pig Receiver +1 +PROJECT1-FEED CONTRACTOR-OF02-E000-PR- PFD-0009 + + + + + +94 +46.4 +-25 / 85 +49.4 +Diameter (Minor/Major Barrel): 24' /30' NB Length: 10.6m +2.7 +CS + 3mm CA +ASME B31.8/ ASME VIII Div 1 + + + + +320-PNL-1105 +Block Valve Station Solar Power System +1 + +Note 1 + + +0.4 + + + + + +5.0 x 4.0 x 3.0 +0.8 +Vendor Standard +API 554 + + + + +320-PIT-1106 +Export Gas Drain Sump +1 + + + + + + +ATM +ATM +-5 / 85 +AMB +1.0 x 1.0 x 1.0 + +Lined Concrete + + + + + +320-PNL-1107 +HRS Solar Power System +1 + +Note 1 + + +0.4 + + + + + +5.0 x 4.0 x 3.0 +0.8 +Vendor Standard +API 554 + + + + +NOTE: +1. HRS and Block Valve Station electrical loads shall be optimised in order to reduce the power consumption and reduce the size of the Solar Panel during the EPC Phase. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 13 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 330 +CONDENSATE STORAGE AND METERING +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +CONDENSATE STORAGE AND METERING +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + + + + + + + + + + + + + + + + +CS + 3 mm CA + + + + + +330-TK-1501 +Condensate Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0008 + +1600 m3 (Note 3, 5) + + + +0.17 +ATM +-5 / 85 +60 +15.0 ID x 12.6 H +100 +with non-metallic internal lining +API 650 + + + + + + + + + + + + + + + + + + + +CS + 3 mm CA + + + + + +330-TK-1502 +Condensate Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0008 + +1600 m3 (Note 3, 5) + + + +0.17 +ATM +-5 / 85 +60 +15.0 ID x 12.6 H +100 +with non-metallic internal lining +API 650 + + + + + + + + + + + + + + + + + + + +Case: Cast Iron + + + +Note 1 + + + + + +Vertical Multistage +33 m3/h @ 40.3 bar + + + + + + + + + +Impeller: Cast + + + + + +330-P-1504A/B +Condensate Export Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0008 +Can Type Centrifugal Pump +dp (Note 1) +62 +75 + +94 +40 +-5 / 85 +63 + +3.4 each +Iron +API 610 Material +Designation I-1 +API 610 + + +2 x 50% pumps. In future, the pumps may operate with a dp of 82 bar and may require revamping or replacement. +002 + + + + + + + + + + + + + + + +Case: Cast Iron + + + + + + + + + +Vertical Multistage + + + + + + + + + + +Impeller: Cast + + + + + +330-P-1505A/B +Condensate Recycle Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0008 +Can Type Centrifugal Pump +16.6 m3/h @ 20.5 bar dp +21 +30 + +28 +20.5 +-5 / 85 +60.8 + +1.7 +Iron +API 610 Material +API 610 + + +Electric Driven. 2x100% pumps. +002 + + + + + + + + + + + + + + + +Designation I-1 + + + + + + + + + + + + + + + + + + + + +CS + 3 mm CA + + + + + +330-XX-1507 +Condensate Metering Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0008 + +66 m3/h (Note 1) + + + +94 +82.1 +-5 / 85 +63 + +2.0 +(Vendor specified) + + + +LACT unit. (Note 6) + +NOTE: + + • Each Condensate Export Pump capacity corresponds to a nominal production rate of 5000 SBPD. Both export pumps may need to be in operation to bring down the storage inventory after a PROJECT Z shutdown. +002 + • Condensate Recycle Pump is designed for turndown and reprocessing requirements. +002 + • Condensate storage tanks: Each tank has 2 days storage capacity corresponding to a nominal production rate of 5000 SBPD. + + • Equipment items 330-TK-1503 (Offspec Condensate Storage Tank), 330-P-1506 (Offspec Condensate Recycle Pump) and 330-P-1504C (Future Condensate Export Pump) have been deleted in Rev. 002 of this document. +002 + • Condensate storage tanks: Specified capacity is the net required storage capacity. Tank dimensions include margins for ullage and dead volume and are based on API 650 typical size. + + • LACT Unit comprises coriolis mass flow meter, with RVP analyser, BS&W monitor and density measurement. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 14 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 360 WELL FLOWLINES +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +WELL FLOWLINES +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + + + + + + + + + + + + + + +Diameter (minor/major + + +ASME VIII DIV 1 / + + + + +360-PLR-0201 +Mobile Pig Launcher +1 + +Note 1 + + + + +FV / 94 + +-25 / 85 + +barrel): 6 /10.75 NB +2.0 +CS + 3mm CA + + + +Packaged item + + + + + + + + + + + + + + + + + +B31.8 + + + + + + + + + + + + + + + + + +Length = 5.8m + + + + + + + + + + + + + + + + + + + + +Diameter (minor/major + + +ASME VIII DIV 1 / + + + + +360-PLR-0202 +Mobile Pig Receiver +1 + +Note 1 + + + + +FV / 94 + +-25 / 85 + +barrel): 6 /10.75 NB +2.0 +CS + 3mm CA + + + +Packaged item + + + + + + + + + + + + + + + + + +B31.8 + + + + + + + + + + + + + + + + + +Length = 8.5m + + + + + + + +360-PL-0210 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +61 +-25 / 85 +50 +7.5 km & 6 NB +220 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 2-1. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0211 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +59 +-25 / 85 +50 +7.4 km & 6 NB +220 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 2-2. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0212 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +62 +-25 / 85 +50 +6.3 km & 6 NB +185 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 5-1. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0213 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +68 +-25 / 85 +50 +20.0 km & 6 NB +580 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 6-1. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0214 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +82 +-25 / 85 +50 +20.5 km & 6 NB +590 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 4-1. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0215 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +73 +-25 / 85 +50 +13.0 km & 6 NB +380 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 3-1. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0216 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +59 +-25 / 85 +50 +3.4 km & 6 NB +120 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Well 1-1. Notes 4, 7 + + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0217 +Wellhead flowline +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +94 +58 +-25 / 85 +50 +1.5 km & 6 NB +50 +CS API 5L X52 + 3mm CA +ASME B31.8 / + + +Project1-1. Notes 4, 7 +002 + + + +PFD-0011 + + + + + + + + + + + + +NT109.01 + + + + +360-PL-0218 +Methanol Line for Project1 +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +383 +Approx. +-5 / 85 +50 +1.5km & 2 NB +17 +CS API 5L X52 + 3mm CA +ASME B36.10 + + +Note 5, 8 + + + + +PFD-0011 + + + + + + +280 + + + + + +B31.4/ NT 109.02 + + + + +360-PL-0219 +Methanol Line for Well 2 1/2 +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +383 +Approx. +-5 / 85 +50 +10.54km & 2 NB +120 +CS API 5L X52 + 3mm CA +ASME B36.10 + + +Note 5, 8 + + + + +PFD-0011 + + + + + + +280 + + + + + +B31.4/ NT 109.02 + + + + +360-PL-0220 +Methanol Line for Well 4 / Well 3 / Well 5 +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +383 +Approx. +-5 / 85 +50 +24.19km & 2 NB +270 +CS API 5L X52 + 3mm CA +ASME B36.10 + + +Note 5, 8 + + + + +PFD-0011 + + + + + + +280 + + + + + +B31.4/ NT 109.02 + + + + +360-PL-0221 +Methanol Line for Well 6 / Well 1 +1 +PROJECT1-FEED CONTRACTOR-OF01-E000-PR- + + + + + +383 +Approx. +-5 / 85 +50 +19.75km & 2 NB +220 +CS API 5L X52 + 3mm CA +ASME B36.10 + + +Note 5, 8 + + + + +PFD-0011 + + + + + + +280 + + + + + +B31.4/ NT 109.02 + + + + +NOTE: + + • One portable skid mounted Mobile pig launcher and Mobile pig receiver common for all wells. Dimensions based on typical supplier information. + + • Deleted. +002 + • All flowlines to be buried and provided with an external 3 layer PE coating (3 mm thk). + + • Flowline wall thickness is 7.11mm for all flowlines. Corrosion allowance for flowline specified at 3mm by COMPANY Laboratory in Country2 (FEED Contractor recommendation is for 6mm CA). +002 + • Methanol lines from FACILITY to Wellheads are routed along the flowlines in trenches. Methanol line thickness: 8.74mm. + + • All Wellheads shall be provided with PSVs including vent stacks. This is for over-pressure protection of the flowlines (Technical Note - Flowline Pressure Protection; PROJECT1-FEED CONTRACTOR-MUL-0000-PR-TNO-0001). +002 + • Pipeline depressuring rate shall confirm the minimum design temperature of the pipelines. + + • The methanol injection line to the wellheads are represented by lines 2 -XA-360-GGG-25CH3M on P&ID PROJECT1-FEED CONTRACTOR-OF01-E360-PR-PID-0001. See also notes 2, 3 and 4 on the same P&ID. + + • Equipment tags 360-KO-0203 to 0209 (Flowline Relief Knock Out Drums) have been deleted in Rev. 002 of this document. +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 15 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 400 COOLING MEDIUM +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +COOLING MEDIUM +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +400-PK-1901 +Refrigeration Package (comprising): +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0009 +Note 1, 2 + +1071 (thermal) + + + + + + + + + + + + +HOLD 4 + +400-RCV-1903 +Refrigerant Receiver Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0009 + +5.3 m + + + +HOLD 4 +20.3 +HOLD 4 +60 +1.5 ID x 4.5 T/T +7.0 +Vendor Specified +ASME VIII Div 1 + +Yes +Part of Refrigeration Package. + +400-KO-1904 +Suction Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0009 + + + + + +HOLD 4 +3.5 +HOLD 4 +1.5 +1.3 D x 2.6 T/T +3.5 +Vendor Specified +ASME VIII Div 1 + +Yes +Part of Refrigeration Package. + +400-CLR-1905 +Condenser (Air Cooler) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0009 +Air Cooler + +1,604 +60 + +HOLD 4 +21.0 +HOLD 4 +In: 81 Out: 60 +12.5 x 4.8 x 5.0 +20.0 +Vendor Specified +ASME VIII DIV 1 API 661 + + +Part of Refrigeration Package. 1 Bay - 2 Fans. Fan duty = 41 kW + +400-K-1906 +Refrigerant Compressor +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0009 +Screw + +532 +640 + +HOLD 4 +In: 3.4 Out: 21 +HOLD 4 +In: 1.1 Out: 81 + +20.0 +Vendor Specified +API 619 + +Yes +Note 4 +Part of Refrigeration Package. 1 x 100% compressor. +002 +400-PK-1902 +Refrigeration Storage Vessel Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0009 +Note 3 +7.5 m + + + +18 + +-25 / 85 + +1.37 OD x 4.7 T/T +2.0 +Vendor Specified +ASME VIII Div 1 + + +HOLD 4 + +NOTE: + • Package equipment and duties are based on vendor information in Phase 1. To be confirmed with supplier in EPC. + • Refrigeration based on propane refrigerant. + • Tote tank containing refrigerant will be delivered to Site and held in-place on a permanent basis to replenish the system inventory in case of losses. Propane inventory required is approximately 3.5 te. Dimension shown above is for typical 2000 US gallon propane tote tank. + • Space and Tie-ins shall be provided within the package unit for future installation of a spare compressor. +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 16 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 410 HEATING MEDIUM +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +HEATING MEDIUM +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +410-PK-1801 +Heating Medium Package (comprising): +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0008 +Notes 1, 2 + + + + + + + + + + + + + + +HOLD 4 + +410-EX-1802 +Heating Medium Expansion Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0008 +Note 1 +4 m3 + + + +15 +3.5 +-5 / 235 +135 +1.3 ID x 3.9 T/T +1.0 +Vendor Specified +ASME VIII Div 1 + +Yes +Part of Heating Medium Package + +410-P-1803A/B +Heating Medium Circulation Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0008 +Centrifugal (Note 1) +263 m3/h each @ 7 bar dp +85 each +115 + +15 +10 +-5 / 235 +135 + +2.0 +Vendor Specified +API 610 + +Yes +Part of Heating Medium Package + +410-F-1804 +Heating Medium Filter +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0008 +Note 1 + + + + +15 +10 +-5 / 235 +135 + +0.5 +Vendor Specified +ASME VIII DIV 1 + +Yes +Part of Heating Medium Package + +410-FH-1805 +Heating Medium Fired Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0008 +Note 1 + +6,100 + + +15 +10 +-5 / 235 +220 +5.0 OD x 14 H +27.0 +Vendor Specified +API 560 / 530 + +Yes +Part of Heating Medium Package + +410-CLR-1806 +Heating Medium Dump Cooler +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0008 +Air Cooler (Note 1) + +2,133 +37 +142 (Bare) +15 +8.5 +-5 / 235 +185 +2.5 x 4.6 +7.3 +Vendor Specified +API 661 + + +Part of Heating Medium Package +2 bays - 2 fans per bay. Total Fan duty = 18 kW + +410-V-1807 +Heating Medium Drain Vessel +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0008 + +35 m3 + + + +3.5 +0.05 +-5 / 185 +135 +2.6 ID x 7.5 T/T +8.1 +CS + 3mm CA Externally Coated +ASME VIII Div 1 + + + + +410-P-1808A/B +Heating Medium Drain Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0008 + +5 m3/h @ 8 bar dp +2.8 +4.0 + +13 +8.5 +-5 / 185 +135 + +1.0 +CS case with CS impeller. API 610 material designation S-9 +API 610 + + + + +NOTE: + • Package equipment and duties are typical. To be confirmed with supplier during EPC. The Heating Medium Package shall comprise several skids. + • The heating medium system comprises two temperature levels of hot oil supply to consumers: Heating medium temperature to Condensate Stabilisation Reboiler and Inlet Heater will be 220°C; while that to Amine Regeneration Reboiler will be 160°C. Typical controls within the package unit are shown in the UFD, which should be confirmed by supplier in EPC. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 17 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 420 CHEMICAL INJECTION +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +CHEMICAL INJECTION +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +420-PK-2801 +Chemical Injection Package (Process) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- + + +Approx. +Approx. + +65 +55.0 +-5 / 85 +50 +3.0 x 4.0 x 3.0 +16.0 +SS (Vendor +API 675 + + +Notes 1, 2, 5 + + + + +UFD-0006 + + +1 +2 + + + + + + + +Specified) + + + + + +420-PK-2802 +Chemical Injection Package (Produced Water) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- + + +Approx. +Approx. + +10 +3.0 +-5 / 85 +50 +3.0 x 3.0 x 2.0 +10.0 +SS (Vendor +API 675 + + +Notes 3, 4, 5 + + + + +UFD-0006 + + +1 +2 + + + + + + + +Specified) + + + + + +NOTE: + +1. The injection chemicals for process typically are corrosion inhibitor, anti-foam, scale inhibitor, demulsifier etc + +2. The Chemical Injection Package for process comprises of (HOLD 4): + + • Multicompartmented tank (4 compartments) + + • Dosing pumps - Multihead dosing pumps (2 x 100%) with 2 heads each are required for Antifoam injection; + +- Multihead dosing pumps (3 x 50% - 2 operating + 1 standby) with 4 heads each are required for Corrosion Inhibitor. The same applies to Scale Inhibitor injection, if required. + +- 2 x 100% dosing pumps for Demulsifier + • Mixers (one per compartment) + +3. The injection chemicals for produced water are corrosion inhibitor, reverse demulsifier, biocide, etc. + +4. The Chemical Injection Package for Produced Water system comprises of (HOLD 4): + + • Multicompartmented tank (3 compartments) + • Dosing pumps (3 operating + 3 standby) + • Mixers (3; one per compartment) + +5. For details, refer Chemical Injection Packages datasheet (PROJECT1-FEED CONTRACTOR-PF01-E420-PR-DAT-0028) + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 18 of 35 + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +FLARE +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +EQUIPMENT LIST DETAILS SYSTEM CODE: 430 +FLARE +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +430-VD-1701 +HP Flare KO Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0003 + +Gas: 104427 kg/h (2.8 MSCMD) Liquid: Liquid: 45352 kg/h (57 m3/h) + + + +FV / 10 +4.0 +-46 / 160 +50 +2.8 ID x 6.5 T/T +12.0 +CS • 3mm CA with non-metallic internal lining +ASME VIII Div 1 + +Yes + + +430-P-1702A/B +HP Flare KO Drum Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0003 +Centrifugal +24.2 m3/h @ 2.5 bar dp +2.9 +5.5 + +15 +2.9 +-5 / 85 +50 + +1.0 +Case: CS Impeller: CS API 610 Material Designation S-5 +API 610 + + + + +430-SK-1703 +HP Flare Stack +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0003 +Package Unit (Note 2) +Gas: 104427 kg/h (2.8 MSCMD) (Note 1) + + + +10 +3.2 +-46 / 160 +50 +Flare stack diameter 18"; Flare tip diameter 16"; Stack ht - 35m (Note 1) +16.0 +Vendor Specified +API 537 / API 521 + + + + +430-BIG-1704 +Ignition Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0003 +Package Unit (Note 3) + + + + + + + + + +0.7 +Vendor Specified + + + +Common for HP and LP Flare + +430-VD-1705 +LP Flare KO Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0004 + +Gas: 32030 kg/h (0.34 MSCMD) +Liquid: 9354 kg/h (9.4 m3/h) + + + +FV / 3.5 +0.3 +-5 / 210 +50 +1.8 ID x 5.0 T/T +4.0 +CS • 3mm CA with non-metallic internal lining +ASME VIII Div 1 + + + + +430-P-1706A/B +LP Flare KO Drum Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0004 +Centrifugal +5.5 m3/h @ 2.7 bar dp +1.3 +2.2 + +8 +3.2 +-5 / 85 +50 + +1.0 +Case: CS Impeller: CS API 610 Material Designation S-5 +API 610 + + + + +430-SK-1707 +LP Flare Stack +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0003 +Package Unit (Note 2) +Gas: 32030 kg/h (0.34 MSCMD) (Note 1) + + + +3.5 +0.15 +-5 / 210 +50 +Flare stack diameter 16"; Flare tip diameter 16" ; Stack ht - 26m (Note 1) +15.0 +Vendor Specified +API 537 / API 521 + + + + +430-SK-1708 +CO2 Vent Stack +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0003 +Package Unit +1814 kg/h (25544 SCMD) + + + +3.5 +0.2 +-5 / 150 +54 +6" vent Stack ht - 20m +2.0 +Vendor Specified + + + + + +430-EH-1709 +HP Flare KO Drum Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0003 +Electric Heater + + +40 + +10.0 + +-46 / 160 + + +0.5 +Element: 316L SS + + + + + +430-EH-1710 +LP Flare KO Drum Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0004 +Electric Heater + + +15 + +3.5 + +-5 / 210 + + +0.3 +Element: 316L SS + + + + + +NOTE: + • HP and LP Flare System capacity and equipment sizes to be confirmed during EPC phase after a Flare and Blowdown Study. + • HP and LP Flare Stack packages typically include: +- Flare Tip Assembly +- Flare Stack and Pilot Gas Riser +- Seal +- Pilot Gas System +- Pilot Gas Back-Up (Propane) + • Redundant Ignition and Monitoring Systems required. One of the ignition sources shall be electronic flare ignition type. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 19 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 440 +PRODUCED WATER TREATMENT +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +PRODUCED WATER TREATMENT +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +440-V-1301 +Produced Water Flash Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-PFD-0007 +Horizontal Vessel +10.2 m3/h + + + +FV / 5 +1.0 +-25 / 85 +50 +1.2 ID x 3.5 T/T +2.8 +CS + 3mm CA ; non-metallic Internal lining +ASME VIII Div I + + + + +440-P-1302 A/B +Produced Water Feed Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0007 +Centrifugal +10.2 m3/h @ 3 bar dp +1.8 +3.0 + +10 +4 +-5 / 85 +50 + +1.7 +Case: DSS Impeller: DSS API 610 Material Class D1 or D2 +API 610 + + +Electric Drive Motor. + +440-PK-1303 +Produced Water Treatment Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0007 +Note 3 +10.2 m3/h +10 + + + + + + + +10 +Vendor Specified + + + +HOLD 4 +002 +440-PIT-1304A/B +Evaporation Pond +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0007 + + + + + + + +-5 / 85 +50 +120 x 120 x 1.5 + +Geo-Textile Lined + + + +Evaporation Ponds to be internally lined with Geo-Textile Membrane. + +440-PK-1305A/B +Oil Skimmer Package +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-PFD-0007 + + + +2 + + + + + + + + + + + +One for each Evaporation Pond. HOLD 4. + +440-PIT-1306 +Offspec Water Pit +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- PFD-0007 + + + + + + + +-5 / 85 +50 +10 x 10 x 1.5 + +Geo-Textile Lined + + + +To be internally lined with Geo-Textile Membrane + +NOTE: + • Produced Water unit capacity based on produced water flow rate of approximately 7 m3/h (Note 2) and a design margin for processing oily water from open drains system and water draw-off from Condensate Stabilisation Column. + • Produced Water flow rate of 7 m3/h is pro-rated from the Rich Winter Heat and Material Balance to correspond to 7,000 SBPD condensate product from FACILITY. + • The Produced Water Treatment Package typically includes a CPI Separator and an Oil Transfer Pump. Space and tie-ins shall be provided within the package for future installation of secondary treatment, e.g. Induced Gas Flotation (IGF) Unit. +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 20 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 450 FUEL GAS +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +FUEL GAS +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +450-EH-2401 +Fuel Gas Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0014 +Electrical + + +80 + +FV / 35 +30 +-25 / 85 +30 - 70 + +0.8 +CS + 3 mm CA Element: CS + + + + +002 +450-KO-2402 +HP Fuel Gas KO Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0014 + +0.12 MSCMD + + + +FV / 35 +30 +-25 / 85 +30 - 70 +0.8 ID x 2.4 T/T +2.5 +CS + 3 mm CA 316L SS internals +ASME VIII Div 1 +Yes + + +002 +450-F-2403A/B +Fuel Gas Filter / Separator +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0014 + +0.12 MSCMD + + + +FV / 35 +30 +-25 / 85 +30 - 70 +0.6 x 1.5 +1.0 +CS + 3 mm CA 316L SS internals +ASME VIII Div 1 +Yes + + +002 +450-KO-2404 +LP Fuel Gas KO Drum +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0014 + +0.06 MSCMD + + + +FV / 10 +5 +-25 / 85 +30 - 70 +0.8 ID x 2.4 T/T +2.0 +CS + 3 mm CA 316L SS internals +ASME VIII Div 1 +Yes + + +002 +NOTE: + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 21 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 460 METHANOL INJECTION +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +METHANOL INJECTION +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +460-P-2901 +Methanol Offloading Pump +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0006 +Centrifugal +10 m3/h @ 3 bar dp +2.8 +4.0 + +7 +3 +-5 / 85 +50 + +1.0 +Case: CS, Impeller: CS API 610 Material Designation S-4 +API 610 + + + + +460-TK-2902 +Methanol Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0006 + +150 m3 + + + +0.05 +ATM +-5 / 85 +50 +6.0 ID x 7.2 H +18.0 +CS + 3mm CA with no- metallic internal lining +API 650 + +Yes +Note 1 + +460-P-2903A/B +Methanol Distribution Pumps For FACILITY +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0006 +Metering Pump +0.82 m3/h @ 58 bar +dp +3 +5.0 + +65 +58 +-5 / 85 +50 + +1.0 +Case: CS, Impeller: CS API 610 Material Designation S-4 +API 675 + + +HOLD 3 - Capacity + +460-P-2904A/B +Methanol Distribution Pumps For Wellheads +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0006 +Diaphragm Pump +3.1 m3/h @ 320 bar +dp +49 +60 + +383 +320 +-5 / 85 +50 + +1.0 +Case: CS, Impeller: CS API 610 Material Designation S-4 +API 675 + + +Note 1 + +NOTE: + • Methanol tank capacity based on one week's capacity considering an ambient soil temperature of 16°C. Wellhead distribution pump capacity based on maximum requirement considering an ambient soil temperature of 10°C. + • Methanol supply and unloading requirements to be confirmed in EPC phase. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 22 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 470 CHLORINATION +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +CHLORINATION +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +470-PK-2601 +Chlorination Unit +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0015 +Note 1 +Note 2 + + + +12 +8 +-5 / 85 +50 +2.0 x 2.0 x 3.0 +2.0 +Titanium / CPVC + + + +Vendor to confirm material selection + +NOTE: + • The Chlorination Package comprises chemical tank with mixer and dosing pumps. + • Chlorination unit capacity suitable for treatment of 30 m3/h Aquifer Water. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 23 of 35 + + +EQUIPMENT LIST DETAILS +SYSTEM CODE: 480 +UTILITY MISCELLANEOUS + + +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY + + +Client +COMPANY +Checked By +JL + + +Location +UTILITY MISCELLANEOUS +Approved By +CF + + +Job No. +PROJECT NUMBER +Revision +002 + + +Doc No. +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001 +Date +DATE + + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +480-TK-0801 +Amine Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0001 + +29 m3 + + + +0.05 +ATM +-5 / 85 +50 +3.35 m OD x 4.6 m H +4.5 +CS • 3mm CA with non- metallic internal lining. System 5 up to 1m from tank bottom +API 12F + + + + +480-A-0802 +Amine Storage Tank Mixer +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0001 + + + +5.5 + + + + + + +0.2 +316L SS + + + + + +480-P-0803A/B +Amine Transfer Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0001 +Centrifugal +5 m3/h @ 6 bar dp +2.1 +3.0 + +10 +6 +-5 / 85 +50 + +1.0 +Case: 316L SS, Impeller: 316L SS API 610 material designation S-9 +API 610 + + + + +480-EH-0804 +Amine Storage Tank Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0001 +Electric + + +10.0 + + + + + + + +Element: 316L SS + + + + + +480-V-0805 +Amine Drain Vessel +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0001 +Horizontal Vessel +15 m3 + + + +FV / 3.5 +0.01 +-5 / 85 +50 +2.2 x 6.0 (ID x TT) +5.5 +CS • 3 mm CA External lining and CP +ASME VIII Div 1 + + + + +480-P-0806A/B +Amine Drain Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0001 +Sump Pump +5 m3/h @ 5 bar dp +1.7 +3.0 + +10 +5 +-5 / 85 +50 + +0.7 +Case: 316L SS, Impeller: 316L SS API 610 material designation S-9 +API 610 + + + + +480-EH-0807 +Amine Drain Vessel Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0001 +Electric + + +5.0 + +3.5 + + + + +0.2 +Element: 316L SS + + + + + +480-TK-0808 +Glycol Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0002 + +15 m3 + + + +0.05 +ATM +-5 / 85 +50 +2.93 OD x 3.65 H +3.5 +CS • 3mm CA +API 12F + + + + +480-P-0809A/B +Glycol Transfer Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0002 +Centrifugal +3 m3/h @ 6 bar dp +1.2 +3.0 + +11 +6 +-5 / 85 +50 + +1 +Case: 316L SS, Impeller: 316L SS API 610 material designation S-9 +API 610 + + + + +480-EH-0810 +Glycol Storage Tank Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0002 +Electric + + +10.0 + + + + + + +0.2 +Element: 316L SS + + + + + +480-V-0811 +Glycol Drain Vessel +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0002 +Horizontal Vessel +10 m3 + + + +FV / 3.5 +0.01 +-5 / 85 +50 +1.9 x 6.0 (ID x TT) +4.5 +CS • 3 mm CA External lining and CP +ASME VIII Div 1 + + + + +480-P-0812A/B +Glycol Drain Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0002 +Centrifugal +5 m3/h @ 5 bar dp +1.7 +3.0 + +11 +5 +-5 / 85 +50 + +1.00 +Case: 316L SS, Impeller: 316L SS API 610 material designation S-9 +API 610 + + + + +480-EH-0813 +Glycol Drain Vessel Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0002 +Electric + + +5.0 + +3.5 + + + + +0.20 +Element: 316L SS + + + + + +NOTE: + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 24 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 500 +AQUIFER / PLANT WATER +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +AQUIFER / PLANT WATER +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +500-F-2601A/B +Aquifer Water Filter +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0015 + +30 m3/h + + + +12 +8 +-5 / 85 +50 + +0.5 +CS + Rubber lining +ASME VIII Div 1 + + + + +500-TK-2602A/B +Plant Water Storage Tank +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0015 +Note 1 +115 m3 + + + +ATM +ATM +-5 / 85 +50 +4.7 OD x 7.3 H +10 +CS + 3mm CA with non-metallic lining +API 12F + + + + +500-P-2603A/B +Plant Water Transfer Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0015 + +32 m3/h @ 8.5 bar +dp +15.0 +20.0 + +12 +8 +-5 / 85 +50 + +1.0 +Case: 316L SS, Impeller: 316L SS +ISO 5199 / ASME B73.1 + + + + +NOTE: +1. Plant water capacity quoted is nominal capacity corresponding to two days storage of normal requirement of plant water (includes requirements for production of fresh water and demin water) and make-up for fire water. See also Note 1 on Page 26. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 25 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 530 +FRESH/POTABLE/DEMINERALISED WATER +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +FRESH/POTABLE/DEMINERALISED WATER +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +530-PK-3101 +Fresh Water RO Treatment Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0015 + +1.75 m3/h + +5.0 + +8 +5.0 +-5 / 85 +50 + +5.0 +316L SS (Vendor Specified) + + + +HOLD 4 + +530-TK-3102A/B +Fresh Water Storage Tank +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0015 + +50 m3 each + + + +ATM +ATM +-5 / 85 +50 +3.66 OD x 6.1 H +7.0 +CS + 3mm CA with non-metallic lining +API 12F + + + + +530-P-3103A/B +Fresh Water Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0015 + +35 m3/h @ 5.5 bar +dp +10.5 +15.0 + +8 +5.7 +-5 / 85 +50 + +1.0 +Case: 316L SS, Impeller: 316L SS +ISO 5199 / ASME B73.1 + + + + +530-PK-3104 +Demin Water Polishing Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0015 + +0.5 m3/h + +5.0 + +8 +3 +-5 / 85 +50 + +1.0 +Vendor specified +ISO 5199 / ASME B73.1 + + +HOLD 4 + +530-TK-3105 +Demin Water Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0015 + +45 m3 + + + +0.05 +ATM +-5 / 85 +50 +3.66 OD x 6.1 H +7.0 +CS + 3mm CA with non-metallic lining +API 12F + + + + +530-P-3106A/B +Demin Water Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0015 + +1.6 m3/h @ 4.7 bar +dp +1.0 +2.2 + +8 +4.7 +-5 / 85 +50 + +1.0 +Case: 316L SS, Impeller: 316L SS +ISO 5199 / ASME B73.1 + + + + +530-PK-3107 +Chemical Dosing Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0015 +Note 3 + + + + +8 +5 +-5 / 85 +50 + +12.0 +316L SS (Vendor Specified) +API 675 + + +Note 2 + +NOTE: + • Fresh water and demin water storage capacity quoted is required working volume corresponding to seven days storage of their normal requirements. + • Chemicals for water treatment typically are biocide and regeneration chemicals for Demineralisation Unit. + • Chemical Dosing Package comprises (HOLD 4): - Multi-compartmented Tank (3 compartments) - Dosing Pump (3 operating + 3 standby) - Mixers (3; one per compartment) + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 26 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 560 OPEN DRAINS +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +OPEN DRAINS +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +560-PIT-3001 +Open Drains Sump +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0005 + +NOTE 1 + + + +ATM +ATM +-5 / 85 +50 +4.0 x 4.0 x 2.5 + +Lined Concrete + + + + + +560-P-3002A/B +Open Drains Sump Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0005 + +2 m3/h @ 3.0 bar dp +0.5 +1.1 + +8.0 +3.0 +-5 / 85 +50 + +1.0 +Case: UNS S31803 Impeller: UNS S3183 API 610 Material Designation D1 + + + + + +560-A-3003 +Open Drain Oil Skimmer Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0005 + + + + + + + + + + + +Vendor Specified + + + +Located within Open Drains Sump + +NOTE: +1. The Open Drains Sump is designed to handle a maximum drainage volume of 5 m3 in one day at a maximum rate of 15 m3/h. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 27 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 570 CLOSED DRAINS +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +CLOSED DRAINS +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +570-V-2101 +Closed Drains Vessel +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0005 + +32 m3 + + + +3.5 +0.1 +-5 / 85 +50 +2.7 ID x 8.5 T/T +8.7 +Shell: CS + 3mm CA with non-metallic lining, external coating and CP +ASME VIII Div 1 +Internal Lining +(Epoxy +Coating) Located in a Concrete Pit + + + +570-P-2102A/B +Closed Drains Transfer Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0005 +Centrifugal +5 m3/h @ 22 bar dp +10.0 +14.0 + +31 +22.0 +-5 / 85 +50 + +0.6 +Case: UNS S31803 Impeller: UNS S3183 API 610 Material Designation D1 +API 610 + + + + +570-EH-2103 +Closed Drains Vessel Heater +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0005 +Electric + + +40 + +3.5 + +-5 / 85 + + + +Element: 316L SS + + + + + +NOTE: + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 28 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 620 DIESEL OIL +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +DIESEL OIL +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +620-TK-2001 +Diesel Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0011 + +60 m3 + + + +0.05 +ATM +-5 / 85 +50 +4.7 OD x 4.9 H +9.0 +CS + 3mm CA with non-metallic internal lining at bottom of tank +API 12F + + +Capacity based on 3 days storage at maximum diesel consumption. + +620-P-2002A/B +Diesel Transfer Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0011 + +5 m3/h @ 2.5 bar dp +1.0 +2.2 + +7 +4.0 +-5 / 85 +50 + +1.0 +Case: CS Impeller: Cast iron API 610 material designation S-1 +API 610 + + + + +620-FC-2003 +Diesel Filter +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0011 + +5 m3/h + + + +7 +4.0 +-5 / 85 +50 + +1.0 +CS + 3mm CA with non-metallic internal lining + + + + + +NOTE: + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 29 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 630 +INSTRUMENT AND PLANT AIR +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +INSTRUMENT AND PLANT AIR +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + + + + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°CMaterials +Size +L x W x H +m +Weight (each) +tonnes + +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +630-K-2201A/B +Air Compressor +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0012 +Oil-free Screw type +760 Sm3/h +111 +132 + +12 +10.0 +-5 / 85 +50 + +4 +CS (Vendor specified) +API 619 / ISO10440 + + +Note 1, HOLD 4 +002 +630-VL-2202 +Plant Air Receiver +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0012 + +15 m3 + + + +12 +10.0 +-5 / 85 +50 +1.8 ID x 5.4 T/T +6 +Shell: CS + 3mm CA with non-metallic internal lining +ASME VIII Div 1 + + + +002 +630-PK-2203A/B +Air Drier Package +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0012 +Each comprises two towers. +660 Sm3/h + + + +12 +9.5 +-5 / 85 +50 + +5 +Vendor Specified +ASME VIII Div 1 + + +Capacity quoted is instrument (dry) air flow rate. Note 1, HOLD 4 +002 +630-VL-2204 +Instrument Air Receiver +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0012 + +40 m3 + + + +12 +9.0 +-5 / 85 +50 +2.5 ID x 7.3 T/T +11 +Shell: CS + 3mm CA with non-metallic internal lining +ASME VIII Div 1 + + + +002 +NOTE: +1. The maximum instrument air demand is 660 Sm3/hr. The Air Compressor Capacity includes an additional 15% margin which has been assumed for drier bed regeneration, giving a total air compressor capacity of 760 Sm3/hr. Supplier to confirm the regeneration air flow requirement. +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 30 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 640 INERT GAS +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +INERT GAS +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +640-PK-2701 +Nitrogen Generator Package +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0013 +Note 1 +90 Sm3/h + +5 + +12 +9 +-5 / 85 +50 + +1.0 +Vendor specified + + + +HOLD 4 +002 +640-RCV-2702 +Nitrogen Receiver +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0013 +Vertical Process Vessel +38 m3 + + + +12 +7 +-5 / 85 +50 +2.5 x 7.0 (ID x TT) +11.0 +Shell: CS + 3mm CA with 316L SS internals +ASME VIII DIV I + + + +002 +640-PK-2703 +High Purity Nitrogen Bottles Skid +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0013 + +20 Sm3/h + + + + + + + + + +Vendor Specified + + + +HOLD 4 + +NOTE: +1. Nitrogen Generation Package typically includes: +- Inlet Carbon Filters (2x100%) +- Inlet Heater (1x100%) +- Membrane Separators (with 20% spare membranes) +002 +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 31 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 660 SEWAGE TREATMENT +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +SEWAGE TREATMENT +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure +barg(each) +Temperature. +°C +Size +L x W x H +m +Weight tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +660-PK-3201 A/B/C/D +Sewage Sump Package +4 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0016 +Septic Tank +5.0 m3 + + + +Vendor Specified +Vendor Specified +-5 / 85 +50 + +1.0 +Vendor Specified + + + +HOLD 3 - quantity, capacity +002 +NOTE: +1. Equipment Tag 660-PK-3202 (Waste Water Treatment Package) has been deleted in Rev 002 of this document. +002 + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 32 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 710 FIRE WATER +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +FIRE WATER +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +710-TK-2301 +Firewater Storage Tank +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0007 +Note 1 +2836 m3 + + + +ATM +ATM +-5 / 85 +50 +18 ID x 14.4 H +120 +CS + 3mm CA with non-metallic internal lining +API 650 + + + +002 +710-P-2302 +Electric Firewater Pump +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0007 + +350 m3/h ® 10 bar +dp +130 +150 + +16 +10.0 +-5 / 85 +50 + +6 +Case: Ni Al Bz Impeller: Ni Al Bz +NFPA 20 + + +3 x 50% Firewater pump configuration +002 +710-PK-2303A/B +Diesel Firewater Pump Package +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0007 +Note 4 +350 m3/h ® 10 bar +dp +130 +150 + +16 +10.0 +-5 / 85 +50 + +15.0 +Vendor Specified +NFPA 20 + + +3 x 50% Firewater pump configuration +002 +710-P-2305A/B +Fire Water Jockey Pump +2 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0007 + +10 m3/h ® 10 bar dp +8.0 +11 + +16 +10.0 +-5 / 85 +50 + +2.0 +Case: Ni Al Bz Impeller: Ni Al Bz +NFPA 20 + + +2 x 100% electric pumps +002 +710-A-2306 +Foam Package (Condensate Storage Area) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0007 +Note 2 +21 m3/h ® 11 bar dp +11 +15 + +16 +11.0 +-5 / 85 +50 + +2.0 +CS + 3mm CA with non-metallic internal lining +NFPA 20 API 650 + + +HOLD 4 + +710-A-2307 +Foam Package (Methanol Storage Area) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0007 +Note 3 + + + + + + + + + + +CS + 3mm CA with non-metallic internal lining +NFPA 20 API 650 + + +HOLD 4 + +710-A-2310 +Deluge Skid +(Methanol Tank) (460-TK-2902) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0007 + + + + + + + + + + + + +NFPA 15 + + +HOLD 4 + +710-A-2311 +Deluge Skid +(Condensate Tank) (330-TK-1501) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0007 + + + + + + + + + + + + +NFPA 15 + + +HOLD 4 + +710-A-2312 +Deluge Skid +(Condensate Tank) (330-TK-1502) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR-UFD-0007 + + + + + + + + + + + + +NFPA 15 + + +HOLD 4 + +710-A-2313 +Deluge Skid +(Offspec Condensate Tank) (330-TK-1503) +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0007 + + + + + + + + + + + + +NFPA 15 + + +HOLD 4 + +NOTE: + • Fire Water Unit designed to cater for only the Condensate Storage Tanks. Accordingly, there will be no ring main and jockey pumps + • Foam Package (Condensate Storage Area) contains Foam Pump and Foam Storage Tank (20m3). + • Foam Package (Methanol Storage Area) contains Foam Storage Tank (1.35 m3). + • Diesel Firewater Pump Package also contain diesel tank storage of 1 m3. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 33 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 800 +MAIN POWER GENERATION +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +MAIN POWER GENERATION +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +800-GT-2001 +Gas Turbine Generator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0010 +Note 1 + +4.0 MW + + + + + + +12 x 5.8 x 8.1 +60 +Vendor Standard +API STD 616 + + +Typically 6.75 MW ISO rated gas turbine (SGT-200) + +800-GT-2002 +Gas Turbine Generator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0010 +Note 1 + +4.0 MW + + + + + + + +60 +Vendor Standard +API STD 616 + + +Typically 6.75 MW ISO rated gas turbine (SGT-200) + +NOTE: + • 2 x 100% duty Gas Turbine Generator Packages, each for 4 MW power output. + • Overall package dimensions for 2 machines. GTG skid alone is significantly smaller. + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 34 of 35 + +EQUIPMENT LIST DETAILS SYSTEM CODE: 830 +EMERGENCY POWER GENERATORS +Project +PROJECT1 DEVELOPMENT PROJECT +Originated By +LY +Client +COMPANY +Checked By +JL +Location +EMERGENCY POWER GENERATORS +Approved By +CF +Job No. +PROJECT NUMBER +Revision +002 +Doc No. +Project1-FEED CONTRACTOR--MUL-E000-PR-LST-0001 +Date +DATE + +Tag Number +Equipment Name +QTY +PFD No. +Package Notes +Capacity +Duty kW +Instal. Power +kW +Surface Area (m2) +Pressure barg +Temperature. +°C +Size +L x W x H +m +Weight (each) +tonnes +Materials +Design Code +Internals +Insulation +Remarks +Rev Flag + + + + + + + + + +Design +Oper +Design +Oper + + + + + + + + +830-GD-2001 +Emergency / Standby Diesel Generator +1 +PROJECT1-FEED CONTRACTOR-PF01-E000-PR- UFD-0010 + + +750 + + + + + + +7.5 x 4.0 x4.0 +30 +CS (Vendor Standard) +IEC-60034 + + + + +NOTE: + + + +PROJECT1-FEED CONTRACTOR-MUL-E000-PR-LST-0001_Rev002 - Page 35 of 35 diff --git a/launch.py b/launch.py new file mode 100644 index 0000000..255ec0c --- /dev/null +++ b/launch.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +""" +Launch script for Mini SpecsComply Pro. +This script sets up and runs the application in one step. +""" + +import os +import sys +import subprocess +import argparse +from pathlib import Path + +def run_command(command, check=True): + """Run a shell command.""" + print(f"Running: {' '.join(command)}") + return subprocess.run(command, check=check) + +def check_env_file(): + """Check if .env file exists and has required keys.""" + if not os.path.exists(".env"): + print("Error: .env file not found. Running setup...") + run_command([sys.executable, "setup.py"]) + return False + + required_keys = [ + "COHERE_API_KEY", + "GROQ_API_KEY", + "PINECONE_API_KEY" + ] + + with open(".env", "r") as f: + env_content = f.read() + + missing_keys = [] + for key in required_keys: + if f"{key}=" not in env_content or f"{key}=your_{key.lower()}" in env_content: + missing_keys.append(key) + + if missing_keys: + print(f"Error: The following API keys are missing or not set in .env: {', '.join(missing_keys)}") + print("Please edit the .env file to add your API keys.") + return False + + return True + +def main(): + """Main function.""" + parser = argparse.ArgumentParser(description="Launch Mini SpecsComply Pro") + parser.add_argument("--host", default="0.0.0.0", help="Host to bind") + parser.add_argument("--port", type=int, default=8000, help="Port to bind") + parser.add_argument("--debug", action="store_true", help="Enable debug mode") + parser.add_argument("--setup", action="store_true", help="Run setup before launching") + args = parser.parse_args() + + # Run setup if requested + + + # Check environment file + if not check_env_file(): + return 1 + + # Run the application + print(f"Launching Mini SpecsComply Pro on {args.host}:{args.port}...") + cmd = [ + sys.executable, "run.py", + "--host", args.host, + "--port", str(args.port) + ] + + if args.debug: + cmd.append("--debug") + + try: + run_command(cmd) + except KeyboardInterrupt: + print("\nApplication stopped by user.") + except Exception as e: + print(f"Error running application: {str(e)}") + return 1 + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fb49618 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,57 @@ +annotated-types==0.7.0 +anthropic==0.49.0 +anyio==4.9.0 +Authlib==1.3.1 +certifi==2025.1.31 +cffi==1.17.1 +charset-normalizer==3.4.1 +click==8.1.8 +cohere==5.15.0 +cryptography==44.0.2 +distro==1.9.0 +fastapi==0.115.12 +fastavro==1.10.0 +filelock==3.18.0 +fsspec==2025.3.2 +groq==0.22.0 +grpcio==1.71.0 +grpcio-health-checking==1.71.0 +grpcio-tools==1.71.0 +h11==0.14.0 +httpcore==1.0.8 +httpx==0.28.1 +httpx-sse==0.4.0 +huggingface-hub==0.30.2 +idna==3.10 +Jinja2==3.1.6 +jiter==0.9.0 +loguru==0.7.3 +MarkupSafe==3.0.2 +openai==1.75.0 +packaging==24.2 +pinecone==6.0.2 +pinecone-plugin-interface==0.0.7 +protobuf==5.29.4 +pycparser==2.22 +pydantic==2.11.3 +pydantic-settings==2.8.1 +pydantic_core==2.33.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.1.0 +python-multipart==0.0.20 +PyYAML==6.0.2 +requests==2.32.3 +setuptools==78.1.0 +six==1.17.0 +sniffio==1.3.1 +starlette==0.46.2 +tenacity==9.1.2 +tokenizers==0.21.1 +tqdm==4.67.1 +types-requests==2.32.0.20250328 +typing-inspection==0.4.0 +typing_extensions==4.13.2 +urllib3==2.4.0 +uvicorn==0.34.1 +validators==0.34.0 +weaviate-client==4.13.2 diff --git a/standard/industrial_equipment_standards.json b/standard/industrial_equipment_standards.json new file mode 100644 index 0000000..c6bb1eb --- /dev/null +++ b/standard/industrial_equipment_standards.json @@ -0,0 +1,152 @@ +{ + "standards": [ + { + "id": "std-101", + "name": "Industrial Equipment Safety Standards", + "description": "Core safety requirements for industrial equipment and machinery", + "requirements": [ + { + "id": "req-101-01", + "description": "All equipment must have emergency stop mechanisms within operator reach", + "severity": "critical", + "details": "Emergency stops must be red in color, clearly marked, and accessible from all operating positions" + }, + { + "id": "req-101-02", + "description": "Moving parts must be guarded with appropriate safety barriers", + "severity": "critical", + "details": "Guards must be secured with tamper-resistant fasteners and interlocked where applicable" + }, + { + "id": "req-101-03", + "description": "Equipment must display appropriate warning labels and safety instructions", + "severity": "major", + "details": "Labels must be in local language and use standardized safety symbols" + }, + { + "id": "req-101-04", + "description": "Operating manual must include maintenance schedules and procedures", + "severity": "major", + "details": "Include daily, weekly, monthly, and annual maintenance requirements" + }, + { + "id": "req-101-05", + "description": "Equipment noise levels must not exceed 85dB during normal operation", + "severity": "major", + "details": "Measured at operator position under full load conditions" + } + ] + }, + { + "id": "std-102", + "name": "Quality Control Documentation", + "description": "Documentation requirements for quality control processes", + "requirements": [ + { + "id": "req-102-01", + "description": "Each equipment must have a unique serial number and QC tracking code", + "severity": "critical", + "details": "Serial numbers must be permanently marked and recorded in production database" + }, + { + "id": "req-102-02", + "description": "Test results must be documented for each critical component", + "severity": "major", + "details": "Include test parameters, results, and operator identification" + }, + { + "id": "req-102-03", + "description": "Calibration records must be maintained for all measuring equipment", + "severity": "major", + "details": "Include calibration date, due date, and calibration certificate reference" + }, + { + "id": "req-102-04", + "description": "Non-conformance reports must be filed for any quality deviations", + "severity": "major", + "details": "Document root cause analysis and corrective actions taken" + }, + { + "id": "req-102-05", + "description": "Quality control procedures must be reviewed annually", + "severity": "minor", + "details": "Update procedures based on audit findings and process improvements" + } + ] + }, + { + "id": "std-103", + "name": "Environmental Compliance", + "description": "Environmental protection and sustainability requirements", + "requirements": [ + { + "id": "req-103-01", + "description": "Equipment must meet current energy efficiency standards", + "severity": "major", + "details": "Comply with ISO 50001 energy management standards" + }, + { + "id": "req-103-02", + "description": "Hazardous materials must be properly labeled and documented", + "severity": "critical", + "details": "Include MSDS and handling procedures for all hazardous materials" + }, + { + "id": "req-103-03", + "description": "Waste disposal procedures must be documented and followed", + "severity": "major", + "details": "Specify appropriate disposal methods for different waste categories" + }, + { + "id": "req-103-04", + "description": "Equipment must not contain prohibited substances", + "severity": "critical", + "details": "Comply with RoHS and REACH regulations" + }, + { + "id": "req-103-05", + "description": "Environmental impact assessment must be conducted", + "severity": "major", + "details": "Include carbon footprint and resource consumption analysis" + } + ] + }, + { + "id": "std-104", + "name": "Technical Documentation Requirements", + "description": "Standards for technical documentation and manuals", + "requirements": [ + { + "id": "req-104-01", + "description": "All technical documents must be version controlled", + "severity": "major", + "details": "Include revision history and change log" + }, + { + "id": "req-104-02", + "description": "Documentation must be available in specified languages", + "severity": "major", + "details": "Minimum requirement: English, Spanish, and Mandarin" + }, + { + "id": "req-104-03", + "description": "Technical specifications must include dimensional drawings", + "severity": "major", + "details": "Provide both metric and imperial measurements" + }, + { + "id": "req-104-04", + "description": "Troubleshooting guides must include decision trees", + "severity": "minor", + "details": "Cover common issues and resolution steps" + }, + { + "id": "req-104-05", + "description": "Electronic documentation must be searchable", + "severity": "minor", + "details": "Provide PDF with OCR and proper bookmarking" + } + ] + } + ] +} \ No newline at end of file diff --git a/standard/technical_documentation.json b/standard/technical_documentation.json new file mode 100644 index 0000000..24af760 --- /dev/null +++ b/standard/technical_documentation.json @@ -0,0 +1,132 @@ +{ + "standards": [ + { + "id": "std-001", + "name": "Technical Documentation Structure", + "description": "Standard for the structure of technical documentation", + "requirements": [ + { + "id": "req-001-01", + "description": "Document must include a title and version number", + "severity": "critical" + }, + { + "id": "req-001-02", + "description": "Document must include a table of contents for documents longer than 10 pages", + "severity": "major" + }, + { + "id": "req-001-03", + "description": "Each major section must start on a new page", + "severity": "minor" + }, + { + "id": "req-001-04", + "description": "Document must include an introduction section", + "severity": "major" + }, + { + "id": "req-001-05", + "description": "Document must include a conclusion or summary section", + "severity": "major" + } + ] + }, + { + "id": "std-002", + "name": "Technical Content Requirements", + "description": "Standard for technical content quality and completeness", + "requirements": [ + { + "id": "req-002-01", + "description": "All technical terms must be defined either in-text or in a glossary", + "severity": "major" + }, + { + "id": "req-002-02", + "description": "Code examples must include comments explaining their functionality", + "severity": "major" + }, + { + "id": "req-002-03", + "description": "Diagrams must include captions and labels", + "severity": "major" + }, + { + "id": "req-002-04", + "description": "All claims must be supported by evidence or references", + "severity": "critical" + }, + { + "id": "req-002-05", + "description": "Document must not contain placeholder or lorem ipsum text", + "severity": "critical" + } + ] + }, + { + "id": "std-003", + "name": "Writing Style and Formatting", + "description": "Standard for consistent writing style and formatting", + "requirements": [ + { + "id": "req-003-01", + "description": "Document must use consistent formatting for headings, lists, and code blocks", + "severity": "minor" + }, + { + "id": "req-003-02", + "description": "Sentences should be concise (preferably under 25 words)", + "severity": "minor" + }, + { + "id": "req-003-03", + "description": "Active voice should be used where possible", + "severity": "minor" + }, + { + "id": "req-003-04", + "description": "Spelling and grammar must be correct throughout the document", + "severity": "major" + }, + { + "id": "req-003-05", + "description": "Acronyms must be defined on first use", + "severity": "major" + } + ] + }, + { + "id": "std-004", + "name": "Technical Accuracy", + "description": "Standard for ensuring technical accuracy", + "requirements": [ + { + "id": "req-004-01", + "description": "All technical specifications must be accurate and up-to-date", + "severity": "critical" + }, + { + "id": "req-004-02", + "description": "Code examples must be tested and functional", + "severity": "critical" + }, + { + "id": "req-004-03", + "description": "API endpoints must include all required parameters and response formats", + "severity": "critical" + }, + { + "id": "req-004-04", + "description": "Error cases and edge conditions must be documented", + "severity": "major" + }, + { + "id": "req-004-05", + "description": "Version compatibility information must be provided for all dependencies", + "severity": "major" + } + ] + } + ] + } \ No newline at end of file