Added guide, and cleaned up handling of students who've moved.
This commit is contained in:
parent
84d4f0abbe
commit
0a0bbe22c9
api/source/api_modules/classroom_compliance
app/src
api
apps/classroom_compliance
|
@ -44,6 +44,7 @@ struct EntriesTableEntry {
|
|||
|
||||
struct EntriesTableStudentResponse {
|
||||
ulong id;
|
||||
ulong classId;
|
||||
string name;
|
||||
ushort deskNumber;
|
||||
bool removed;
|
||||
|
@ -53,6 +54,7 @@ struct EntriesTableStudentResponse {
|
|||
JSONValue toJsonObj() const {
|
||||
JSONValue obj = JSONValue.emptyObject;
|
||||
obj.object["id"] = JSONValue(id);
|
||||
obj.object["classId"] = JSONValue(classId);
|
||||
obj.object["name"] = JSONValue(name);
|
||||
obj.object["deskNumber"] = JSONValue(deskNumber);
|
||||
obj.object["removed"] = JSONValue(removed);
|
||||
|
@ -107,6 +109,7 @@ void getEntries(ref HttpRequestContext ctx) {
|
|||
);
|
||||
EntriesTableStudentResponse[] studentObjects = students.map!(s => EntriesTableStudentResponse(
|
||||
s.id,
|
||||
s.classId,
|
||||
s.name,
|
||||
s.deskNumber,
|
||||
s.removed,
|
||||
|
@ -126,7 +129,8 @@ void getEntries(ref HttpRequestContext ctx) {
|
|||
student.id,
|
||||
student.name,
|
||||
student.desk_number,
|
||||
student.removed
|
||||
student.removed,
|
||||
student.class_id
|
||||
FROM classroom_compliance_entry entry
|
||||
LEFT JOIN classroom_compliance_student student
|
||||
ON student.id = entry.student_id
|
||||
|
@ -166,7 +170,7 @@ void getEntries(ref HttpRequestContext ctx) {
|
|||
ClassroomComplianceStudent student = ClassroomComplianceStudent(
|
||||
r.getUlong(8),
|
||||
r.getString(9),
|
||||
cls.id,
|
||||
r.getUlong(12),
|
||||
r.getUshort(10),
|
||||
r.getBoolean(11)
|
||||
);
|
||||
|
@ -187,6 +191,7 @@ void getEntries(ref HttpRequestContext ctx) {
|
|||
// Their data should still be shown, so add the student here.
|
||||
studentObjects ~= EntriesTableStudentResponse(
|
||||
student.id,
|
||||
student.classId,
|
||||
student.name,
|
||||
student.deskNumber,
|
||||
student.removed,
|
||||
|
|
|
@ -218,6 +218,7 @@ void getStudentOverview(ref HttpRequestContext ctx) {
|
|||
|
||||
void moveStudentToOtherClass(ref HttpRequestContext ctx) {
|
||||
Connection conn = getDb();
|
||||
conn.setAutoCommit(false);
|
||||
User user = getUserOrThrow(ctx, conn);
|
||||
auto student = getStudentOrThrow(ctx, conn, user);
|
||||
struct Payload {
|
||||
|
@ -238,11 +239,24 @@ void moveStudentToOtherClass(ref HttpRequestContext ctx) {
|
|||
ctx.response.writeBodyString("Invalid class was selected.");
|
||||
return;
|
||||
}
|
||||
// Check that the new class doesn't already have a student with the same name.
|
||||
bool studentNameExistsInNewClass = recordExists(
|
||||
conn,
|
||||
"SELECT id FROM classroom_compliance_student WHERE class_id = ? AND name = ?",
|
||||
payload.classId,
|
||||
student.name
|
||||
);
|
||||
if (studentNameExistsInNewClass) {
|
||||
ctx.response.status = HttpStatus.BAD_REQUEST;
|
||||
ctx.response.writeBodyString("A student in the selected class has the same name as this one.");
|
||||
return;
|
||||
}
|
||||
// All good, so update the student's class to the desired one, and reset their desk.
|
||||
update(
|
||||
conn,
|
||||
"UPDATE classroom_compliance_student SET class_id = ?, desk_number = 0 WHERE id = ?",
|
||||
payload.classId, student.id
|
||||
);
|
||||
conn.commit();
|
||||
// We just return 200 OK, no response body.
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ export function getDefaultEntry(dateStr: string): Entry {
|
|||
|
||||
export interface EntriesResponseStudent {
|
||||
id: number
|
||||
classId: number
|
||||
name: string
|
||||
deskNumber: number
|
||||
removed: boolean
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
<script setup lang="ts">
|
||||
import { EMOJI_ABSENT, EMOJI_BEHAVIOR_GOOD, EMOJI_BEHAVIOR_MEDIOCRE, EMOJI_BEHAVIOR_POOR, EMOJI_PHONE_COMPLIANT, EMOJI_PHONE_NONCOMPLIANT, EMOJI_PRESENT, type Entry } from '@/api/classroom_compliance';
|
||||
import EntryTableCell from './entries_table/EntryTableCell.vue';
|
||||
import { ref, type Ref } from 'vue';
|
||||
|
||||
const sampleEntry: Ref<Entry | null> = ref({
|
||||
id: 1,
|
||||
date: '2025-01-01',
|
||||
createdAt: new Date().getTime(),
|
||||
absent: false,
|
||||
phoneCompliant: true,
|
||||
behaviorRating: 3,
|
||||
comment: ''
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="guide-page">
|
||||
<h2>Guide</h2>
|
||||
<p>
|
||||
This page provides a quick guide to using the <em>Classroom Compliance</em>
|
||||
application.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
<h3>Classes</h3>
|
||||
<p>
|
||||
In <em>Classroom Compliance</em>, all your students' data is linked to a
|
||||
particular class. You'll start by clicking <strong>Add Class</strong> for
|
||||
each class you teach. It doesn't really matter if your school uses a block
|
||||
schedule or daily period schedule; each class just needs a number and
|
||||
school year to identify it.
|
||||
</p>
|
||||
<p>
|
||||
You can view all your classes with the <RouterLink to="/classroom-compliance">View My Classes</RouterLink>
|
||||
link at the top of this page. Click on any of the classes you see listed,
|
||||
and you'll go to that class' page.
|
||||
</p>
|
||||
|
||||
<h4 id="the-class-page">The Class Page</h4>
|
||||
<p>
|
||||
This is the main page you'll be working with in <em>Classroom Compliance</em>.
|
||||
Here, you'll find actions to add / remove / edit the list of students in
|
||||
the class, add notes to the class, and most importantly, record students'
|
||||
attendance, behavior, and phone compliance.
|
||||
</p>
|
||||
<p>
|
||||
The following actions are available here:
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>Add Student</strong> - Add a student to the class.</li>
|
||||
<li><strong>Import Students</strong> - Import a list of students in bulk. This is useful when you've just created
|
||||
a class, and you want to copy and paste a list of student names from a spreadsheet.</li>
|
||||
<li><strong>Clear Assigned Desks</strong> - Resets every student's assigned desk. Useful if you've just removed
|
||||
assigned seats or are reshuffling everyone.</li>
|
||||
<li><strong>Delete this Class</strong> - Pretty self-explanatory. This will permanently delete this class. This
|
||||
CANNOT be undone, so be careful to only delete the class when you're sure you won't need it anymore.</li>
|
||||
</ul>
|
||||
|
||||
<h5>Notes</h5>
|
||||
<p>
|
||||
Sometimes, you might want to write a note to remind yourself of something for a class. Write a note in the text
|
||||
box, then click <strong>Add Note</strong> to add the note.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
<h3>Entries</h3>
|
||||
<p>
|
||||
The main point of <em>Classroom Compliance</em> is to make it easy to keep track of each student's behavior,
|
||||
attendance, and phone usage. On each class' page, you'll find a large table with a row for each student, and a
|
||||
column for each day of the week. This is the <strong>Entries Table</strong>.
|
||||
</p>
|
||||
<p>
|
||||
Simply click the "+" icon to add an entry for a student, or click on the day's "+" icon to add an entry for each
|
||||
student that day. Then, just click the emoji to edit the entry. A sample entry is included below for you to play
|
||||
around with.
|
||||
</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<EntryTableCell date-str="2025-01-01" :last-save-state-timestamp="0" v-model="sampleEntry"
|
||||
style="min-width: 150px" />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<ul>
|
||||
<li>Attendance: {{ EMOJI_PRESENT }} for present, and {{ EMOJI_ABSENT }} for absent.</li>
|
||||
<li>Phone Compliance: {{ EMOJI_PHONE_COMPLIANT }} for compliant, and {{ EMOJI_PHONE_NONCOMPLIANT }} for
|
||||
non-compliant.</li>
|
||||
<li>Behavior: {{ EMOJI_BEHAVIOR_GOOD }} for good behavior, {{ EMOJI_BEHAVIOR_MEDIOCRE }} for mediocre, and {{
|
||||
EMOJI_BEHAVIOR_POOR }} for poor.</li>
|
||||
<li>If you'd like to add a comment, click 💬 and enter your comment in the popup.</li>
|
||||
<li>Click the 🗑️ to remove the entry.</li>
|
||||
<li><em>Note that if a student is absent, you can't add any phone or behavior information.</em></li>
|
||||
</ul>
|
||||
<p>
|
||||
When editing, changed entries are highlighted, and you need to click <strong>Save</strong> or <strong>Discard
|
||||
Edits</strong> to save or discard your changes, respectively.
|
||||
</p>
|
||||
|
||||
<h4>Table Views</h4>
|
||||
<p>
|
||||
Above the Entries Table, there's a selection of <em>views</em> to choose from. By default, <strong>Full
|
||||
View</strong> is selected.
|
||||
</p>
|
||||
<ul>
|
||||
<li><strong>Full View</strong> - Shows a full week's entries for all students, including their scores.</li>
|
||||
<li><strong>Grading View</strong> - Only show students and their scores. Useful when transferring grades to
|
||||
another system.</li>
|
||||
<li><strong>Whiteboard View</strong> - Only shows student names and their assigned desks. Useful for showing to
|
||||
your class so they know their assigned seats.</li>
|
||||
<li><strong>Today View</strong> - Same as the Full View, but only shows today, which can be useful when entering
|
||||
data for a class.</li>
|
||||
</ul>
|
||||
|
||||
<h4>Scores</h4>
|
||||
<p>
|
||||
Students' scores are calculated per week. The calculation is shown below:
|
||||
</p>
|
||||
<p>
|
||||
<code>phone_score * 0.3 + behavior_score * 0.7</code> where
|
||||
</p>
|
||||
<ul>
|
||||
<li><code>phone_score = days_compliant / days_present</code></li>
|
||||
<li><code>behavior_score = (good_days * 1.0 + mediocre_days * 0.5) / days_present</code></li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h3>Adding / Editing Students</h3>
|
||||
<p>
|
||||
As mentioned briefly in <a href="#the-class-page">The Class Page</a>, you can add students one-by-one, or in bulk.
|
||||
</p>
|
||||
<p>
|
||||
When adding a student individually, or editing an existing student, here's what you need to know:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Each student in a class needs a <strong>unique</strong> name. Unless you have twins with the same first name,
|
||||
this shouldn't be an issue.</li>
|
||||
<li>If a student has an assigned desk number, you can set it. Set the desk number to 0 if there's no assigned
|
||||
seating for the student.</li>
|
||||
<li>To preserve a record of students who have since been removed from a class, you can check the
|
||||
<strong>Removed</strong> checkbox to indicate that the student is no longer in the class. Their old data will
|
||||
still show up, but you can't add any new entries for them.
|
||||
</li>
|
||||
<li>
|
||||
The <strong>Move to another class</strong> button will, as its name implies, move the student to another one of
|
||||
your classes.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
.guide-page {
|
||||
max-width: 50ch;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { BASE_URL, EMOJI_ABSENT, EMOJI_BEHAVIOR_GOOD, EMOJI_BEHAVIOR_MEDIOCRE, EMOJI_BEHAVIOR_POOR, EMOJI_PHONE_COMPLIANT, EMOJI_PHONE_NONCOMPLIANT, EMOJI_PRESENT } from '@/api/classroom_compliance';
|
||||
import { BASE_URL } from '@/api/classroom_compliance';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
|
||||
const authStore = useAuthStore()
|
||||
|
@ -23,24 +23,14 @@ async function downloadExport() {
|
|||
<template>
|
||||
<main>
|
||||
<h1>Classroom Compliance</h1>
|
||||
<p>
|
||||
With this application, you can track each student's compliance to various classroom policies,
|
||||
like:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Attendance: {{ EMOJI_PRESENT }} - Present, {{ EMOJI_ABSENT }} - Absent</li>
|
||||
<li>Phone Usage (or lack thereof): {{ EMOJI_PHONE_COMPLIANT }} - Compliant, {{ EMOJI_PHONE_NONCOMPLIANT }} -
|
||||
Noncompliant</li>
|
||||
<li>Behavior: {{ EMOJI_BEHAVIOR_GOOD }} - Good, {{ EMOJI_BEHAVIOR_MEDIOCRE }} - Mediocre, {{ EMOJI_BEHAVIOR_POOR
|
||||
}} - Poor</li>
|
||||
</ul>
|
||||
|
||||
<div class="button-bar">
|
||||
<RouterLink to="/classroom-compliance">View My Classes</RouterLink>
|
||||
<RouterLink to="/classroom-compliance/guide">Guide</RouterLink>
|
||||
<a @click.prevent="downloadExport()" href="#">Export All Data</a>
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
<RouterView />
|
||||
<RouterView :key="$route.fullPath" />
|
||||
</main>
|
||||
</template>
|
||||
|
|
|
@ -2,14 +2,18 @@
|
|||
import type { EntriesResponseStudent } from '@/api/classroom_compliance';
|
||||
defineProps<{
|
||||
student: EntriesResponseStudent,
|
||||
classId: number
|
||||
classId: number,
|
||||
}>()
|
||||
</script>
|
||||
<template>
|
||||
<td class="student-name-cell" :class="{ 'student-removed': student.removed }">
|
||||
<RouterLink :to="'/classroom-compliance/classes/' + classId + '/students/' + student.id" class="student-link">
|
||||
<RouterLink :to="'/classroom-compliance/classes/' + student.classId + '/students/' + student.id"
|
||||
class="student-link">
|
||||
{{ student.name }}
|
||||
</RouterLink>
|
||||
<div v-if="classId !== student.classId" class="other-class-text">
|
||||
<RouterLink :to="'/classroom-compliance/classes/' + student.classId">In another class</RouterLink>
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
<style scoped>
|
||||
|
@ -31,4 +35,17 @@ defineProps<{
|
|||
text-decoration: line-through;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.other-class-text {
|
||||
font-size: x-small;
|
||||
}
|
||||
|
||||
.other-class-text>a {
|
||||
color: gray;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.other-class-text>a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -35,6 +35,10 @@ export function createClassroomComplianceRoutes(): RouteRecordRaw {
|
|||
component: () => import('@/apps/classroom_compliance/ImportStudentsView.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: 'guide',
|
||||
component: () => import('@/apps/classroom_compliance/GuideView.vue'),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue