Skip to content

Commit 029e882

Browse files
Merge branch 'views'
2 parents b2b322f + 823f17e commit 029e882

File tree

11 files changed

+343
-20
lines changed

11 files changed

+343
-20
lines changed

config/urls.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from django.urls import path
88
from django.views import defaults as default_views
99
from django.views.generic import TemplateView
10+
from thoughtswap.thoughtswap import views
11+
1012

1113
urlpatterns = [
1214
path("", TemplateView.as_view(template_name="pages/home.html"), name="home"),
@@ -27,6 +29,9 @@
2729
# ...
2830
# Media files
2931
*static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT),
32+
path('prompt-bank/', views.prompt_bank_view, name='prompt_bank'),
33+
path('add-prompt-to-bank/', views.add_prompt_to_bank, name='add_prompt_to_bank'),
34+
3035
]
3136

3237

docs/thoughtswap.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# ThoughtSwap
2+
3+
## Project Overview
4+
5+
Thought Swap is a discussion facilitation app where a facilitator sends prompts to participants, who submit anonymous responses that are later redistributed for small-group discussion.
6+
7+
**Github repository:** https://github.com/Lab-Lab-Lab/ThoughtSwap
8+
9+
**Key technologies:**
10+
- Django (backend)
11+
- Django Channels (WebSocket communication)
12+
- HTML, JavaScript (frontend templates)
13+
14+
**Core features:**
15+
- Prompt creation, management, and reuse
16+
- Anonymous response submission
17+
- Response “swap” feature for small-group discussion
18+
- Facilitator dashboard with course and session controls
19+
20+
## What Has Been Accomplished
21+
22+
- Set up Django backend and database models
23+
- Course, Enrollment, Prompt, Session, PromptUse, Thought
24+
- Integrated WebSocket support (channels) for real-time updates to both the facilitator and participant
25+
- Facilitators can switch their course from three states: Active, Draft, and Inactive
26+
- Facilitators can create prompts ahead of time
27+
- Facilitators can send prompts to participants to respond to
28+
- Facilitators can disperse responses using the “swap” feature
29+
- Participants can join a new course via a room code
30+
- Participants can view the current prompt
31+
- Participants can respond to the current prompt
32+
- Participants can receive a thought to discuss
33+
34+
## Key Decisions Made (with Explanations)
35+
36+
- **What is PromptUse**
37+
The `PromptUse` model acts as a connection between a prompt and a session. It allows the same prompt to be reused across multiple sessions while keeping track of responses specific to each session. This design improves flexibility by avoiding duplicate prompt entries and enables better tracking of when and where prompts were used.
38+
39+
- **Why there are different HTML templates**
40+
I separated the HTML templates into `facilitator_session.html`, `participant_session.html`, and `teacher_dashboard.html` to clearly divide the interfaces by user role. Each role has very different needs:
41+
42+
- **Intended functionality of restrictions around swapping**
43+
When swapping responses, the system should ensure that:
44+
- Each participant receives a response from someone else (not their own)
45+
- There must be at least two active student who have responded
46+
- If there are more students than responses, responses are randomly duplicated
47+
48+
49+
## Known Issues or Bugs
50+
51+
- Swapping thoughts needs to be more robust
52+
- Late joiners need to be fully addressed
53+
- Saftey and robustness of user contributions has not been fully tested
54+
55+
## Next Steps
56+
57+
- Front-end styling + UX
58+
- Facilitator's ability to create a new course
59+
- Facilitator's ability to create a new session
60+
- “Demo mode,” where the facilitator and/or participants need not create a lasting account
61+
- Functionality for late joiners
62+
- When swapping: if one author is more prolific for a certain prompt, before assigning all their thoughts to others, first ensure that each author's thoughts are assigned (and specifically to authors (users who have submitted for this prompt))
63+
- Somehow allow for the participants indicate their thoguhts about the ditributed thought
64+
- Start having semantic analysis
65+
- Find some way to track how the discussion went (Form for after the discussion?)
66+
- Participant view for the facilitator
67+
- Offer rich text editor in prompt composition and thought composition
68+
69+
## Important Files / Code to Know
70+
71+
- `facilitator_session.html`
72+
This is the main interface for facilitators. It allows them to:
73+
- Write and send prompts
74+
- Swap anonymous participant responses
75+
- Access the prompt bank
76+
- View active and past prompts
77+
It also includes WebSocket logic to handle live updates from the server.
78+
79+
- `participant_session.html`
80+
This is the participant view during a session. Participants can:
81+
- See the current prompt
82+
- Submit their responses
83+
- Receive a swapped response to discuss
84+
It connects to the WebSocket server to listen for prompt updates and swapped thoughts.
85+
86+
- `teacher_dashboard.html`
87+
This is the dashboard where teachers manage their courses and sessions.
88+
- Shows all enrolled courses
89+
- Lets the teacher switch session states (Active, Inactive, Draft)
90+
- Displays and manages the prompt bank, including adding new prompts
91+
92+
- `models.py`
93+
Contains all the Django data models, including:
94+
- Course, Enrollment, Prompt, Session, PromptUse, Thought
95+
96+
- `views.py`
97+
Contains the Django views that:
98+
- Handle requests and render the pages
99+
- Manage course state changes
100+
- Provide data to the templates
101+
102+
- `consumers.py`
103+
Contains the Django Channels consumers that:
104+
- Handle WebSocket connections
105+
- Receive and send real-time events like new prompts, new thoughts, swaps, and prompt bank data
106+

