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 __pycache__
.DS_Store
.vscode
.idea
.pytest_cache .pytest_cache
.coverage
.coverage.* .DS_Store
.coverage.*.*
.coverage.*.*.* .env
.coverage.*.*.*.*
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 FROM python:3.11-slim
# Set working directory
WORKDIR /app WORKDIR /app
# Install system dependencies # Copy requirements and install 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 . COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \ RUN pip install --no-cache-dir -r requirements.txt
pip install --no-cache-dir -r requirements.txt
# Copy the application # Copy application code
COPY . . COPY . .
# Create necessary directories # Make startup script executable
RUN mkdir -p static templates/admin logs RUN chmod +x start.sh
# Create entrypoint script # Set environment variables
COPY docker-entrypoint.sh /usr/local/bin/ ENV PYTHONPATH=/app
RUN chmod +x /usr/local/bin/docker-entrypoint.sh ENV UVICORN_PORT=8000
ENV UVICORN_WORKERS=4
EXPOSE ${PORT_BACKEND} # Expose port
EXPOSE 8000
ENTRYPOINT ["docker-entrypoint.sh"] # Use the new startup script in production mode
CMD ["python", "main.py"] 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: services:
backend: backend:
container_name: ccapi container_name: cc-api
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
env_file: .env env_file:
- .env
- .env.app.prod
ports: ports:
- "${PORT_BACKEND}:${PORT_BACKEND}" - 8000:8000
networks: networks:
- kevlarai-network - 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 "$@"

120
main.py
View File

@ -1,4 +1,6 @@
import os import os
import argparse
import sys
from modules.logger_tool import initialise_logger from modules.logger_tool import initialise_logger
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True) logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True)
from fastapi import FastAPI, HTTPException from fastapi import FastAPI, HTTPException
@ -100,34 +102,94 @@ def initialize_with_retry(max_attempts: int = 3, initial_delay: int = 5) -> bool
return False return False
if __name__ == "__main__": def run_initialization_mode():
import uvicorn """Run only the initialization process"""
import os logger.info("Running in initialization mode")
logger.info("Starting system initialization...")
# Run initialization with retry logic
if not initialize_with_retry(): if initialize_with_retry():
logger.error("Failed to initialize system after multiple attempts") logger.info("Initialization completed successfully")
# Continue anyway to allow the API to start and handle health checks return True
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
)
else: else:
logger.info("Running without Reload and without SSL (behind reverse proxy)") logger.error("Initialization failed after multiple attempts")
uvicorn.run( return False
"main:app",
host="0.0.0.0", def run_development_mode():
port=int(os.getenv('PORT_BACKEND', 8000)), # <-- not 443 """Run the server in development mode with auto-reload"""
log_level="info", logger.info("Running in development mode")
proxy_headers=True, logger.info("Starting uvicorn server with auto-reload...")
timeout_keep_alive=10,
workers=int(os.getenv('UVICORN_WORKERS', '1')) 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 typing import Dict, Optional, Any, TypedDict, List
from supabase import create_client, Client from supabase import create_client, Client
from supabase.lib.client_options import SyncClientOptions from supabase.lib.client_options import SyncClientOptions
from gotrue import SyncMemoryStorage from supabase_auth import SyncMemoryStorage
from modules.logger_tool import initialise_logger from modules.logger_tool import initialise_logger
logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), 'default', True) 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 sys
import logging import logging
import datetime import datetime
import pytest try:
import pytest
PYTEST_AVAILABLE = True
except ImportError:
PYTEST_AVAILABLE = False
from dotenv import load_dotenv, find_dotenv from dotenv import load_dotenv, find_dotenv
# Load environment variables # Load environment variables
@ -86,17 +90,22 @@ class FileFormatter(logging.Formatter):
return logging.Formatter(LOG_FORMAT).format(record) return logging.Formatter(LOG_FORMAT).format(record)
class PytestFormatter: class PytestFormatter:
@pytest.hookimpl(hookwrapper=True) if PYTEST_AVAILABLE:
def pytest_runtest_makereport(self, item, call): @pytest.hookimpl(hookwrapper=True)
outcome = yield def pytest_runtest_makereport(self, item, call):
report = outcome.get_result() outcome = yield
if report.when == "call": report = outcome.get_result()
if report.passed: if report.when == "call":
logging.success(f"✅ Test passed: {item.name}") if report.passed:
elif report.failed: logging.success(f"✅ Test passed: {item.name}")
logging.error(f"❌ Test failed: {item.name}") elif report.failed:
elif report.skipped: logging.error(f"❌ Test failed: {item.name}")
logging.warning(f"⏭️ Test skipped: {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 # Custom logger methods
def _add_custom_log_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 # Database and Auth
supabase supabase
supabase_auth
sqlalchemy sqlalchemy
sqlalchemy-utils sqlalchemy-utils
asyncpg asyncpg

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