layout: post title: “HHIS Tutor Board Design Revision and Addendum” date: 2025-02-19 categories: tutor-board hhis —

HHIS Tutor Board Design Revision and Addendum

By Xander Van Pelt, Date 19-February-2025

This Document

I’ve made this to address the changes and features which I’ve added or changed from the initial design draft / plan. I’ve included all the main changes and additions to the original plan within this document. The document goes into some technical detail, but is mostly aimed at outlining the changes.

Approaches to achieving the project’s objectives

I had already outlined some clear goals for this project:

  1. It is not meant to replace anything in Google Classroom, the Classroom announcements exist and serve their purpose well, I don’t think I have anything to add to that.
  2. If teachers want to post their own announcements, I don’t want it to be cumbersome to create posts in both Classroom and The ‘Tutor Board’.
  3. My goal is to create a centralized, easy to view and access website, perhaps only hosted on the schools network (won’t be accessible from the ‘world wide web’).
  4. I want this to be a place where students can access all relevant school events and notices for that day.
  5. I want the information to only pertain to that day. Maybe in the future, if there seems to be a need for it, I will add the ability to look at past day’s event boards.

Here’s the methods I have settled on to achieve the project’s objectives:

  1. I’ve created a python script that runs on a cron job (about once every 15 minutes) and fetches google classroom for announcements, upon finding a new one, it checks for the announcement’s suitability to be put automatically onto the notice board, which also helps to meet objective 2. If the announcement is deemed suitable it then gets added to the notice board, or the new ‘upcoming events’ board. The script uses OpenAI’s o1-mini and gpt-4o LLM models to evaluate and rephrase the notices (actually I’ve updated to gpt-4.1 for everything).
  2. The python script removes any need for teachers to post both on classroom and my website, though I allow teachers to edit existing notices, in case they were rephrased poorly by the AI.
  3. Conveniently, by keeping the website on a onrender.com url, essentially a free, development URL, my website doesn’t get scraped by web crawlers and won’t appear on search engines. I’ve considered using geo-blocking or hosting on the school’s local network, but my current approach is simple and works for now.
  4. By creating a mobile and desktop view for my website I believe it’s convenient and easy to access for most students. Also the addition of the weather widget and other boards make the website more useful.
  5. I’ve added a way to view past day boards, called the “time travel” board. It makes it easy and convenient to look at boards from yesterday, the day before yesterday, or even further. This conveniently works by using the deterministic nature of the algorithm I use to pick the random flag and element of the day as well. The current date is also passed in to the URL endpoints of my backend which allows for viewing old notices and other data.

Extra features

Apart from notices which contain the following:

  1. Target for Announcement (KS4, Secondary, Whole School, etc.)
  2. Notice Title
  3. Notice Description
  4. Start date of announcement
  5. End date of announcement

I’ve also found that many announcements that come from classrooms are reminders of events coming up, so I thought a dedicated section for announcements which are classified as events should be made. These contain:

  1. Event name
  2. Short event description
  3. Date of event occurrence

I’ve changed the login system from using “login with google” to creating my own JWT login system, for ease of implementation. The login system works by comparing a user’s username and password with those stored in the database, if it’s a match, then a JWT is signed and sent to the client as a cookie.

As for the other boards / widgets I wanted to include, the following are the ones I’ve ended up having in launch:

  • Upcoming Events
  • HHIS Most Recent Newsletter Viewer
  • Teacher login
  • Daily games (Wordle, Connections, Globble, GeoGuesser)
  • Trivia questions
  • Word of the day in English, French and Spanish!
  • Joke of the day
  • Flag of the day
  • Name the element
  • Weather and Air Quality
  • Canteen food that day (Sort of)
  • Quote of day (provided by each form tutor)

Missing all but 1 planed board:

  • Fascinating / Impactful videos shared by teachers

In reflection, I think the large number of boards and thus activities to do on the board made it not boring to look at everyday as there is always something new, I think this helped make it more interesting and made people visit the website more often each day.

UI & UX Considerations

As I addressed in the design outline document I think UX & UI will be vital in ensuring the app is simple, easy to use and easily adopted. I have tried through many considerations in developing this application, to meet these UI/UX requirements.

I implemented a dynamically resizable grid of widgets that collapse into a single column when viewed on a narrow device, and then into 2x2 or 2x3 segments.

