Environment methods

This commit is contained in:
kcar 2025-08-23 19:01:36 +01:00
parent cba9d1e341
commit 2a85845835
146 changed files with 370 additions and 383 deletions

18
.gitignore vendored
View File

@ -1,10 +1,10 @@
.env
.DS_Store
.vscode
.idea
__pycache__
.pytest_cache
.coverage
.coverage.*
.coverage.*.*
.coverage.*.*.*
.coverage.*.*.*.*
.DS_Store
.env
data/init_data/
logs/

View File

@ -1,38 +1,34 @@
# Example Dockerfile showing how to use the new startup system
# This is a reference - not meant to replace your existing Dockerfile
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
libreoffice \
poppler-utils \
libpq-dev \
gcc \
python3-dev \
postgresql-client \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set up virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Python packages
# Copy requirements and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy the application
# Copy application code
COPY . .
# Create necessary directories
RUN mkdir -p static templates/admin logs
# Make startup script executable
RUN chmod +x start.sh
# Create entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Set environment variables
ENV PYTHONPATH=/app
ENV UVICORN_PORT=8000
ENV UVICORN_WORKERS=4
EXPOSE ${PORT_BACKEND}
# Expose port
EXPOSE 8000
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["python", "main.py"]
# Use the new startup script in production mode
CMD ["./start.sh", "prod"]
# Alternative: Run initialization first, then production server
# CMD ["sh", "-c", "./start.sh init && ./start.sh prod"]
# Alternative: Use Python directly
# CMD ["python3", "main.py", "--mode", "prod"]

View File

@ -1,38 +0,0 @@
FROM python:3.11-slim
WORKDIR /app/backend
# Install system dependencies
RUN apt-get update && apt-get install -y \
libreoffice \
poppler-utils \
libpq-dev \
gcc \
python3-dev \
postgresql-client \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set up virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Python packages
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Copy the application
COPY . .
# Create necessary directories
RUN mkdir -p static templates/admin logs
# Create entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
EXPOSE ${PORT_BACKEND}
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["python", "main.py"]

View File

