Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e75a4c2
background task & logging
aspiringLich Feb 28, 2026
74db110
Merge branch 'develop' into feature/39-box-integration
aspiringLich Feb 28, 2026
5305599
upload folders to box
aspiringLich Feb 28, 2026
06e29ee
cleanup & mark chunked uploads as unimplemented
aspiringLich Feb 28, 2026
591a7b3
file caching (not working)
aspiringLich Mar 2, 2026
457183b
tweaks
aspiringLich Mar 9, 2026
b7c573d
Merge branch 'main' into feature/39-box-integration
aspiringLich Mar 9, 2026
5ed8da5
review pass
aspiringLich Mar 9, 2026
c6ceb54
review pass 2
aspiringLich Mar 9, 2026
88edf16
fix error when deleting files
aspiringLich Mar 9, 2026
4b647db
update database
aspiringLich Mar 9, 2026
8a24124
update image download api
aspiringLich Mar 9, 2026
9837057
Merge branch 'develop' into feature/39-box-integration
aspiringLich Mar 9, 2026
fc90e63
update README & fix merge conflict
aspiringLich Mar 9, 2026
3721ad9
update boxsdk
aspiringLich Apr 16, 2026
543557f
Merge branch 'develop' into feature/39-box-integration
aspiringLich Apr 16, 2026
08cbd29
storage backend
aspiringLich Apr 16, 2026
0002f9b
nits
aspiringLich Apr 16, 2026
6b5d9f3
move box upload logic to storage.py
aspiringLich Apr 16, 2026
f7e266f
nit (BOX_DEVELOPER_TOKEN overrides BOX_JWT)
aspiringLich Apr 16, 2026
b7f104d
box storage tests
aspiringLich Apr 16, 2026
62c5d02
oauth and docker compose examples
aspiringLich Apr 16, 2026
0c8c54c
cleaner storage api
aspiringLich Apr 23, 2026
f4e67e9
oauth working mostly!
aspiringLich Apr 23, 2026
ea0c92b
update record upload / media upload with new logic
aspiringLich Apr 23, 2026
c835dbb
polish storage logic
aspiringLich Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions bfd9000_web/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Django Settings
Comment thread
aspiringLich marked this conversation as resolved.
SECRET_KEY=your-secret-key-here
DEBUG=True
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
Comment thread
aspiringLich marked this conversation as resolved.

# Box.com Integration
BOX_DEVELOPER_TOKEN=your-box-developer-token-here
BOX_JWT_CONFIG_FILE=/path/to/box-jwt-config.json
BOX_FOLDER_ID=your-box-folder-id-here

# Scanner Configuration
SCANNER_API_BASE=http://localhost:5000
SCANNER_DEVICE_ID=scanner-001

# BFD9020 AI Service
BFD9020_BASE_URL=https://wingate.case.edu/bfd9020

