Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1b214da
Basic calendar template in tom_common
Fingel Feb 27, 2026
84ede34
Move calendar to own app
Fingel Feb 27, 2026
f80a670
Add calendarEvent model
Fingel Feb 27, 2026
6700bc4
Add events to calendar
Fingel Feb 27, 2026
abfe947
Remove some duplicate logic
Fingel Feb 27, 2026
3916719
Clean up rendering logic
Fingel Feb 27, 2026
609418a
Add timezone to calendar page
Fingel Feb 28, 2026
cefc88f
Create events
Fingel Feb 28, 2026
5958fc0
Add moon to calendar
Fingel Feb 28, 2026
439b695
Style all day events
Fingel Feb 28, 2026
38d5e2a
Styling for single day events
Fingel Feb 28, 2026
ed77026
Update events
Fingel Feb 28, 2026
62ebee4
Rename to event_form
Fingel Feb 28, 2026
c5d3e53
Remove some duplicate logic in event click htmx
Fingel Feb 28, 2026
a3776d5
Rename event
Fingel Feb 28, 2026
c9ab265
Add new event button
Fingel Feb 28, 2026
41c38ec
Add today button
Fingel Feb 28, 2026
bd9696b
Delete events
Fingel Mar 1, 2026
9acd75c
Make self return type compat with py 3.10
Fingel Mar 1, 2026
2e68def
Organize imports
Fingel Mar 1, 2026
42d1b8e
navbar integration
Fingel Mar 1, 2026
8df6ca1
Return to the same month as created event
Fingel Mar 4, 2026
7ccf6aa
Add target list association to calendar events
Fingel Mar 5, 2026
f0e155c
Only show target list keys for lists present in that month
Fingel Mar 5, 2026
a4e1ec6
Better placement of timezone indicator
Fingel Mar 5, 2026
d3589e9
Stay on current month after updating event
Fingel Mar 5, 2026
1b99fbc
Silence type checker
Fingel Mar 5, 2026
87a17ac
Add tests
Fingel Mar 5, 2026
2649490
Merge branch 'dev' into calendar
Fingel Mar 5, 2026
49366b2
Add user, proposal, telescope free-form fields
Fingel Mar 5, 2026
3771136
Initial todo list support
Fingel Mar 6, 2026
37737ce
Show incomplete todo count on events
Fingel Mar 6, 2026
c69e021
Refresh calendar when todos are updated
Fingel Mar 6, 2026
c110611
Must open event to follow url
Fingel Mar 12, 2026
9daf488
Add link to target list on form
Fingel Mar 12, 2026
b14d05f
Add instrument field
Fingel Mar 12, 2026
145e66a
Add save and edit button - todos easier
Fingel Mar 12, 2026
db1b8d4
Adjustable utc offset
Fingel Mar 12, 2026
32b3bf9
Use select for utc offset
Fingel Mar 12, 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
1 change: 1 addition & 0 deletions tom_base/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
'tom_observations',
'tom_dataproducts',
'tom_dataservices',
'tom_calendar',
]

SITE_ID = 1
Expand Down
Empty file added tom_calendar/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions tom_calendar/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.contrib import admin

from .models import CalendarEvent

admin.site.register(CalendarEvent)
12 changes: 12 additions & 0 deletions tom_calendar/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from django.apps import AppConfig


class TomCalendarConfig(AppConfig):
name = 'tom_calendar'

def nav_items(self):
"""
Integration point for adding items to the navbar.
This method should return a list of partial templates to be included in the navbar.
"""
return [{'partial': 'tom_calendar/partials/navbar_item.html'}]
27 changes: 27 additions & 0 deletions tom_calendar/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.27 on 2026-02-27 22:33

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='CalendarEvent',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('description', models.TextField(blank=True, default='')),
('start_time', models.DateTimeField()),
('end_time', models.DateTimeField()),
('url', models.URLField(blank=True, default='')),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
],
),
]
20 changes: 20 additions & 0 deletions tom_calendar/migrations/0002_calendarevent_target_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.27 on 2026-03-05 17:52

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('tom_targets', '0030_alter_basetarget_slope'),
('tom_calendar', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='calendarevent',
name='target_list',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='tom_targets.targetlist'),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 4.2.27 on 2026-03-05 22:35

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tom_calendar', '0002_calendarevent_target_list'),
]