@ -1,38 +0,0 @@
FROM python:3.11-slim
WORKDIR /app/backend
# Install system dependencies
RUN apt-get update && apt-get install -y \
libreoffice \
poppler-utils \
libpq-dev \
gcc \
python3-dev \
postgresql-client \
curl \
&& rm -rf /var/lib/apt/lists/*
# Set up virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install Python packages
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Copy the application
COPY . .
# Create necessary directories
RUN mkdir -p static templates/admin logs
# Create entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
EXPOSE ${PORT_BACKEND}
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["python", "main.py"]

108
README.md
View File

@ -1,108 +0,0 @@
The main issues are with system-level dependencies like `libreoffice` and `gcc` which aren't typically managed through conda on macOS.
```yaml:backend/requirements.conda.yml
name: classroomcopilot-backend
channels:
- conda-forge
- nodefaults
dependencies:
- python=3.11
# Core dependencies
- pip
- setuptools
- wheel
# Server dependencies
- fastapi
- uvicorn
- python-dotenv
- python-multipart
- python-jose
- pyjwt
- jinja2
# Database and Auth
- sqlalchemy
- sqlalchemy-utils
- asyncpg
- redis-py
# Neo4j
- neo4j-python-driver
# Data Processing
- pandas
- scipy
- requests
- openpyxl
- scikit-learn
# Testing
- pytest
- pytest-html
# Database clients
- postgresql
- libpq
# Additional dependencies via pip
- pip:
# Supabase
- supabase
# Neo4j specific
- neontology
# Document Processing
- python-pptx
- python-docx
- pdfminer.six
- Pillow
- psutil
# Web Scraping and Processing
- emoji
- extruct
- w3lib
# Google APIs
- youtube-transcript-api
- google-api-python-client
- google-auth-oauthlib
# LangChain Ecosystem
- "langchain[llms]"
- langchain-community
- langchain-openai
- langgraph
# OpenAI
- openai
- ollama
# Microsoft Authentication
- msal
```
I've made several important changes to make the conda environment more compatible:
1. Removed system-level dependencies that should be installed via the system package manager:
- `libreoffice` - Install via Homebrew on macOS (`brew install libreoffice`)
- `gcc` - Install via Xcode Command Line Tools on macOS
- `poppler` - Install via Homebrew on macOS (`brew install poppler`)
2. Changed channel configuration:
- Using only `conda-forge` with `nodefaults` to avoid package conflicts
- This provides better consistency across platforms
3. Fixed the `langchain[llms]` specification by adding quotes to handle the square brackets properly
For macOS users, here are the steps to set up the environment:
1. First, install system dependencies:
```bash
# Install Xcode Command Line Tools (if not already installed)
xcode-select --install
# Install other system dependencies via Homebrew
brew install libreoffice poppler postgresql
```
2. Then create the conda environment:
```bash
conda env create -f backend/requirements.conda.yml
```
3. Activate the environment:
```bash
conda activate classroomcopilot-backend
```
This setup should now work properly on macOS while still maintaining compatibility with the Docker environment for production. The system-level dependencies are handled by the appropriate system package manager (Homebrew for macOS) rather than conda, which is the recommended approach for these types of dependencies.
Would you like me to make any additional adjustments to the environment configuration?

Binary file not shown.

View File

@ -1,12 +1,14 @@
services:
backend:
container_name: ccapi
container_name: cc-api
build:
context: .
dockerfile: Dockerfile
env_file: .env
env_file:
- .env
- .env.app.prod
ports:
- "${PORT_BACKEND}:${PORT_BACKEND}"
- 8000:8000
networks:
- kevlarai-network

View File

@ -1,43 +0,0 @@
#!/bin/bash
set -e
# Add backend to Python path
export PYTHONPATH="/app:${PYTHONPATH}"
# Create init directories
mkdir -p /init
# Function to check if initialization is needed
check_init_needed() {
if [ ! -f "/init/status.json" ]; then
return 0
fi
# Check if any status is incomplete
incomplete=$(python -c "
import json
try:
with open('/init/status.json', 'r') as f:
status = json.load(f)
print(not all(v for k, v in status.items() if k != 'timestamp'))
except (FileNotFoundError, json.JSONDecodeError):
print('True')
")
if [ "$incomplete" = "True" ]; then
return 0
else
return 1
fi
}
# Run initialization if needed
if check_init_needed; then
echo "Running initialization..."
python -c "from run.initialization import initialize_system; initialize_system()"
else
echo "System already initialized, skipping..."
fi
# Execute the main command
exec "$@"

118
main.py
View File

@ -1,4 +1,6 @@
import os
import argparse
import sys
from modules.logger_tool import initialise_logger
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
from fastapi import FastAPI, HTTPException
@ -100,34 +102,94 @@ def initialize_with_retry(max_attempts: int = 3, initial_delay: int = 5) -> bool
return False
if __name__ == "__main__":
import uvicorn
import os
def run_initialization_mode():
"""Run only the initialization process"""
logger.info("Running in initialization mode")
logger.info("Starting system initialization...")
# Run initialization with retry logic
if not initialize_with_retry():
logger.error("Failed to initialize system after multiple attempts")
# Continue anyway to allow the API to start and handle health checks
if os.getenv('BACKEND_DEV_MODE') == 'true':
logger.info("Running with Reload")
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv('PORT_BACKEND', 8000)),
log_level="info",
proxy_headers=True,
timeout_keep_alive=10,
reload=True
)
if initialize_with_retry():
logger.info("Initialization completed successfully")
return True
else:
logger.info("Running without Reload and without SSL (behind reverse proxy)")
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv('PORT_BACKEND', 8000)), # <-- not 443
log_level="info",
proxy_headers=True,
timeout_keep_alive=10,
workers=int(os.getenv('UVICORN_WORKERS', '1'))
logger.error("Initialization failed after multiple attempts")
return False
def run_development_mode():
"""Run the server in development mode with auto-reload"""
logger.info("Running in development mode")
logger.info("Starting uvicorn server with auto-reload...")
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv('UVICORN_PORT', 8000)),
log_level=os.getenv('LOG_LEVEL', 'info'),
proxy_headers=True,
timeout_keep_alive=10,
reload=True
)
def run_production_mode():
"""Run the server in production mode"""
logger.info("Running in production mode")
logger.info("Starting uvicorn server in production mode...")
uvicorn.run(
"main:app",
host="0.0.0.0",
port=int(os.getenv('UVICORN_PORT', 8000)),
log_level=os.getenv('LOG_LEVEL', 'info'),
proxy_headers=True,
timeout_keep_alive=10,
workers=int(os.getenv('UVICORN_WORKERS', '1'))
)
def parse_arguments():
"""Parse command line arguments"""
parser = argparse.ArgumentParser(
description="ClassroomCopilot API Server",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Startup modes:
init - Run initialization scripts (database setup, etc.)
dev - Run development server with auto-reload
prod - Run production server (for Docker/containerized deployment)
"""
)
parser.add_argument(
'--mode', '-m',
choices=['init', 'dev', 'prod'],
default='dev',
help='Startup mode (default: dev)'
)
return parser.parse_args()
if __name__ == "__main__":
args = parse_arguments()
# Set environment variable for backward compatibility
if args.mode == 'dev':
os.environ['BACKEND_DEV_MODE'] = 'true'
else:
os.environ['BACKEND_DEV_MODE'] = 'false'
logger.info(f"Starting ClassroomCopilot API in {args.mode} mode")
if args.mode == 'init':
# Run initialization only
success = run_initialization_mode()
sys.exit(0 if success else 1)
elif args.mode == 'dev':
# Run development server
run_development_mode()
elif args.mode == 'prod':
# Run production server
run_production_mode()
else:
logger.error(f"Invalid mode: {args.mode}")
sys.exit(1)

View File

@ -2,7 +2,7 @@ import os
from typing import Dict, Optional, Any, TypedDict, List
from supabase import create_client, Client
from supabase.lib.client_options import SyncClientOptions
from gotrue import SyncMemoryStorage
from supabase_auth import SyncMemoryStorage
from modules.logger_tool import initialise_logger
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)

View File

@ -2,7 +2,11 @@ import os
import sys
import logging
import datetime
import pytest
try:
import pytest
PYTEST_AVAILABLE = True
except ImportError:
PYTEST_AVAILABLE = False
from dotenv import load_dotenv, find_dotenv
# Load environment variables
@ -86,17 +90,22 @@ class FileFormatter(logging.Formatter):
return logging.Formatter(LOG_FORMAT).format(record)
class PytestFormatter:
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(self, item, call):
outcome = yield
report = outcome.get_result()
if report.when == "call":
if report.passed:
logging.success(f"✅ Test passed: {item.name}")
elif report.failed:
logging.error(f"❌ Test failed: {item.name}")
elif report.skipped:
logging.warning(f"⏭️ Test skipped: {item.name}")
if PYTEST_AVAILABLE:
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(self, item, call):
outcome = yield
report = outcome.get_result()
if report.when == "call":
if report.passed:
logging.success(f"✅ Test passed: {item.name}")
elif report.failed:
logging.error(f"❌ Test failed: {item.name}")
elif report.skipped:
logging.warning(f"⏭️ Test skipped: {item.name}")
else:
# Dummy implementation when pytest is not available
def pytest_runtest_makereport(self, item, call):
pass
# Custom logger methods
def _add_custom_log_methods():

View File

@ -1,74 +0,0 @@
name: classroomcopilot-backend
channels:
- conda-forge
- nodefaults
dependencies:
- python=3.11
# Core dependencies
- pip
- setuptools
- wheel
# Server dependencies
- fastapi
- uvicorn
- python-dotenv
- python-multipart
- python-jose
- pyjwt
- jinja2
# Database and Auth
- sqlalchemy
- sqlalchemy-utils
- asyncpg
- redis-py
# Neo4j
- neo4j-python-driver
# HTTP and Async
- aiohttp
# Data Processing
- pandas
- scipy
- requests
- openpyxl
- scikit-learn
# Testing
- pytest
- pytest-html
# Database clients
- postgresql
- libpq
# LibreOffice for document conversion
- libreoffice
# Additional dependencies via pip
- pip:
# Supabase
- supabase
# Neo4j specific
- neontology
# HTTP and Async
- sseclient-py
# Document Processing
- python-pptx
- python-docx
- pdfminer.six
- Pillow
- psutil
- PyPDF2>=3.0.0
# Web Scraping and Processing
- emoji
- extruct
- w3lib
# Google APIs
- youtube-transcript-api
- google-api-python-client
- google-auth-oauthlib
# LangChain Ecosystem
- "langchain[llms]"
- langchain-community
- langchain-openai
- langgraph
# OpenAI
- openai
- ollama
# Microsoft Authentication
- msal

View File

@ -13,6 +13,7 @@ jinja2
# Database and Auth
supabase
supabase_auth
sqlalchemy
sqlalchemy-utils
asyncpg

Some files were not shown because too many files have changed in this diff Show More