Skip to content

Commit d0da6db

Browse files
authored
Merge pull request #25 from pythonkr/feature/session-room-schedule
feat: 세션 시간/공간 개념 추가
2 parents bdbaa7b + da4f7db commit d0da6db

5 files changed

Lines changed: 287 additions & 14 deletions

File tree

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Generated by Django 5.2 on 2025-07-06 14:47
2+
3+
import uuid
4+
5+
import django.db.models.deletion
6+
from django.conf import settings
7+
from django.db import migrations, models
8+
9+
10+
class Migration(migrations.Migration):
11+
dependencies = [
12+
("event", "0003_alter_event_name_alter_event_name_en_and_more"),
13+
("presentation", "0007_historicalpresentation_summary_and_more"),
14+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15+
]
16+
17+
operations = [
18+
migrations.CreateModel(
19+
name="CallForPresentationSchedule",
20+
fields=[
21+
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
22+
("created_at", models.DateTimeField(auto_now_add=True)),
23+
("updated_at", models.DateTimeField(auto_now=True)),
24+
("deleted_at", models.DateTimeField(blank=True, null=True)),
25+
("start_at", models.DateTimeField()),
26+
("end_at", models.DateTimeField()),
27+
(
28+
"created_by",
29+
models.ForeignKey(
30+
null=True,
31+
on_delete=django.db.models.deletion.PROTECT,
32+
related_name="%(class)s_created_by",
33+
to=settings.AUTH_USER_MODEL,
34+
),
35+
),
36+
(
37+
"deleted_by",
38+
models.ForeignKey(
39+
null=True,
40+
on_delete=django.db.models.deletion.PROTECT,
41+
related_name="%(class)s_deleted_by",
42+
to=settings.AUTH_USER_MODEL,
43+
),
44+
),
45+
(
46+
"next_call_for_presentation_schedule",
47+
models.ForeignKey(
48+
blank=True,
49+
null=True,
50+
on_delete=django.db.models.deletion.SET_NULL,
51+
to="presentation.callforpresentationschedule",
52+
),
53+
),
54+
(
55+
"presentation",
56+
models.ForeignKey(
57+
blank=True,
58+
null=True,
59+
on_delete=django.db.models.deletion.PROTECT,
60+
related_name="call_for_presentation_schedules",
61+
to="presentation.presentation",
62+
),
63+
),
64+
(
65+
"presentation_type",
66+
models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="presentation.presentationtype"),
67+
),
68+
(
69+
"updated_by",
70+
models.ForeignKey(
71+
null=True,
72+
on_delete=django.db.models.deletion.PROTECT,
73+
related_name="%(class)s_updated_by",
74+
to=settings.AUTH_USER_MODEL,
75+
),
76+
),
77+
],
78+
options={
79+
"abstract": False,
80+
},
81+
),
82+
migrations.CreateModel(
83+
name="Room",
84+
fields=[
85+
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
86+
("created_at", models.DateTimeField(auto_now_add=True)),
87+
("updated_at", models.DateTimeField(auto_now=True)),
88+
("deleted_at", models.DateTimeField(blank=True, null=True)),
89+
("name", models.CharField(max_length=256)),
90+
(
91+
"created_by",
92+
models.ForeignKey(
93+
null=True,
94+
on_delete=django.db.models.deletion.PROTECT,
95+
related_name="%(class)s_created_by",
96+
to=settings.AUTH_USER_MODEL,
97+
),
98+
),
99+
(
100+
"deleted_by",
101+
models.ForeignKey(
102+
null=True,
103+
on_delete=django.db.models.deletion.PROTECT,
104+
related_name="%(class)s_deleted_by",
105+
to=settings.AUTH_USER_MODEL,
106+
),
107+
),
108+
("event", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="event.event")),
109+
(
110+
"updated_by",
111+
models.ForeignKey(
112+
null=True,
113+
on_delete=django.db.models.deletion.PROTECT,
114+
related_name="%(class)s_updated_by",
115+
to=settings.AUTH_USER_MODEL,
116+
),
117+
),
118+
],
119+
options={
120+
"abstract": False,
121+
},
122+
),
123+
migrations.CreateModel(
124+
name="RoomSchedule",
125+
fields=[
126+
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
127+
("created_at", models.DateTimeField(auto_now_add=True)),
128+
("updated_at", models.DateTimeField(auto_now=True)),
129+
("deleted_at", models.DateTimeField(blank=True, null=True)),
130+
("start_at", models.DateTimeField()),
131+
("end_at", models.DateTimeField()),
132+
(
133+
"created_by",
134+
models.ForeignKey(
135+
null=True,
136+
on_delete=django.db.models.deletion.PROTECT,
137+
related_name="%(class)s_created_by",
138+
to=settings.AUTH_USER_MODEL,
139+
),
140+
),
141+
(
142+
"deleted_by",
143+
models.ForeignKey(
144+
null=True,
145+
on_delete=django.db.models.deletion.PROTECT,
146+
related_name="%(class)s_deleted_by",
147+
to=settings.AUTH_USER_MODEL,
148+
),
149+
),
150+
(
151+
"presentation",
152+
models.ForeignKey(
153+
blank=True,
154+
null=True,
155+
on_delete=django.db.models.deletion.PROTECT,
156+
to="presentation.presentation",
157+
),
158+
),
159+
("room", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="presentation.room")),
160+
(
161+
"updated_by",
162+
models.ForeignKey(
163+
null=True,
164+
on_delete=django.db.models.deletion.PROTECT,
165+
related_name="%(class)s_updated_by",
166+
to=settings.AUTH_USER_MODEL,
167+
),
168+
),
169+
],
170+
options={
171+
"abstract": False,
172+
},
173+
),
174+
]

