Skip to content

Latest commit

 

History

415 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Classi

Classi logo

Classi is a local-first Flutter app for teachers. It stores groups, students, grades, notes, checklists, and material tracking data in encrypted .classi libraries. Automatic backup and restore to a WebDAV server keeps your data safe and portable across devices.

Supported platforms

  • Android
  • macOS
  • Windows
  • Linux

Features

  • SQLCipher-backed Drift database with passphrase setup and recovery key support
  • Adaptive navigation for groups, notes, and settings
  • Groups and students flow, including archive, unarchive, clone, and deletion
  • Batch student creation, WebUntis CSV class-list import, and a live WebUntis connection for importing classes and class lists, and for taking over lesson topics and attendance
  • Grade entry, chart-based grade history, checklist management, note management, and material tracking
  • Grade categories that nest one level deep (e.g. Written holding tests and quizzes, each weighted within it), a grade distribution for every lesson, and a grade round that goes through the class one student at a time, with keyboard entry on desktop
  • Class attendance statistics per group, for the whole year or one term
  • Export of a group as one OpenDocument spreadsheet (.ods) with sheets for grades, attendance, homework, material, and a summary per student
  • Avatar editing powered by avatar_maker, persisted per student in the local database, plus a browser Avatar Designer that lets students build their own avatar and hand you a short code
  • WebDAV backup with automatic upload on lock and automatic restore on startup
  • Configurable light, dark, and system theme
  • Auto-update for desktop platforms (macOS, Windows, Linux) via the updat package
  • English and German translations through easy_localization

Data storage

Classi stores your library in a .classi folder. Library-specific settings such as grade systems, sorting, theme, lock, and WebDAV backup configuration are stored inside that .classi folder too, so they move with the project instead of being kept as global app preferences.

On desktop, the first-run setup requires an explicit folder selection so your data is never silently placed inside an app-private directory. On Android, scoped storage forbids raw file access to folders you pick in shared storage, so libraries are always created in Classi's app-specific storage directory (Android/data/<package>/files/Classi). This directory is removed when the app is uninstalled — configure a WebDAV backup to keep a restorable copy.

Recommended locations:

Platform Recommended folder
Android Fixed to Classi's app storage; use WebDAV backup for portability
macOS (App Store) ~/Documents/Classi or another location outside ~/Library/Containers/
macOS / Windows / Linux Any folder in your home directory or an accessible drive

WebDAV backups

Classi can automatically back up your library to any WebDAV server (e.g. Nextcloud, ownCloud, or a self-hosted server). Configure the server URL, credentials, and remote folder path in Settings → Backups. Once saved:

  • Auto-export uploads a .classi-backup archive whenever Classi locks or switches libraries.
  • Auto-import checks for a newer backup on startup and offers to restore it.

You can also trigger a manual restore from the setup screen by choosing Restore from WebDAV backup.

WebUntis

Not affiliated with Untis. Classi is an independent open-source project and is not affiliated with, endorsed by, or supported by Untis GmbH. "Untis" and "WebUntis" are trademarks of Untis GmbH and are used here only to say which system Classi works with.

The connection uses an interface that Untis does not document for third parties. It can change or stop working at any time, without notice.

Check with your school first. Connecting reads students' names, attendance and lesson topics from your school's WebUntis. Many schools and school authorities only allow approved apps to process student data. Make sure Classi is allowed before you connect it. Classi keeps the data on your device and talks only to your school's WebUntis server.

Classi can connect to your school's WebUntis and read from it. Connect an account under Settings → WebUntis with the server host (e.g. mese.webuntis.com — pasting the whole address works too), the school's login name, and your WebUntis credentials. Once connected:

  • Import courses or classes from the groups screen. A course becomes a group holding exactly its own students, whether that is half a class or students from several classes. Pick a whole class only when the group really is the entire class.
  • Link an existing group from its page, to one of your courses or to a class; the link can be changed or removed there too. Linked groups and linked students carry a small cloud mark.
  • Import students from a linked group: Classi reads the class register of the group's recent lessons. Students already in the group are matched by name and keep their grades, notes, and avatars; they only gain their WebUntis id.
  • Lesson mode works per lesson, like the class register in WebUntis. It opens the lesson that is running now, from the group's timetable and the school's bell times (read from WebUntis), and chips switch between the day's lessons. Attendance, homework and material are kept per lesson, so a group seen in periods 1–2 and 5–6 has two separate records that day.
  • Lesson topic and attendance in lesson mode: for a linked group, lesson mode shows the topic and the absences WebUntis has for exactly this lesson's WebUntis lessons, next to your own, with buttons to take them over into Classi.
  • Swipes as in Untis Mobile: left marks a student absent, right marks them late, with Undo. This only changes Classi.

The connection only reads: Classi never writes anything to WebUntis. Your password is not stored: WebUntis exchanges it once for an app access secret, which is kept in the platform's secure storage alongside the WebDAV password. Server, school, and user name live in the .classi project, so they travel with the library.

Taking over attendance makes the lesson match WebUntis for the students linked to it, excuse and lateness included; students without a WebUntis id keep what you recorded.

Two things depend on your school's WebUntis configuration: the account needs permission to open the class register, and the class needs lessons in the last few weeks for a class list to be read from. Classi says which of the two is missing rather than showing an empty list.

Avatar Designer

The Avatar Designer is a standalone Flutter web app (a second entry point in this repo, lib/avatar_designer/) that students open in a browser at https://classi.openpatch.org/avatar/. They design an avatar with the same avatar_maker customizer used in the app, press Create code, and hand you a short code like AV1-XXXX-XXXX-XX. In Classi, open a student's avatar editor and choose Enter code to load and save it.

