Skip to content
Open
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
11 changes: 9 additions & 2 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ wagtail-indymeet/
│ │ ├── session.py # Session and SessionMembership models
│ │ ├── event.py # Event models
│ │ └── resource.py # Resource models
│ │ └── talk.py # Talk w/ GIS PointField
│ ├── serializers/
│ │ └── talks.py # GeoJSON REST API serializer
│ ├── views/
│ │ └── talks.py # TemplateView + ListAPIView
│ ├── templates/
│ │ └── home/talks/ # Importmap for Three.js
│ ├── management/
│ │ └── commands/ # Django management commands
│ └── puput_migrations/ # Blog-specific migrations
Expand All @@ -40,8 +47,8 @@ wagtail-indymeet/

## Technology Stack

- **Backend:** Django 5.2, Wagtail CMS, PostgreSQL
- **Frontend:** Tailwind CSS, Alpine.js
- **Backend:** Django 5.2, Wagtail CMS, PostgreSQL + PostGIS (GIS extension), Django REST Framework + GeoJSON APIs
- **Frontend:** Tailwind CSS, Alpine.js, Three.js (3D globe), Leaflet.js (admin geo-widget)
- **Package Management:** uv (fast Python package installer)
- **Deployment:** Dokku (Heroku buildpacks)
- **Testing:** pytest, pytest-django, playwright
Expand Down
2 changes: 1 addition & 1 deletion .env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#DATABASE_URL://USER:PASSWORD@HOST:PORT/NAME
DATABASE_URL=postgres://djangonaut:djangonaut@localhost:5432/djangonaut-space
DATABASE_URL=postgis://djangonaut:djangonaut@localhost:5432/djangonaut-space
SECRET_KEY=<SECRETKEY>
ENVIRONMENT='production'
RECAPTCHA_PUBLIC_KEY='dummy_value'
Expand Down
2 changes: 1 addition & 1 deletion .env.template.docker
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DATABASE_URL=postgres://djangonaut:djangonaut@db:5432/djangonaut
DATABASE_URL=postgis://djangonaut:djangonaut@db:5432/djangonaut
POSTGRES_USER=djangonaut
POSTGRES_PASSWORD=djangonaut
POSTGRES_DB=djangonaut
Expand Down
2 changes: 1 addition & 1 deletion .env.template.local
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DATABASE_URL=postgres://djangonaut:djangonaut@localhost:5432/djangonaut-space
DATABASE_URL=postgis://djangonaut:djangonaut@localhost:5432/djangonaut-space
SECRET_KEY=<SECRETKEY>
RECAPTCHA_PUBLIC_KEY='dummy_value'
RECAPTCHA_PRIVATE_KEY='dummy_value'
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
env:
ENVIRONMENT: 'test'
DJANGO_SETTINGS_MODULE: 'indymeet.settings.test'
DATABASE_URL: "postgres://djangonaut:djangonaut@localhost:5432/djangonaut-space"
DATABASE_URL: "postgis://djangonaut:djangonaut@localhost:5432/djangonaut-space"
DEBUG: False
SECRET_KEY: "hunter2"
RECAPTCHA_PUBLIC_KEY: "dummy_value"
Expand All @@ -25,7 +25,7 @@ jobs:
contents: read
services:
pg:
image: postgres:17
image: postgis/postgis:17-3.5
ports:
- 5432:5432
env:
Expand All @@ -42,6 +42,11 @@ jobs:
with:
python-version: '3.11'

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libgdal-dev g++ gdal-bin libpq-dev

- name: Install uv
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7

Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ repos:
rev: v6.0.0
hooks:
- id: check-added-large-files
exclude: three/.*\.js$ # Skip large Three.js files
- id: check-case-conflict
- id: check-json
- id: check-merge-conflict
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RUN apt-get update \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update \
&& apt-get install nodejs -y \
&& apt-get install -y nodejs libgdal-dev g++ gdal-bin libpq-dev \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man \
&& apt-get clean

Expand Down
23 changes: 15 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,13 @@ This is an example of how to list things you need to use the software and how to
# On Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
```
3. Create a posgresql database
3. Install required system libraries
```sh
# Install system libraries on Debian/Ubuntu with:
sudo apt install libgdal-dev gdal-bin libpq5
```
(For Windows installation use OSGeo4W or conda for GDAL/PostGIS support. For macOS installation use Homebrew for equivalent libraries.)
4. Create a posgresql database
```sh
psql -U postgres
```
Expand All @@ -110,15 +116,16 @@ This is an example of how to list things you need to use the software and how to
```sh
postgres=# exit
```
4. Install dependencies (this will automatically create a virtual environment)
Note: PostGIS extension is automatically created by Django migrations in step 7 (uv run python manage.py migrate).
5. Install dependencies (this will automatically create a virtual environment)
```sh
uv sync --extra dev --extra test
```
Set up the git hook scripts
```sh
uv run pre-commit install
```
5. Copy `.env.template.local` file, rename to `.env` and use variables for your local postgres database.
6. Copy `.env.template.local` file, rename to `.env` and use variables for your local postgres database.
Copy in Linux:
```sh
cp .env.template.local .env
Expand All @@ -127,22 +134,22 @@ This is an example of how to list things you need to use the software and how to
```sh
copy .env.template.local .env
```
6. Run migrations and create superuser
7. Run migrations and create superuser
```sh
uv run python manage.py migrate
# Potentially load data first
# uv run python manage.py loaddata fixtures/data.json
uv run python manage.py createsuperuser
```
7. Install tailwind. You also need npm installed.
8. Install tailwind. You also need npm installed.
```sh
uv run python manage.py tailwind install
```
8. Run server locally
9. Run server locally
```sh
uv run python manage.py runserver
```
9. Run tailwind in another terminal locally
10. Run tailwind in another terminal locally
```sh
uv run python manage.py tailwind start
```
Expand All @@ -168,7 +175,7 @@ If you have docker installed, alternatively

2. In a new terminal, run any setup commands you need such as
```sh
docker compose exec web python manage.py createsuperuser
docker compose exec web uv run python manage.py createsuperuser
```

3. Go to: http://127.0.0.1:8000/ and enjoy!
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ services:

db:
restart: always
image: postgres:17
image: postgis/postgis:17-3.5
volumes:
- postgres_data:/var/lib/postgresql/data/
- postgres_data:/var/lib/postgresql/data
env_file:
- ./.env.template.docker
ports:
Expand Down
70 changes: 70 additions & 0 deletions docs/post-map-changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Post-Map Page Setup (For Existing DB Users)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by the purpose of this file. Can you help me understand it please?


After pulling the branch with the map page, follow these steps if you have **existing PostgreSQL data**.

## Changes Made

- **Dockerfile**: Added `libgdal-dev g++ gdal-bin libpq-dev` for GDAL compilation
- **pyproject.toml**: Added GDAL pinned to `3.6.2` (matches Debian bookworm)
- **docker-compose.yml**: Database image changed from `postgres:17` to `postgis/postgis:17-3.5` (include PostGIS binaries) -
**Image change doesn't touch your data, your volume mount stays identical**
- **`.env.template.local`, `.env.template`, `.env.template.docker`**: Have the `DATABASE_URL` changed from `postgres://` to **`postgis://`**
- **Django migrations**: `home/migrations/0049_talk_talkspeaker.py` automatically runs `CREATE EXTENSION IF NOT EXISTS postgis;` during `python manage.py migrate` - **no manual psql command needed**

Note: The postgres:// → postgis:// change was updated across ALL active config files (`.env` templates, `.github/workflows/tests.yml`, etc.) except `docs/archive/hetzner-migration.md` (archived doc).

## Required Actions (Preserves Your Data)
### ⚠️ Update Your .env File
#### Copy the **NEW** template to update DATABASE_URL:
Copy in Linux:
```sh
cp .env.template.local .env
```
Copy in Windows:
```sh
copy .env.template.local .env
```

#### Check the `DATABASE_URL` variable change in the .env:
from:
```
DATABASE_URL=postgres://djangonaut:djangonaut@localhost:5432/djangonaut-space
```
to:
```
DATABASE_URL=postgis://djangonaut:djangonaut@localhost:5432/djangonaut-space
```


## ⚠️ NEW: Install System Libraries (Non-Docker Users)

### **Map page requires GDAL/PostGIS libraries:**

**Linux (Debian/Ubuntu):**
```sh
sudo apt install libgdal-dev gdal-bin libpq5
```

## If using Docker:
### Rebuild & Restart Services
```sh
docker compose build --no-cache # Rebuilds with GDAL libraries
docker compose up -d db # Restart DB with PostGIS image
```

### Fix Collation Mismatch (One-time)
Collation fix is a one-time operation - handle it manually after switching images:
```sh
docker compose exec db psql -U djangonaut -d djangonaut -c "ALTER DATABASE djangonaut REFRESH COLLATION VERSION;"
```

### OPTIONAL - Silence template1 and postgres warnings:
```sh
docker compose exec db psql -U djangonaut -d template1 -c "ALTER DATABASE template1 REFRESH COLLATION VERSION;"
docker compose exec db psql -U djangonaut -d postgres -c "ALTER DATABASE postgres REFRESH COLLATION VERSION;"
```

### Start Full Stack
```sh
docker compose up -d
```
7 changes: 7 additions & 0 deletions home/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from django.contrib.gis.geos import Point

DATE_INPUT_FORMAT = ["%Y-%m-%d", "%m/%d/%Y", "%m/%d/%y", "%d/%m/%y", "%d/%m/%Y"]

SURVEY_FIELD_VALIDATORS = {
"max_length": {"email": 250, "text": 700, "text_area": 700, "url": 250},
"min_length": {"text_area": 1, "text": 1},
}

SRID_WGS84 = 4326 # WGS84 standard (lat/lon coordinates)

# Conventional "unknown location" coordinate (0°N, 0°E, WGS84)
NULL_ISLAND = Point(0, 0, srid=SRID_WGS84)
124 changes: 124 additions & 0 deletions home/migrations/0049_talk_talkspeaker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Generated by Django 5.2.8 on 2026-01-25 13:53

import django.contrib.gis.db.models.fields
import django.db.models.deletion
import modelcluster.fields
import uuid
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("home", "0048_create_testimonial_model"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.RunSQL(
"CREATE EXTENSION IF NOT EXISTS postgis;",
reverse_sql=migrations.RunSQL.noop, # Does nothing when rolled back
),
Comment on lines +19 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be replaced with django.contrib.postgres.operations.CreateExtension if you'd like:

        CreateExtension("postgis"),

migrations.CreateModel(
name="Talk",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"uuid",
models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
),
("title", models.CharField(max_length=255, verbose_name="title")),
(
"description",
models.TextField(
blank=True,
help_text="Provide a brief summary of the talk.",
verbose_name="description",
),
),
("date", models.DateField()),
(
"talk_type",
models.CharField(
choices=[("on_site", "On-Site"), ("online", "Online")],
default="on_site",
help_text="Select the format of the talk.",
max_length=12,
verbose_name="talk Format",
),
),
(
"event_name",
models.CharField(
help_text="Please provide the name of the event where the talk was held.",
max_length=255,
verbose_name="event name",
),
),
(
"video_link",
models.URLField(
blank=True,
default="",
max_length=1024,
verbose_name="video Link",
),
),
("address", models.CharField(blank=True, max_length=250, null=True)),
(
"location",
django.contrib.gis.db.models.fields.PointField(
blank=True, null=True, srid=4326
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="TalkSpeaker",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"sort_order",
models.IntegerField(blank=True, editable=False, null=True),
),
(
"speaker",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
(
"talk",
modelcluster.fields.ParentalKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="speakers",
to="home.talk",
),
),
],
options={
"unique_together": {("talk", "speaker")},
},
),
]
1 change: 1 addition & 0 deletions home/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
from .resource import *
from .session import *
from .survey import *
from .talk import *
from .testimonial import *
Loading
Loading