One aspect of the UI design that I’m proud of is the interface buttons. I had initially started by playing around with various CSS button styles I found on a website similar to CSS Buttons. After combining some elements from various button designs and tweaking an animation using a bezier curve by hand, to create a playful and subtle ‘bounce’ effect, where the button shadow and size has a springy rebound effect when clicked on / hovered over. All to create a reactive, interactive experience.

Furthermore, the font choice I think helped the user’s feel more comfortable and familiar with the experience as Nunito is the font used in many of the school’s materials. Including the font used for the HHIS logo.

Lastly, I’ve changed the color of the website to a more plain white, yet with an eggshell hint as to not be so sterile. This helped the website look more modern, straying away from the school logo’s colors but more modern looking.

Here’s the most recent look of the website, with more modern colors and elements. I’ll also include the draft design to show the process of iteration I undertook:

Technical Considerations

As stated in my design outline I’ve adopted the ReactJs framework for this project’s frontend, as its flexible and versatile features will allow me to quickly create this project, given I have some time constraints as a single developer.

However the backend will not use Python with a library like django anymore because I found express.js to be lighterweight, quick and easy to use. Also I had less experience with javascript in general and express.js so I thought it would be a good learning experience either way. Which it very much turned out to be.

My design process for the python script that would fetch classroom notices and announcements evolved drastically over the application’s development. The goal was to create an agentic AI service that would edit, delete and add notices and events to the board all autonomously at regular intervals. My first approach used structured, rigid steps that use LLMs for evaluation stages, rewriting and content filtering. Everything else was handled by code. The responses of these LLMs had to be sanitized and were also inconsistent even with the same prompts and prompting that instructed the IA to respond in a very specific way. I treated the task of updating the Tutor Board very programmatically, step by step, running each announcement that comes through a set series of steps.

The steps that I used in my initial approach are as follows (in flow chart format):

START
├── Database Setup
│ ├── Create backend_key table if not exists
│ ├── Create processed_announcements table if not exists
│ └── Load existing backend key and processed announcements

├── Authentication & Token Management
│ ├── Check if backend token needs refresh (>28 days old)
│ ├── If yes → Request new token from backend
│ └── Authenticate with Google Classroom API (OAuth2)

├── Main Processing Loop
│ ├── Fetch all active courses from Google Classroom
│ └── For each course:
│ ├── Get announcements updated in last 12 hours
│ └── For each recent announcement:
│ ├── Check if already processed (by ID & update time)
│ ├── If new/updated → Process announcement:
│ │ ├── Extract text and materials (files, links, videos)
│ │ ├── AI Relevance Check: Is this suitable for notice board?
│ │ │ └── If not suitable → Skip and mark as processed
│ │ ├── AI Classification: Is this an "event" or "notice"?
│ │ ├── Duplicate Detection:
│ │ │ ├── Query existing notices/events from backend
│ │ │ ├── AI comparison to find duplicates
│ │ │ └── If duplicate → Update targets or skip
│ │ ├── Content Processing:
│ │ │ ├── Determine target year groups (7-13)
│ │ │ ├── AI content condensation and formatting
│ │ │ └── Create structured JSON (title, description, dates)
│ │ └── Backend Operations:
│ │ ├── If new → Create notice/event on backend
│ │ ├── If update → Update existing notice/event
│ │ └── Save processed announcement record
│ └── Continue to next announcement

└── Cleanup
├── Save all processed announcements to database
└── Purge announcement records older than 24 hours

Now the issue with this approach is a lack of flexibility, and accommodation for edge cases or special assignments in the rigid processing pipeline. Each LLM prompt was designed to carry out one specific task (e.g. check appropriateness for school board, decide if an announcement from Google Classroom should be a notice or event on Tutor Board, reword, format, decide title). This meant each LLM prompt was blind to the context and vision of the entire task. Some of the largest flaws with this method were:

  • Inability to link context and relate existing notices with new notices
  • Change notices based on new information that another notice has provided
  • Replace an existing notice with a new one (duplicates would likely be made)

Using the pipeline I have created implementing the features above would have been a very complex and cumbersome undertaking, as numerous conditionals would need to be used. My main.py file was over 1300 lines. The fact is splitting a task into small tasks and allocating these tasks to separate prompts, which don’t have complete context of the rest of the task, the larger scope of the task and moving pieces, or the existing notices, isn’t a very robust and intelligent or elegant solution. The initial design method completely fails to leverage the biggest advantage of LLMs, them being context machines and their ability to reason and plan, instead feeding it simple tasks and asking for an evaluation or structured output, and processing their outputs in code to come to a decision with how to process any given notice.