The code encodes only the avatar selections (no personal data). It is tied to the avatar_maker version bundled here; a code made with a mismatched version is rejected with a clear message rather than applied incorrectly.

Run it locally:

flutter run -d chrome -t lib/avatar_designer/main.dart

Build for hosting (served at /avatar/ on classi.openpatch.org):

flutter build web --release \
  --target lib/avatar_designer/main.dart \
  --base-href /avatar/ \
  --pwa-strategy=none

Pushes to main that touch the designer are published automatically by the deploy-site.yml workflow (see GitHub Actions).

Website

https://classi.openpatch.org is a static landing page kept in site/ (index.html, styles.css, main.js, CNAME). It is bilingual: German is authored inline and English lives in data-en* attributes that main.js swaps in, defaulting to the browser language and remembering the choice. Direct download links and the version line are filled in at runtime from the GitHub releases API, so the page never has to be edited for a release; without that request every button still falls back to the releases page.

tool/build_site.sh [OUTDIR] [DESIGNER_BUILD_DIR] assembles the deployable tree. It copies the logo and the screenshots from the places that already own them — .github/logo.png, the AppStream screenshots in linux/packaging/screenshots/, and the Play Store phone screenshots — so the site cannot drift from the store listings. Passing the designer build directory places it at OUTDIR/avatar.

Preview it locally:

tool/build_site.sh build/site
python3 -m http.server 8000 --directory build/site

Local development

Linux desktop builds need native packages installed first:

sudo apt-get update
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libssl-dev
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter analyze
flutter test
flutter run -d android
flutter run -d macos
flutter run -d windows
flutter run -d linux

macOS desktop builds require Xcode on a Mac with command-line tools installed.

Release builds

Releases are packaged with Fastforge:

dart pub global activate fastforge
fastforge package --platform android --targets apk
fastforge package --platform linux   --targets appimage
fastforge package --platform macos   --targets dmg
fastforge package --platform windows --targets exe

Or run all platforms at once using the project release config:

fastforge release --name release

Store metadata

fastlane/metadata/android/ holds the store listing — title, descriptions, icon, phone screenshots and one release-notes file per version code (en-US/changelogs/46.txt belongs to the build whose version_code.txt said 46). F-Droid reads this tree straight from the repository; the Play Store gets it from the release.yml workflow via fastlane supply.

The tree is authored for F-Droid, which accepts anything. Google Play does not: release notes are capped at 500 characters, screenshots may not have an alpha channel, and their longest side may be at most twice the shortest — the 1080x2424 phone screenshots here are 1:2.24 and would be rejected. Rather than degrade the F-Droid assets, tool/prepare_play_metadata.py writes a normalised copy that only Play sees:

python3 tool/prepare_play_metadata.py fastlane/metadata/android build/play-metadata

It trims release notes on a bullet boundary, drops the alpha channel and letterboxes screenshots by repeating their edge pixels (so padding continues the app bar instead of adding black bars), and fails loudly on anything hand-written that Play would reject, such as a title over 30 characters.

Because the whole tree is uploaded on every release, the repository is the source of truth: edits made directly in the Play Console are overwritten on the next tag. To see what would be sent without committing anything to Play, add --validate_only true:

fastlane supply --package_name org.openpatch.classi \
  --metadata_path build/play-metadata \
  --skip_upload_aab true --skip_upload_apk true --skip_upload_changelogs true \
  --json_key playstore-credentials.json --validate_only true

(Release notes are skipped there because they attach to a release that only exists once the bundle is uploaded.)

German release notes fall back to de-DE/changelogs/default.txt, since the generated changelog is English only. Add de-DE/changelogs/<version code>.txt to give a release proper German notes.

Contributing

See CONTRIBUTING.md for local setup, pull request expectations, and release hygiene.

GitHub Actions

Five workflows are included:

  • ci.yml — runs on every push to main/master and on pull requests. It installs dependencies, runs Drift code generation, analyzes the code, and executes the test suite.

  • deploy-site.yml — on pushes to main that touch the website or the Avatar Designer, builds the designer, assembles the site with tool/build_site.sh and deploys it to GitHub Pages as classi.openpatch.org — the landing page at /, the designer at /avatar/. Requires Settings → Pages → Source: GitHub Actions to be enabled once, and a CNAME DNS record pointing classi.openpatch.org at openpatch.github.io.

  • build-pr.yml — triggered by posting a slash command as a comment on any pull request. Supported commands:

    • /build android — builds and uploads an APK
    • /build linux — builds and uploads an AppImage
    • /build macos — builds and uploads a DMG
    • /build windows — builds and uploads an EXE installer

    Only the requested platform is built using Fastforge. Once the artifact is uploaded the workflow replies directly on the pull request with a link to download the artifact.

    Android PR builds use the application ID org.openpatch.classi.pr so they can be installed alongside the production app without overwriting it.

  • changelog.yml — on every push to main, regenerates the unreleased section with git-cliff into fastlane/metadata/android/en-US/changelogs/<next version code>.txt and commits it, so the next release already carries its notes for both stores. See Store metadata.

  • release.yml — triggered by version tags (v*). It generates a changelog with git-cliff, commits an updated CHANGELOG.md, builds release artifacts for Android (APK and AAB), Linux (AppImage), macOS (DMG), and Windows (EXE installer) using Fastforge, publishes a GitHub Release with all artifacts attached, and uploads the AAB to the Play Store production track together with the store metadata using fastlane supply.

Development support

Classi is developed with support from Claude Opus 5.

License

Released under the MIT License.

About

A class management app for teachers

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages