Spaces:
Sleeping
Sleeping
Multi-select areas, colour-coded area tags, Creighton students only, titles removed, Creighton blue
4f5fdec verified |
Download README.md from stevafernandes/CURAS: direct link, hf CLI and curl.
- Browser
- Download file 7.4 kB
-
https://huggingface.co/spaces/stevafernandes/CURAS/resolve/main/README.md
- Command line
-
hf download hf://spaces/stevafernandes/CURAS/README.md
-
curl -L -o README.md https://huggingface.co/spaces/stevafernandes/CURAS/resolve/main/README.md
7.4 kB
| title: CURAS Research Database | |
| colorFrom: gray | |
| colorTo: gray | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| # CURAS Research Database | |
| A public database of undergraduate research opportunities posted and updated by the | |
| Center for Undergraduate Research and Scholarship (CURAS). Faculty submit new listings or | |
| updates through a form, a CURAS administrator approves them, and approved listings appear | |
| on the public page immediately. | |
| - **Public page** (`/`): search and filter listings by area of study (several at once), | |
| type of opportunity, term and standing. Listings are headed by the faculty member's name, | |
| every listing states that it is open to Creighton students only, and each area of study | |
| is shown as a colour-coded tag (the colours are defined in `app/fields.py` and | |
| `app/static/style.css`). Each listing has a permanent link (`/listings/<id>`). The pages | |
| use Creighton's brand palette (Pantone 286 C blue, #0054A6). | |
| - **Submission form** (`/submit`): faculty enter their name and NetID plus every listing | |
| field. Choosing an existing listing pre-fills the form to propose an update. | |
| - **Admin** (`/admin`): shared-password sign-in. Review pending submissions (updates show a | |
| side-by-side comparison with the current listing), approve or reject, edit, retire and | |
| restore listings, add a listing directly, export everything as CSV. | |
| Data lives in Supabase (Postgres). The application talks to Supabase only from the server | |
| with the `service_role` key; Row Level Security is enabled with no policies, so the public | |
| `anon` key can neither read nor write the tables. | |
| ## One-time setup | |
| ### 1. Create the tables in Supabase | |
| In the Supabase dashboard open **SQL Editor**, paste the contents of | |
| [`supabase/schema.sql`](supabase/schema.sql) and run it. It creates two tables, | |
| `listings` and `submissions`, and locks them down. | |
| ### 2. Collect the four secrets | |
| | Secret | Where it comes from | | |
| |---|---| | |
| | `SUPABASE_URL` | Supabase dashboard: the **Connect** button at the top of the project, or Settings, Data API. It looks like `https://<ref>.supabase.co` | | |
| | `SUPABASE_SERVICE_ROLE_KEY` | Settings, **API Keys**: create or copy a **Secret** key (starts with `sb_secret_`). On older projects the legacy `service_role` key under the "Legacy API keys" tab also works. Never the publishable or anon key, and never put it in a browser | | |
| | `ADMIN_PASSWORD` | A long password you choose (at least 12 characters). Everyone who approves listings uses it. | | |
| | `SESSION_SECRET` | Any long random string (for example 48 random characters). Signs the admin session cookie. | | |
| ### 3. Load the existing listings (once) | |
| In the SQL Editor, paste the contents of [`supabase/seed_listings.sql`](supabase/seed_listings.sql) | |
| and run it. It inserts the 50 listings from the September 2026 workbook as published | |
| listings and ends by reporting `listings_loaded = 50`. It refuses to run a second time, so | |
| it cannot create duplicates. | |
| Nothing needs to be installed or run on your own computer. (If you ever receive a newer | |
| workbook, `scripts/import_excel.py` converts it to `seed/listings_seed.json` and | |
| `scripts/make_seed_sql.py` regenerates the SQL file; both need Python with | |
| `scripts/requirements-dev.txt` installed. `scripts/seed_supabase.py` is an alternative that | |
| loads the JSON through the API using a `.env` file.) | |
| ### 4. Deploy on Hugging Face | |
| 1. Create a new Space: **Docker** SDK, **CPU basic** hardware, visibility of your choice | |
| (the public page needs to be public for students to see it). | |
| 2. Upload these files, keeping the folder structure: `Dockerfile`, `requirements.txt`, | |
| `README.md` and the whole `app/` folder. (`supabase/`, `scripts/`, `seed/` and `tests/` | |
| are not needed on the Space but are harmless if uploaded.) | |
| 3. In the Space, open **Settings**, then **Variables and secrets**, and add the four | |
| secrets from step 2 as **Secrets** (not public variables). | |
| 4. The Space builds and starts. Check `https://<owner>-<space-name>.hf.space/healthz`; | |
| it should report `"db_configured": true` and `"admin_configured": true`. | |
| 5. Share the direct address `https://<owner>-<space-name>.hf.space/` with students and | |
| faculty. The Space page on huggingface.co shows the same site inside a frame, which | |
| works for browsing and submitting but not for admin sign-in (see below). | |
| Optional settings (add as Variables if you need them): `APP_TIMEZONE` (default | |
| `America/Chicago`, the calendar day stamped on "Updated"), `SESSION_HOURS` (default 8), | |
| `CACHE_SECONDS` (default 60, how long the public page may show cached listings). | |
| ### 5. Sign in as admin | |
| Open the Space directly at its own address, `https://<owner>-<space-name>.hf.space/admin/login`, | |
| not inside the Space page on huggingface.co. The sign-in cookie is only sent to the | |
| site's own address, so signing in inside the embedded frame does not work (the page says | |
| so and links to the direct address). Sessions last 8 hours. After 5 wrong passwords from | |
| one network the sign-in waits 15 minutes. | |
| To change the admin password, change the `ADMIN_PASSWORD` secret and restart the Space. | |
| Changing `SESSION_SECRET` signs every admin out. | |
| ## How approval works | |
| 1. A faculty member submits the form. The submission is stored with status `pending` and | |
| is not public. | |
| 2. An admin opens **Admin**, reviews it and clicks **Approve and publish** (or rejects it | |
| with an optional note that stays private). | |
| 3. On approval a new listing is created, or, for an update, the existing listing is | |
| replaced and its "Updated" date set to today. The public page shows the change | |
| immediately. | |
| Listings are never deleted from the admin pages; **Retire** hides a listing and | |
| **Restore** brings it back. An update approved for a retired listing changes its content | |
| but leaves it retired until an admin restores it. | |
| Listings imported from the workbook that lack one of the required fields (for example a | |
| standing or an area of study) can still be edited and updated without inventing that | |
| content; brand-new listings must fill every required field. | |
| ## Running locally | |
| Requires Docker (for a Postgres + PostgREST stand-in that behaves like Supabase's REST | |
| API) and Python 3.12 or newer. | |
| ```bash | |
| pip install -r requirements.txt -r scripts/requirements-dev.txt | |
| docker compose -f .local/docker-compose.yml up -d | |
| python scripts/seed_supabase.py --env .local/.env.local | |
| .local/run_local.sh # serves http://127.0.0.1:7861 | |
| python -m pytest -q tests # unit tests plus every public and admin flow | |
| ``` | |
| `.local/.env.local` holds throw-away local credentials only (and sets `COOKIE_SECURE=false` | |
| because the local server is plain http). The real `.env` is ignored by git and must never | |
| be uploaded to the Space (use Space secrets instead). | |
| ## Project layout | |
| ``` | |
| app/main.py routes (public, submission, admin) | |
| app/services.py validation, filtering, approval logic | |
| app/fields.py the single definition of every listing field and vocabulary | |
| app/normalize.py turns messy multi-value text into vocabulary items | |
| app/db.py Supabase REST (PostgREST) client | |
| app/security.py sessions, CSRF tokens, rate limiting | |
| app/templates/ Jinja2 templates | |
| app/static/ style.css, app.js | |
| scripts/ import_excel.py, seed_supabase.py | |
| seed/ listings_seed.json generated from the workbook | |
| supabase/schema.sql database schema | |
| tests/ pytest suite | |
| ``` | |