# CORS Configuration
CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
76 changes: 73 additions & 3 deletions bfd9000_web/BFD9000/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,32 @@
https://docs.djangoproject.com/en/5.2/ref/settings/
"""

import logging
import os
from pathlib import Path

from dotenv import load_dotenv

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# Load environment variables from .env file
load_dotenv()


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/


# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get(
'SECRET_KEY', 'django-insecure-+6m#s88j*)qb+a%2s%cw31e2k04um&*a-fk!jgcpl3849(w4sm')

# Box.com Configuration
BOX_DEVELOPER_TOKEN = os.environ.get('BOX_DEVELOPER_TOKEN')
BOX_JWT_CONFIG_FILE = os.environ.get('BOX_JWT_CONFIG_FILE')
BOX_FOLDER_ID = os.environ.get('BOX_FOLDER_ID')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DEBUG', 'True') == 'True'
APP_VERSION_FILE = BASE_DIR / 'VERSION'
Expand All @@ -37,6 +49,9 @@
if not DEBUG and 'SECRET_KEY' not in os.environ:
raise RuntimeError('SECRET_KEY must be set when DEBUG=False')

if not DEBUG and 'BOX_ACCESS_TOKEN' not in os.environ:
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
raise RuntimeError('BOX_ACCESS_TOKEN must be set when DEBUG=False')

Comment thread
aspiringLich marked this conversation as resolved.
Outdated
ALLOWED_HOSTS = os.environ.get(
'DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')

Expand Down Expand Up @@ -227,8 +242,63 @@ def _prefix_url(path):

SCANNER_API_BASE = os.environ.get('SCANNER_API_BASE', 'http://localhost:5000')
SCANNER_DEVICE_ID = os.environ.get('SCANNER_DEVICE_ID', 'scanner-001')
BFD9020_BASE_URL = os.environ.get(
'BFD9020_BASE_URL', 'https://wingate.case.edu/bfd9020')
BFD9020_BASE_URL = os.environ.get('BFD9020_BASE_URL', 'https://wingate.case.edu/bfd9020')

# Logging Configuration
class PrettyFormatter(logging.Formatter):
Comment thread
aspiringLich marked this conversation as resolved.
"""A custom formatter to add color to stdout log records."""

GRAY = "\x1b[90m"
YELLOW = "\x1b[33;21m"
RED = "\x1b[31;21m"
BOLD_RED = "\x1b[31;1m"
RESET = "\x1b[0m"
Comment thread
aspiringLich marked this conversation as resolved.

# Define a different format for each level
FORMATS = {
logging.DEBUG: f"{GRAY}%(asctime)s {GRAY}DEBUG {GRAY}%(name)s: {RESET}%(message)s",
logging.INFO: f"{GRAY}%(asctime)s {RESET}INFO {GRAY}%(name)s: {RESET}%(message)s",
logging.WARNING: f"{GRAY}%(asctime)s {YELLOW}WARN {GRAY}%(name)s: {RESET}%(message)s",
logging.ERROR: f"{GRAY}%(asctime)s {RED}ERROR {GRAY}%(name)s: {RESET}%(message)s",
logging.CRITICAL: f"{GRAY}%(asctime)s {BOLD_RED}CRIT {GRAY}%(name)s: {RESET}%(message)s"
}

def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)

LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'pretty': {
'()': PrettyFormatter,
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'pretty',
},
},
'root': {
'handlers': ['console'],
'level': 'INFO',
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
'archive': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
},
},
}

# Thumbnail generation policy (staging and UI previews)
THUMBNAIL_MAX_WIDTH = int(os.environ.get('THUMBNAIL_MAX_WIDTH', '300'))
Expand All @@ -239,4 +309,4 @@ def _prefix_url(path):
'THUMBNAIL_HARD_MAX_BYTES', str(100 * 1024)))
THUMBNAIL_DEFAULT_QUALITY = int(
os.environ.get('THUMBNAIL_DEFAULT_QUALITY', '75'))
THUMBNAIL_MIN_QUALITY = int(os.environ.get('THUMBNAIL_MIN_QUALITY', '40'))
THUMBNAIL_MIN_QUALITY = int(os.environ.get('THUMBNAIL_MIN_QUALITY', '40'))
25 changes: 25 additions & 0 deletions bfd9000_web/archive/apps.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
"""Django app configuration for the archive app."""

import logging
import os
import sys
import threading

from django.apps import AppConfig

logger = logging.getLogger(__name__)


class ArchiveConfig(AppConfig):
"""Configure default settings for the archive app."""
default_auto_field = "django.db.models.BigAutoField" # pyright: ignore[reportAssignmentType]
name = "archive"

def ready(self):
"""Initialize the archive app and start background tasks."""
# Only start during runserver, not during migrations, tests, etc.
# Guard against running twice in development (autoreloader issue)
if 'runserver' in sys.argv and not self._is_reloader_process():
Comment thread
aspiringLich marked this conversation as resolved.
Outdated
self._start_background_task()

def _is_reloader_process(self):
"""Check if this is the reloader process (development only)."""
return 'runserver' in sys.argv and os.environ.get('RUN_MAIN') != 'true'

def _start_background_task(self):
"""Start the background media upload thread."""
from archive.media_upload import media_upload_worker

thread = threading.Thread(target=media_upload_worker, daemon=True)
thread.start()
Loading
Loading