app/event/presentation/models.py

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,31 @@
1111

1212
class PresentationQuerySet(BaseAbstractModelQuerySet):
1313
def get_all_nested_data(self):
14-
return self.filter_active().prefetch_related(
15-
models.Prefetch(
16-
lookup="categories",
17-
queryset=PresentationCategory.objects.filter_active(),
18-
to_attr="_prefetched_active_categories",
19-
),
20-
models.Prefetch(
21-
lookup="speakers",
22-
queryset=PresentationSpeaker.objects.filter_active().select_related("user"),
23-
to_attr="_prefetched_active_speakers",
24-
),
14+
return (
15+
self.filter_active()
16+
.prefetch_related(
17+
models.Prefetch(
18+
lookup="categories",
19+
queryset=PresentationCategory.objects.filter_active(),
20+
to_attr="_prefetched_active_categories",
21+
),
22+
models.Prefetch(
23+
lookup="speakers",
24+
queryset=PresentationSpeaker.objects.filter_active().select_related("user", "image"),
25+
to_attr="_prefetched_active_speakers",
26+
),
27+
models.Prefetch(
28+
lookup="roomschedule_set",
29+
queryset=RoomSchedule.objects.filter_active().select_related("room", "room__event"),
30+
to_attr="_prefetched_active_room_schedules",
31+
),
32+
models.Prefetch(
33+
lookup="call_for_presentation_schedules",
34+
queryset=CallForPresentationSchedule.objects.filter_active().select_related("presentation_type"),
35+
to_attr="_prefetched_active_call_for_presentation_schedules",
36+
),
37+
)
38+
.select_related("image")
2539
)
2640

2741

@@ -83,3 +97,25 @@ class PresentationSpeaker(BaseAbstractModel):
8397
user = models.ForeignKey(User, on_delete=models.PROTECT)
8498
image = models.ForeignKey(PublicFile, on_delete=models.PROTECT, null=True, blank=True)
8599
biography = MarkdownField(blank=True, default="")
100+
101+
102+
class CallForPresentationSchedule(BaseAbstractModel):
103+
presentation_type = models.ForeignKey(PresentationType, on_delete=models.PROTECT)
104+
presentation = models.ForeignKey(
105+
Presentation, on_delete=models.PROTECT, related_name="call_for_presentation_schedules", null=True, blank=True
106+
)
107+
start_at = models.DateTimeField()
108+
end_at = models.DateTimeField()
109+
next_call_for_presentation_schedule = models.ForeignKey("self", on_delete=models.SET_NULL, null=True, blank=True)
110+
111+
112+
class Room(BaseAbstractModel):
113+
event = models.ForeignKey(Event, on_delete=models.PROTECT)
114+
name = models.CharField(max_length=256)
115+
116+
117+
class RoomSchedule(BaseAbstractModel):
118+
room = models.ForeignKey(Room, on_delete=models.PROTECT)
119+
start_at = models.DateTimeField()
120+
end_at = models.DateTimeField()
121+
presentation = models.ForeignKey(Presentation, on_delete=models.PROTECT, null=True, blank=True)

app/event/presentation/serializers.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
from event.presentation.models import Presentation, PresentationCategory, PresentationSpeaker
1+
from event.presentation.models import (
2+
CallForPresentationSchedule,
3+
Presentation,
4+
PresentationCategory,
5+
PresentationSpeaker,
6+
RoomSchedule,
7+
)
28
from rest_framework import serializers
39

410

@@ -17,11 +23,40 @@ class Meta:
1723
fields = ("id", "nickname", "biography", "image")
1824

1925

26+
class RoomScheduleSerializer(serializers.ModelSerializer):
27+
room_name = serializers.CharField(source="room.name", read_only=True)
28+
event_id = serializers.IntegerField(source="room.event.id", read_only=True)
29+
event_name = serializers.CharField(source="room.event.name", read_only=True)
30+
31+
class Meta:
32+
model = RoomSchedule
33+
fields = ("id", "room_name", "event_id", "event_name", "start_at", "end_at")
34+
35+
36+
class CallForPresentationScheduleSerializer(serializers.ModelSerializer):
37+
presentation_type_name = serializers.CharField(source="presentation_type.name", read_only=True)
38+
39+
class Meta:
40+
model = CallForPresentationSchedule
41+
fields = ("id", "presentation_type_name", "start_at", "end_at", "next_call_for_presentation_schedule")
42+
43+
2044
class PresentationSerializer(serializers.ModelSerializer):
2145
image = serializers.FileField(source="image.file", read_only=True, allow_null=True)
2246
categories = PresentationCategorySerializer(many=True, read_only=True)
2347
speakers = PresentationSpeakerSerializer(many=True, read_only=True)
48+
room_schedules = RoomScheduleSerializer(source="room_schedules_set", many=True, read_only=True)
49+
call_for_presentation_schedules = CallForPresentationScheduleSerializer(many=True, read_only=True)
2450

2551
class Meta:
2652
model = Presentation
27-
fields = ("id", "title", "description", "image", "categories", "speakers")
53+
fields = (
54+
"id",
55+
"title",
56+
"description",
57+
"image",
58+
"categories",
59+
"speakers",
60+
"room_schedules",
61+
"call_for_presentation_schedules",
62+
)

app/event/presentation/test/conftest.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import dataclasses
2+
from datetime import datetime, timedelta
23

34
import pytest
45
from event.models import Event
56
from event.presentation.models import (
7+
CallForPresentationSchedule,
68
Presentation,
79
PresentationCategory,
810
PresentationCategoryRelation,
911
PresentationSpeaker,
1012
PresentationType,
13+
Room,
14+
RoomSchedule,
1115
)
1216
from faker import Faker
1317
from model_bakery import baker
@@ -26,6 +30,9 @@ class PresentationTestEntity:
2630
presentation_category: PresentationCategory
2731
presentation_category_relation: PresentationCategoryRelation
2832
presentation_speaker: PresentationSpeaker
33+
room: Room
34+
room_schedule: RoomSchedule
35+
call_for_presentation_schedule: CallForPresentationSchedule
2936

3037

3138
@pytest.fixture
@@ -47,6 +54,8 @@ def create_event(create_user_with_organization_and_relation):
4754
def create_presentation_set(create_event):
4855
fake = Faker()
4956
user, organization, relation, event = create_event
57+
58+
# 기존 데이터 생성
5059
presentation_type = baker.make(PresentationType, event=event)
5160
presentation = baker.make(Presentation, type=presentation_type)
5261
presentation_category = baker.make(PresentationCategory, type=presentation_type, name=fake.name())
@@ -55,6 +64,22 @@ def create_presentation_set(create_event):
5564
)
5665
presentation_speaker = baker.make(PresentationSpeaker, presentation=presentation, user=user)
5766