operations = [
migrations.AddField(
model_name='calendarevent',
name='proposal',
field=models.CharField(blank=True, default='', max_length=200),
),
migrations.AddField(
model_name='calendarevent',
name='telescope',
field=models.CharField(blank=True, default='', max_length=200),
),
migrations.AddField(
model_name='calendarevent',
name='user',
field=models.CharField(blank=True, default='', max_length=200),
),
]
25 changes: 25 additions & 0 deletions tom_calendar/migrations/0004_eventtodo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.27 on 2026-03-05 23:42

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('tom_calendar', '0003_calendarevent_proposal_calendarevent_telescope_and_more'),
]

operations = [
migrations.CreateModel(
name='EventTodo',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('description', models.CharField(max_length=200)),
('is_completed', models.BooleanField(default=False)),
('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='todos', to='tom_calendar.calendarevent')),
],
),
]
18 changes: 18 additions & 0 deletions tom_calendar/migrations/0005_calendarevent_instrument.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-03-12 20:58

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tom_calendar', '0004_eventtodo'),
]

operations = [
migrations.AddField(
model_name='calendarevent',
name='instrument',
field=models.CharField(blank=True, default='', max_length=200),
),
]
Empty file.
51 changes: 51 additions & 0 deletions tom_calendar/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from django.db import models

from tom_targets.models import TargetList

from .utils import BOOTSTRAP_COLORS


class EventTodo(models.Model):
event = models.ForeignKey('CalendarEvent', on_delete=models.CASCADE, related_name='todos')
description = models.CharField(max_length=200)
is_completed = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)

def __str__(self):
return f'Todo for {self.event.title}: {self.description}'


class CalendarEvent(models.Model):
"""
Class representing an event in the calendar.

Other applications can create calendar events by creating instances of this class.

"""
title = models.CharField(max_length=200)
description = models.TextField(blank=True, default="")
start_time = models.DateTimeField()
end_time = models.DateTimeField()
url = models.URLField(blank=True, default="")
"""The URL a user can visit for more information or associated object."""
target_list = models.ForeignKey(TargetList, on_delete=models.SET_NULL, null=True, blank=True)
user = models.CharField(max_length=200, blank=True, default="")
proposal = models.CharField(max_length=200, blank=True, default="")
telescope = models.CharField(max_length=200, blank=True, default="")
instrument = models.CharField(max_length=200, blank=True, default="")
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)

todos: models.Manager[EventTodo]

def __str__(self):
return self.title

@property
def color(self) -> str:
return BOOTSTRAP_COLORS[self.pk % len(BOOTSTRAP_COLORS)]

@property
def active_todos(self):
return self.todos.filter(is_completed=False)
30 changes: 30 additions & 0 deletions tom_calendar/templates/tom_calendar/calendar_page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% extends 'tom_common/base.html' %}
{% block title %}Calendar{% endblock %}
{% block content %}
{% include 'tom_calendar/partials/calendar.html' %}
<!-- Modal -->
<div class="modal fade" id="cal-modal" tabindex="-1" aria-labelledby="calModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body" id="cal-modal-body">
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_javascript %}
<script>
document.body.addEventListener("calClose", function () {
$("#cal-modal").modal("hide");
});
document.body.addEventListener("calRefresh", function () {
const cal = document.getElementById("calendar-partial");
htmx.ajax("GET", cal.dataset.url, {target: "#calendar-partial", swap: "outerHTML"});
});
</script>
{% endblock %}
Loading
Loading