local.sqlite3

360 KB
Binary file not shown.

thoughtswap/templates/thoughtswap/facilitator_session.html

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ <h2>Facilitator</h2>
66
<textarea id="promptText" placeholder="Write a prompt..."></textarea>
77
<button onclick="sendPrompt()">Send Prompt</button>
88
<button onclick="sendSwap()">Swap Responses</button>
9+
<button onclick="promptBank()">Prompt Bank</button>
10+
11+
<div id="promptBankModal">
12+
<ul id="promptBankList"></ul>
13+
<button onclick="closePromptBank()">Close</button>
14+
</div>
15+
16+
<div id="modalBackdrop"
17+
18+
onclick="closePromptBank()">
19+
</div>
20+
921

1022
<hr>
1123

@@ -21,7 +33,7 @@ <h4 id="activePromptText">Loading prompt...</h4>
2133
<script>
2234
const socket = new WebSocket("ws://" + window.location.host + "/ws/thoughtswap/{{ course.join_code }}/");
2335

24-
const prompts = {{prompts|safe}};
36+
const prompts = {{ prompts| safe}};
2537
const promptIds = Object.keys(prompts);
2638
let activePromptId = promptIds[promptIds.length - 1];
2739

@@ -77,6 +89,9 @@ <h4 id="activePromptText">Loading prompt...</h4>
7789
}
7890

7991
if (data.type === "new_thought") {
92+
console.log("Received new thought:", data);
93+
const pid = data.prompt_id;
94+
console.log("Prompt ID:", pid);
8095
if (!prompts[promptId]) {
8196
prompts[promptId] = {
8297
content: "(Unknown prompt)",
@@ -91,6 +106,32 @@ <h4 id="activePromptText">Loading prompt...</h4>
91106
renderActivePrompt(promptId);
92107
}
93108
}
109+
110+
if (data.type === "prompt_bank_data") {
111+
const bankList = document.getElementById("promptBankList");
112+
bankList.innerHTML = "";
113+
114+
const sortedPrompts = data.prompts.sort((a, b) => b.id - a.id);
115+
console.log("sorted prompts", sortedPrompts);
116+
117+
if (sortedPrompts.length === 0) {
118+
const li = document.createElement("li");
119+
li.innerText = "No prompts available.";
120+
bankList.appendChild(li);
121+
} else {
122+
sortedPrompts.forEach(prompt => {
123+
const li = document.createElement("li");
124+
li.innerText = prompt.content;
125+
const btn = document.createElement("button");
126+
btn.innerText = "Send Prompt";
127+
btn.onclick = () => sendPromptFromBank(prompt.id);
128+
li.appendChild(btn);
129+
bankList.appendChild(li);
130+
});
131+
}
132+
133+
openPromptBank();
134+
}
94135
};
95136

