Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 46 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Fusion-Integrated — copy to .env for local development.
# .env is gitignored. Real environment variables always win.

DJANGO_SETTINGS_MODULE=config.settings.dev

# Required in production, no fallback there.
# python -c 'import secrets; print(secrets.token_urlsafe(64))'
# DJANGO_SECRET_KEY=

DB_NAME=fusion_integrated
DB_USER=fusion_admin
# Set this to whatever your local Postgres uses.
DB_PASSWORD=
DB_HOST=127.0.0.1
DB_PORT=5432
# Must stay 0 if PgBouncer is ever put in front in transaction mode.
DB_CONN_MAX_AGE=0

# --- Fusion_System_Administrator = the IAM -----------------------------------
# Identity, RBAC and the common directory (student/faculty info) all live there.
# This service has no user table of its own.
IAM_BASE_URL=http://127.0.0.1:8001
IAM_API_PREFIX=/api
# Server-to-server credential, for directory reads with no user session behind
# them (a report listing 300 applicants by name). Mint it on the IAM with:
# manage.py service_token --issue fusion-integrated
# Sent as `Authorization: Service fsvc_...` — a different scheme from a user
# session, so the IAM can never mistake this service for a person.
IAM_SERVICE_TOKEN=
IAM_TIMEOUT_SECONDS=5
IAM_SESSION_CACHE_SECONDS=60
# Must differ from the console's cookie: on one hostname they overwrite each other.
IAM_AUTH_COOKIE_NAME=fusion_session

# Optional. Without these, cache falls back to in-process LocMem.
# REDIS_CACHE_URL=redis://127.0.0.1:6379/1
# REDIS_BROKER_URL=redis://127.0.0.1:6380/0

# Documents are Google Drive links, so nothing is written here any more. The
# path only has to stay resolvable for rows attached before that switch.
# Must NOT be inside the repo. Defaults to
# ~/.local/share/fusion-integrated/uploads.
# PLACEMENT_UPLOAD_ROOT=

DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1
DJANGO_CORS_ALLOWED_ORIGINS=http://localhost:5173
109 changes: 40 additions & 69 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,78 +1,49 @@
# Byte-compiled / optimized / DLL files
# --- Secrets ------------------------------------------------------------------
.env
.env.*
!.env.example
*.pem
*.key
*.p12

# --- User data ------------------------------------------------------------------
# Uploaded documents: real resumes and certificates. Never in version control.
var/
media/

# --- Python ---------------------------------------------------------------------
.venv/
venv/
__pycache__/
*.py[cod]
*$py.class

# IDEs #
.vscode
.idea

# Distribution / packaging
.Python
env/
build/
media
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# Swap files
*.swp

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
.pytest_cache/
.ruff_cache/
.mypy_cache/
.import_linter_cache/
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Flask stuff:
instance/.webassets-cache

# PyBuilder
target/
staticfiles/
*.sqlite3
*.sql
*.dump

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv: ignore environment folder ending with env. e.g. env, venv, myenv, fusionenv, etc
*env/

# Rope project settings
.ropeproject

# VSCode Settings
#.vscode/settings.json

# Node Modules
# --- Node / client ----------------------------------------------------------------
node_modules/

FusionIIIT/static/
package-lock.json


client/dist/
client/screenshots/
*.tsbuildinfo
npm-debug.log*
yarn-error.log

# --- Generated docs ---------------------------------------------------------------
# Rebuilt by `make module-structure`; shared as a file, not carried in the repo.
docs/module-structure.pdf
docs/module-structure.html

# --- Editors and OS ---------------------------------------------------------------
.DS_Store
**/generated.pdf
.idea/
.vscode/
*.swp
68 changes: 68 additions & 0 deletions .importlinter
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[importlinter]
root_packages =
config
core
fusion_auth
modules
# Required because the domain contract forbids external packages
# (django, rest_framework, celery).
include_external_packages = True