67+
# Room과 RoomSchedule 데이터 생성
68+
room = baker.make(Room, event=event, name=fake.company())
69+
start_time = datetime.now()
70+
room_schedule = baker.make(
71+
RoomSchedule, room=room, presentation=presentation, start_at=start_time, end_at=start_time + timedelta(hours=1)
72+
)
73+
74+
# CallForPresentationSchedule 데이터 생성
75+
cfp_start = start_time - timedelta(days=30)
76+
call_for_presentation_schedule = baker.make(
77+
CallForPresentationSchedule,
78+
presentation_type=presentation_type,
79+
start_at=cfp_start,
80+
end_at=cfp_start + timedelta(days=14),
81+
)
82+
5883
return PresentationTestEntity(
5984
user=user,
6085
organization=organization,
@@ -64,6 +89,9 @@ def create_presentation_set(create_event):
6489
presentation_category=presentation_category,
6590
presentation_category_relation=presentation_category_relation,
6691
presentation_speaker=presentation_speaker,
92+
room=room,
93+
room_schedule=room_schedule,
94+
call_for_presentation_schedule=call_for_presentation_schedule,
6795
)
6896

6997

app/event/presentation/test/count_queries_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ def test_count_queries(
1010
django_assert_max_num_queries: DjangoAssertNumQueries, create_presentation_set: PresentationTestEntity
1111
):
1212
reset_queries()
13-
with django_assert_max_num_queries(3):
13+
with django_assert_max_num_queries(5):
1414
queryset = Presentation.objects.get_all_nested_data()
1515
list(queryset) # query evaluation

0 commit comments

Comments
 (0)