96137
function sendPrompt() {
@@ -109,8 +150,33 @@ <h4 id="activePromptText">Loading prompt...</h4>
109150
type: "swap_responses"
110151
}));
111152
}
153+
function promptBank() {
154+
console.log("Requesting prompt bank");
155+
socket.send(JSON.stringify({
156+
type: "prompt_bank"
157+
}));
158+
}
159+
160+
function sendPromptFromBank(promptId) {
161+
socket.send(JSON.stringify({
162+
type: "send_bank_prompt",
163+
prompt_id: promptId
164+
}));
165+
closePromptBank();
166+
}
167+
168+
function openPromptBank() {
169+
document.getElementById("promptBankModal").style.display = "block";
170+
document.getElementById("modalBackdrop").style.display = "block";
171+
}
172+
173+
function closePromptBank() {
174+
document.getElementById("promptBankModal").style.display = "none";
175+
document.getElementById("modalBackdrop").style.display = "none";
176+
}
177+
178+
112179

113-
// Initial render
114180
populatePromptDropdown();
115181
renderActivePrompt(activePromptId);
116182
</script>

thoughtswap/templates/thoughtswap/participant_session.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ <h2>Student</h2>
3232

3333
function submitResponse() {
3434
const content = document.getElementById("responseText").value;
35+
if (content.trim() === "") {
36+
alert("Please enter a response before submitting.");
37+
return;
38+
}
3539
console.log("Sending response:", content);
3640
socket.send(JSON.stringify({
3741
type: "submit_thought",

thoughtswap/templates/thoughtswap/teacher_dashboard.html

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,55 @@ <h3>My Courses</h3>
2424
</li>
2525
{% endfor %}
2626
</ul>
27+
28+
<hr>
29+
30+
<h3>Prompt Bank</h3>
31+
<ul id="promptBankList"></ul>
32+
33+
<h6>Add a New Prompt to Bank</h6>
34+
<textarea id="newPromptText" placeholder="Write a new prompt..."></textarea><br>
35+
<button onclick="addPromptToBank()">Add to Bank</button>
36+
37+
<script>
38+
function loadPromptBank() {
39+
fetch("/prompt-bank/")
40+
.then(response => response.json())
41+
.then(data => {
42+
const bankList = document.getElementById("promptBankList");
43+
bankList.innerHTML = "";
44+
45+
const sortedPrompts = data.prompts.sort((a, b) => b.id - a.id);
46+
47+
sortedPrompts.forEach(prompt => {
48+
const li = document.createElement("li");
49+
li.innerText = prompt.content;
50+
bankList.appendChild(li);
51+
});
52+
});
53+
}
54+
55+
function addPromptToBank() {
56+
const content = document.getElementById("newPromptText").value;
57+
if (content.trim() === "") return;
58+
59+
fetch("/add-prompt-to-bank/", {
60+
method: "POST",
61+
headers: {
62+
"Content-Type": "application/json",
63+
"X-CSRFToken": "{{ csrf_token }}"
64+
},
65+
body: JSON.stringify({ content: content })
66+
})
67+
.then(response => response.json())
68+
.then(data => {
69+
document.getElementById("newPromptText").value = "";
70+
loadPromptBank();
71+
});
72+
}
73+
74+
window.onload = loadPromptBank;
75+
</script>
76+
77+
2778
{% endblock %}

0 commit comments

Comments
 (0)