However with the advent of the ability for AI APIs like ChatGPT to run tool calls and give structured outputs (reducing the need for exhausting sanitization checks of AI text output, and rigorous prompting). The idea of a new solution emerged. A truly agentic AI agent that takes a holistic view of the task and data, is given specific guidelines and has the autonomy to call tools and to do its task. I thought, why am I breaking the task down into if statements and evaluation procedures? Why don’t I just prompt it in one go, give it a task to complete, instructions on how it should be completed well, and the ability to add / edit / delete and view notices? So that’s what I did. This approach essentially addresses all the shortcomings of the initial design and even surpasses it in proficiency. Though to think that this task would have been almost impossible 3 years ago is shocking.

So here’s how my new approach works, it is flexible, reactive, fully context aware and able to reason before planning changes.

The system implements a conversation loop where:

  1. The agent receives the initial prompt with context data
  2. It decides what tools to use based on the data
  3. Each tool call result is appended to the conversation
  4. The latest board state is provided after each action
  5. The loop continues for up to 8 iterations or until the agent calls the finish_processing function

The new prompt engineering approach:

Role Definition: The agent is given a clear identity as a “digital school board assistant for Hua Hin International School”

  1. Contextual Information:
    • Current date and time (for temporal awareness)
    • Google Classroom data (announcements and assignments)
    • Current state of the Tutor Board (existing notices and events)
  2. Task Definition: Clear instructions on what to do with the data
  3. Decision Guidelines: Specific rules for how to:
    • Distinguish between notices and events
    • Format content correctly
    • Determine date ranges
    • Assign year group targets
    • Handle duplicates and outdated content
  4. Tool Usage Instructions: How to interact with the backend systems

The flowchart showing the newer radically different architecture is below:

START
├── Environment Setup
│ ├── Load secrets from JSON file
│ ├── Initialize OpenAI client, ClassroomAPI, and BackendAPI
│ └── Define 11-function toolkit for AI agent

├── Data Collection
│ ├── Fetch announcements from Google Classroom
│ ├── Fetch assignments from Google Classroom
│ └── If no new data → End session immediately

├── Context Preparation
│ ├── Generate comprehensive prompt with:
│ │ ├── Current date/time
│ │ ├── All classroom data (announcements + assignments)
│ │ ├── Current board state (notices + events)
│ │ └── Detailed operational guidelines
│ └── Package into conversation format

├── AI Agent Processing Loop (Max 8 iterations)
│ ├── Send context to GPT-4.1 with tool access
│ ├── Agent analyzes situation and chooses tools
│ ├── For each tool call:
│ │ ├── Execute function via call_function()
│ │ ├── Return results to agent
│ │ ├── Update conversation history
│ │ └── Provide fresh board state for context
│ ├── Agent continues reasoning with new information
│ └── Loop until agent calls finish_processing() or max iterations

├── Available Agent Actions:
│ ├── Information Gathering:
│ │ ├── get_announcements() → Latest classroom announcements
│ │ ├── get_assignments() → Upcoming assignments
│ │ ├── get_existing_notices() → Current notice board
│ │ └── get_existing_events() → Current events board
│ ├── Content Management:
│ │ ├── create_notice(title, desc, dates, targets)
│ │ ├── update_notice(id, title, desc, dates, targets)
│ │ ├── delete_notice(id)
│ │ ├── create_event(name, desc, date)
│ │ ├── update_event(id, name, desc, date)
│ │ └── delete_event(id)
│ └── Session Control:
│ └── finish_processing(message) → End with summary

└── Session Termination
├── Either: Agent-controlled via finish_processing()
├── Or: Force-end after 8 iterations
└── Report runtime statistics and actions taken

Not only was this approach more simple, elegant and left the heavy lifting to a capable LLM model, but it was also far cleaner and simpler to write, maintain and update. The only potential downside being, cost, speed and risk in having allowed the AI model to perform unaudited changes to the board.

During the soft roll out and set up of Tutor Board, one of my teachers that I requested to integrate her classroom into the system had raised a safety and security issue. She was worried that potentially sensitive or private data (student names or passwords, other sensitive information) might be accidentally leaked if put in the classroom. She pointed out that Google Classroom is private and only those who joined can see, but tutor board is a public website. Knowing the responsibility I had in developing a safe and secure application, I took precautions to make sure that no sensitive information was inadvertently shown on Tutor Board by working with prompting instructions to filter out sensitive information.

I emailed my teacher back to inform her of the measures I took, and we moved forward from there.

Here’s some of the prompts I used for Tutor Board. LLM prompting is an extremely iterative task which requires continuous testing, result evaluation, and tweaking. In the end I settled on the following prompt:

After some iterations, here’s the tables I now have on the backend’s database.

1. Notices Table

Column Name Data Type Constraints / Details
id INT AUTO_INCREMENT, PRIMARY KEY
title VARCHAR(255) NOT NULL
description TEXT NOT NULL
startDate DATE NOT NULL
endDate DATE NOT NULL
target JSON (Optional)
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP

2. Quotes Table

Column Name Data Type Constraints / Details
id INT AUTO_INCREMENT, PRIMARY KEY
class VARCHAR(255) NOT NULL
quote VARCHAR(255) NOT NULL
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP

3. Upcoming Events Table

Column Name Data Type Constraints / Details
id INT AUTO_INCREMENT, PRIMARY KEY
name VARCHAR(255) NOT NULL
description TEXT NOT NULL
date DATE NOT NULL

4. Users Table

Column Name Data Type Constraints / Details
id INT AUTO_INCREMENT, PRIMARY KEY
name VARCHAR(255) NOT NULL, UNIQUE
password VARCHAR(255) NOT NULL

The development of the final database schema emerged through an iterative process of optimizing data requirements. A particularly effective implementation was the integration of date-based filtering in the SQL queries. By passing the date as a parameter, the system could efficiently retrieve relevant notices based on temporal constraints. This parameterization served a dual purpose: it enabled precise database querying and supported a flexible front-end interface where users could view notice boards from different time periods.

Here’s the core implementation of the notice retrieval endpoint:

server.get('/api/notices', (req, res) => {

const { currentDate } = req.query;

if (!currentDate) {
return res.status(400).json({ message: 'Missing current date query parameter.' });
}

const currentDateObj = new Date(currentDate);
if (isNaN(currentDateObj.getTime())) {
return res.status(400).json({ message: 'Invalid current date provided.' });
}

const query = `
SELECT * FROM notices
WHERE ? BETWEEN startDate AND endDate
`;

db.query(query, [currentDate], (err, results) => {
if (err) {
console.error('Error fetching notices:', err);
return res.status(500).json({ message: 'Error fetching notices' });
}

`res.status(200).json(results);`     `});`   `});`

Revised Cost Estimation

The project cost was very similar to what I predicted at the beginning of the product which was 23 USD / month, I arrived on this number from the following estimate sheet:

Estimated Costs:

Service Provider Cost ( USD / month )
MySQL server hosting Digital Ocean 15
Web Service Backend hosting Render.com 7
OpenAI API OpenAI 1
    TOTAL: 23 USD / Month (750 THB)

But in reality here are the costs of running this project:

Service Provider Cost ( USD / month )
MySQL server hosting DigitalOcean 15 (However is free because of $200 DigitalOcean student credit)
Web Service Backend hosting Render.com 7 (Is also $0 as I have opted for the free instance of the web service hosting, that ‘spins up’ after long periods of inactivity)
Python Google Classroom Scraper Running on a Cron Job Render.com 1
OpenAI API OpenAI 2
    TOTAL: 25 USD / Month (840 THB)

I think my cost projections were pretty much spot on, and furthermore, the expansion or adding of features to my app should very minimally affect cost, meaning regardless of reasonable size or scope expected for this project, my costs will remain the same. I think overall I was quite accurate in my cost predictions, and this is important as it ensures I will be able to sustain my project.

Testing and Feedback Reflection

Testing and feedback is crucial for this project. On launch, I decided to give the application URL and login details to all the teachers in secondary school at once, for a grand launch of sorts. I instructed them to email me with feedback or bug reports, but after about 3 weeks of release, I have yet to receive anything but praise for the project, especially from Miss Kahleigh, who praised my website’s selection of games that she cited were useful for her class and helped her create other classroom activities. I had expected the feedback system to be more crucial for my website, however given the surprisingly small number of bugs in the code, as I had done rigorous testing of each feature and bit of functionality throughout development, the feedback wasn’t as important.

One of the last changes I made was adding a landing screen for new users, it reads the following:

Welcome to HHIS Tutor Board!

What is this application for?

I intended this app to be used during class tutor times between 8:30 and 8:45. I tried to include all the things I commonly did in the mornings when I had tutor time, for example playing games like Wordle, Contexto and Geoguessr, which are now quick button presses from this app.

However, an integral part I think that was missing from tutor time was a centralized place for school announcements, which led me to create this app.

How does this compare to Google Classroom?

I found that Google Classroom didn’t have a centralized view for all my announcements; you had to go to each class and scroll to find announcements. So I’ve linked them all to my notice board here, all accessible in one place. Any announcement you make on Classroom should appear on this app within 10 minutes, given you’ve posted it between 7:00 and 18:00. Any edits you make will also be reflected on the website, given that you’ve made the edit less than 12 h after first posting the announcement.

Apart from the notices, this app also provides games and a platform for tutor groups to collaborate or compete. If you ever find a notice that is perhaps inaccurate please edit it to make it clearer for others, after signing in of course. There’s no need to post your own notices here, unless you won’t post them on classroom, as they will automatically get added.

What else does this application have to offer?

There are a few widgets so far, like Guess the Flag, Guess the Element, and Word of the Day, which are meant to be played with the entire class. Because some people are a little too good with flags, I’ve added a blur slider for extra challenge!

Also note the board looks the same for everyone on any given day. Right now, I’m looking for ideas on what widgets I can add, so if you have an idea or proposal, don’t hesitate to send me an email.

Last message

I’ve spent quite some time working on it and hope you all find use from it, if you find it’s missing something let me know!

- Xander

Quiz Making Reflection

The last board I added to my website before its release was my tutor group trivia questions, where each tutor group competed with the same set of questions each day of the week. I wanted the questions to be school related, to have a tie with the school community and its teachers or history. I also wanted some questions to be purposely niche, in the hopes of one child perhaps going “wait, I know this one!” in the middle of the classroom. So I came up with categories of questions that I aimed to have about 10 questions in. After drafting various questions it came time to find the answers through some primary research. So in my free time I went around asking for teacher birthdays, favorite bands, foods, measuring white boards and chair heights. In the end I feel like I landed on a unique set of 15 questions for each of the 5 days of the week. The competition itself was a big success with the final leaderboard looking like so (Miss Kaleighs class takes the win!):

A revision of my planned future features

After reaching the launch milestone of my application, I have decided to review and extend the ambitions I had set in the future features list to the following:

Planned Future Features:

  1. Customizable User Interface
    • Allow tutors to personalize their home views
    • Customize background images, color themes, and button styles
    • Configure which boards to display and their layout
  2. Seasonal Themes
    • Implement special holiday-themed interfaces
    • Include themes for Christmas, Halloween, and Loy Kratong
    • Customize backgrounds and button styles for each theme
  3. Enhanced Notice System
    • Add ability to pin important notices at the top
    • Enable student reactions and voting on shared quotes
    • Support vote-based discussion boards for debate questions
    • Create a dedicated student announcement board for sharing projects and initiatives
  4. Interactive Teaching Tools
    • Implement teacher-hosted boards for daily activities
    • Example: Math question of the day with class submission functionality

Google Classroom Announcements Scraper Improvements:

  1. Notice Update System Current Issue: The system creates new notices instead of updating existing ones when teachers create a second, addendum announcement. Example Scenario:
    • Original: “Meeting at 3:15 today”
    • Update: “Meeting changed to 2:15”
    • Current Behavior: Creates two separate notices
    • Desired Behavior: Update the original notice to reflect the new time
  2. AI-Powered Content Processing (the benefit of the below is a more flexible and autonomous, even robust solution):
    • Use AI to analyze and categorize incoming announcements
    • Implement tool-based approach with the following capabilities:
      • Monitor current events and announcements
      • Create or edit notices
      • Create or edit events
      • Handle complex cases (e.g., multiple events in one announcement)
    • Process Flow:
      • AI receives announcement
      • Analyzes content and context
      • Determines appropriate action (create/edit notice or event)
      • Executes chosen action using available tools