[importlinter:contract:modules-only-touch-contracts]
name = Modules may reach each other ONLY through contracts.py
type = forbidden
source_modules =
modules.placement
modules.directory
modules.accesscontrol
forbidden_modules =
modules.placement.models
modules.placement.services
modules.placement.selectors
modules.placement.api
modules.placement.domain
modules.directory.models
modules.directory.services
modules.directory.api
modules.accesscontrol.models
# DIRECT imports only. contracts.py legitimately imports its OWN module's
# models and services, so following the chain transitively would make this
# contract impossible to satisfy. What we forbid is a module reaching straight
# into another module's internals.
allow_indirect_imports = true
# contracts.py is deliberately absent from the list above — it is the door.

[importlinter:contract:domain-is-pure-python]
name = domain/ must not import Django, DRF or Celery
type = forbidden
source_modules =
# Every module's domain, not just the first one written.
modules.placement.domain
forbidden_modules =
django
rest_framework
celery

# NOTE on `devtools`: it is intentionally NOT listed as a module anywhere in
# this file. Seeding fixtures is cross-cutting by nature — it touches placement
# and directory at once — so it is a tool, not a bounded context. It
# is installed in dev/test settings only. Declaring the exemption here keeps it
# visible instead of it quietly becoming a back door.

[importlinter:contract:core-is-a-leaf]
name = core/ must not import any module
type = forbidden
source_modules = core
forbidden_modules =
modules
fusion_auth

[importlinter:contract:placement-layers]
name = Layering inside placement
type = layers
layers =
modules.placement.api
modules.placement.services
modules.placement.selectors
modules.placement.models
modules.placement.domain
Binary file added 01_ELM_Problem_Statement_v1.4_FINAL.docx
Binary file not shown.
Binary file not shown.
Binary file added 03_ELM_Use_Case_Specifications_v1.4_FINAL.docx
Binary file not shown.
Binary file not shown.
Binary file added 06_ELM_Traceability_Matrix_v1.5_FINAL.xlsx
Binary file not shown.
Binary file added 06_ELM_Workflow_Specifications_v1.6_FINAL.docx
Binary file not shown.
61 changes: 61 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.DEFAULT_GOAL := help
PY := .venv/bin/python
export DJANGO_SETTINGS_MODULE ?= config.settings.dev

help: ## show this help
@grep -hE '^[a-z-]+:.*?## ' $(MAKEFILE_LIST) | awk -F':.*?## ' '{printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'

install: ## create the venv and install everything
uv venv --python 3.12 .venv
uv pip install --python $(PY) -e ".[dev]"

up: ## start postgres + redis
docker compose up -d

migrate: ## apply migrations
$(PY) manage.py migrate

seed: ## register modules and load sample data
$(PY) manage.py seed_modules
$(PY) manage.py seed_demo

dev: ## run the server
$(PY) manage.py runserver 0.0.0.0:8002

test: ## run the test suite
$(PY) -m pytest

check: ## everything CI runs
$(PY) manage.py check
$(PY) manage.py makemigrations --check --dry-run
.venv/bin/ruff check .
.venv/bin/lint-imports
$(PY) ops/checks/no_cross_module_fk.py
$(PY) ops/checks/contracts_are_plural.py
$(PY) ops/checks/nav_matches_routes.py
$(PY) manage.py permission_manifest --check
$(MAKE) schema-check
$(PY) -m pytest -q

check-client: ## the client half of CI
cd client && npm run typecheck && npm test && npm run build

SCHEMA := openapi/fusion-integrated.v1.yaml

schema: ## regenerate the committed OpenAPI schema
$(PY) manage.py spectacular --fail-on-warn --file $(SCHEMA)

permissions: ## regenerate the permission manifest the IAM seeds from
$(PY) manage.py permission_manifest

module-structure: ## regenerate the module-structure reference PDF
$(PY) ops/docs/module_structure.py

schema-check: ## the committed schema must match the code
@$(PY) manage.py spectacular --fail-on-warn --file /tmp/openapi.check.yaml
@diff -u $(SCHEMA) /tmp/openapi.check.yaml > /dev/null \
|| { echo "$(SCHEMA) is stale — run 'make schema'"; exit 1; }
@echo "openapi schema matches the code"

.PHONY: help install up migrate seed dev test check check-client schema \
schema-check permissions module-structure
Loading
Loading