Banyak grup bersepeda memiliki perjalanan reguler - kru komuter Senin pagi, perjalanan petualangan Sabtu pertama setiap bulan, atau putaran sosial Rabu malam. Sampai sekarang, penyelenggara harus membuat setiap acara secara manual. Dengan fitur Pesta Berulang kami yang baru, Anda dapat mengatur jadwal sekali dan membiarkan Party Onbici menghasilkan instansi secara otomatis.
Masalah dengan Acara Satu Kali
Grup bersepeda komunitas biasanya memiliki jadwal yang dapat diprediksi:
- “Setiap Selasa dan Kamis jam 6:30 pagi”
- “Minggu pertama setiap bulan”
- “Setiap Sabtu kedua pagi”
Membuat acara-acara ini secara manual membosankan, rentan kesalahan, dan membuat sulit untuk mempertahankan rute dan pengaturan yang konsisten di seluruh kejadian.
Memperkenalkan iCalendar RRULE
Kami membangun sistem acara berulang kami berdasarkan standar iCalendar RRULE (RFC 5545). Ini adalah format yang sama yang digunakan oleh Google Calendar, Apple Calendar, dan Outlook - memastikan kompatibilitas dengan ekosistem kalender yang lebih luas.
Contoh Pola Pengulangan
1
2
3
4
5
6
7
8
9
10
11
| RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR
→ Setiap Senin, Rabu, dan Jumat
RRULE:FREQ=MONTHLY;BYDAY=1SA
→ Sabtu pertama setiap bulan
RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=SU
→ Setiap Minggu kedua
RRULE:FREQ=DAILY;COUNT=10
→ Harian untuk 10 kejadian
|
Model Data
RecurringParty berfungsi sebagai template yang menghasilkan instansi Party:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| class RecurringParty(models.Model):
name = models.CharField(max_length=255)
# Konfigurasi jadwal
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
max_occurrences = models.PositiveIntegerField(null=True, blank=True)
recurrence = RecurrenceField() # pola RRULE
# Field template (disalin ke setiap instansi)
departure_time = models.TimeField()
arrival_time = models.TimeField()
origin = PointField()
destination = PointField()
route = models.JSONField()
difficulty = models.CharField(choices=DIFFICULTY_CHOICES)
# ... atribut pesta lainnya
is_active = models.BooleanField(default=True)
|
Setiap instansi Party yang dihasilkan terhubung kembali ke induknya:
1
2
3
4
5
6
7
8
9
10
11
12
| class Party(models.Model):
# Field yang ada...
# Dukungan pesta berulang
scheduled_date = models.DateField(null=True, blank=True)
recurring_parent = models.ForeignKey(
RecurringParty,
on_delete=models.SET_NULL,
null=True,
related_name="instances"
)
is_cancelled = models.BooleanField(default=False)
|
Pembuatan Instansi
Ketika pesta berulang dibuat atau jadwal berubah, kami menghasilkan instansi untuk periode mendatang:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| def generate_instances(self, lookahead_days: int = 30) -> list[Party]:
"""Hasilkan instansi pesta untuk kejadian mendatang."""
occurrences = self.recurrence.between(
datetime.now().date(),
datetime.now().date() + timedelta(days=lookahead_days),
)
created = []
for date in occurrences:
# Lewati jika instansi sudah ada
if self.instances.filter(scheduled_date=date).exists():
continue
# Buat instansi pesta baru dari template
party = Party.objects.create(
name=f"{self.name} - {date.strftime('%B %d')}",
scheduled_date=date,
recurring_parent=self,
departure_time=self.departure_time,
arrival_time=self.arrival_time,
origin=self.origin,
destination=self.destination,
route=self.route,
# ... salin field template lainnya
)
created.append(party)
return created
|
Pembuatan Otomatis dengan Celery Beat
Tugas Celery harian menjaga instansi tetap terkini:
1
2
3
4
5
6
7
8
9
| @shared_task
def generate_recurring_party_instances():
"""Hasilkan instansi pesta untuk semua pesta berulang aktif."""
for recurring_party in RecurringParty.objects.filter(is_active=True):
created = recurring_party.generate_instances(lookahead_days=30)
if created:
logger.info(
f"Dihasilkan {len(created)} instansi untuk {recurring_party.name}"
)
|
Jadwal Celery Beat menjalankan ini pada tengah malam:
1
2
3
4
5
6
| CELERY_BEAT_SCHEDULE = {
"generate-recurring-party-instances": {
"task": "party_onbici.apps.party.tasks.generate_recurring_party_instances",
"schedule": crontab(hour=0, minute=0), # Harian pada tengah malam
},
}
|
Antarmuka Pengguna
Membuat Pesta Berulang
Form menggunakan widget django-recurrence untuk membangun pola RRULE:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| class RecurringPartyForm(forms.ModelForm):
recurrence = RecurrenceField(
label="Pola pengulangan",
help_text="Atur kapan pesta ini harus berulang.",
required=True,
)
class Meta:
model = RecurringParty
fields = [
"name", "start_date", "end_date", "recurrence",
"departure_time", "arrival_time",
"origin", "destination", "description",
"difficulty", "gender", "privacy",
]
|
Widget menyediakan antarmuka intuitif untuk pola umum:
- Harian / Mingguan / Bulanan / Tahunan
- Hari-hari tertentu dalam seminggu
- Pola ordinal (pertama, kedua, terakhir)
- Interval kustom (setiap 2 minggu)
- Kondisi akhir (sampai tanggal, setelah N kejadian, atau tidak pernah)
Mengelola Instansi
Penyelenggara dapat melihat semua instansi yang dihasilkan dan:
- Edit instansi individual - Ubah rute atau waktu untuk satu kejadian
- Batalkan instansi - Tandai tanggal tertentu sebagai dibatalkan
- Regenerasi instansi - Trigger pembuatan secara manual untuk 30 hari ke depan
Menangani Kasus Tepi
Kesadaran Zona Waktu
Semua tanggal disimpan dalam zona waktu yang dikonfigurasi penyelenggara dan ditampilkan dalam waktu lokal penonton:
1
2
3
| # Hasilkan instansi menggunakan tanggal sadar zona waktu
local_tz = get_current_timezone()
today = timezone.now().astimezone(local_tz).date()
|
Penanganan Hari Libur dan Pengecualian
Pengguna dapat membatalkan instansi individual tanpa mempengaruhi pola berulang:
1
2
3
| party = recurring_party.instances.get(scheduled_date=holiday_date)
party.is_cancelled = True
party.save()
|
Instansi yang dibatalkan masih muncul dalam daftar dengan indikator visual, mencegah kebingungan.
Instansi Yatim
Jika pesta berulang dihapus, instansi yang dihasilkan dapat secara opsional dipertahankan:
1
2
3
4
5
6
7
8
9
10
11
| def form_valid(self, form):
delete_instances = request.POST.get("delete_instances") == "true"
if delete_instances:
# Soft-delete instansi masa depan
recurring_party.instances.filter(
scheduled_date__gte=date.today(),
deleted_at__isnull=True,
).update(deleted_at=timezone.now())
recurring_party.delete()
|
Pengalaman Pengguna
Membuat perjalanan mingguan hanya membutuhkan beberapa langkah:
- Pergi ke Dashboard → Pesta Berulang → Buat
- Atur detail pesta Anda (rute, waktu, kesulitan)
- Konfigurasi pola pengulangan (mis., “Setiap Sabtu jam 8 pagi”)
- Atur tanggal mulai dan tanggal akhir opsional
- Simpan - instansi dihasilkan secara otomatis!
Sistem membuat instansi pesta 30 hari ke depan, dan Celery terus menghasilkan yang baru seiring waktu berlalu.
Manfaat untuk Komunitas Bersepeda
- Atur dan lupakan - Tidak perlu lagi membuat acara mingguan
- Konsistensi - Rute yang sama, waktu yang sama, pengaturan yang sama
- Fleksibilitas - Edit atau batalkan kejadian individual
- Penemuan - Pengendara dapat dengan mudah menemukan perjalanan grup reguler
- Integrasi kalender - Berlangganan acara berulang di aplikasi kalender Anda
Siap mengatur perjalanan reguler Anda? Buat pesta berulang dan biarkan kami menangani penjadwalannya!