← Back to Projects

Multi-Tenant Survey Platform

A Flask and Azure platform for running the same structured questionnaire across many organisational units at once — with role-based access, scheduled review rounds, progress tracking, and AI-generated summaries of what came back.

Flask Python Azure Cosmos DB Azure Blob Storage Azure App Service OpenAI GitHub Actions

The problem it solves

Any organisation running a periodic assessment across a lot of separate units hits the same wall. The questionnaire goes out as a spreadsheet, comes back in forty inconsistent versions, and someone spends a fortnight reconciling them by hand. Nobody can see who has finished and who has not. Last cycle's answers are in someone's inbox.

This platform replaces that with one system: a structured questionnaire, per-unit access control, visible completion state, a formal review stage before anything is final, and an audit trail that survives the people who ran the last cycle.

Key Features

Role-Based Access

Authentication with distinct administrator and respondent roles, scoped so each participant sees only their own unit's questionnaire

Admin Portal

User and tenant management with a dashboard covering completion state, review status, and outstanding submissions

Configurable Questionnaires

Section-based questionnaire definitions, so more than one assessment type can run from a single deployment

Progress Tracking

Completion status tracked automatically per section, giving administrators live visibility without chasing anyone

Scheduled Review Rounds

Time-boxed review periods with their own configuration, so submissions are reviewed and returned rather than merely collected

Cosmos DB Storage

Azure Cosmos DB as the NoSQL backing store, partitioned so tenants scale independently of one another

AI-Generated Summaries

OpenAI integration turns raw responses into readable per-unit summary documents, removing the manual synthesis step

Document Management

Supporting files stored in Azure Blob Storage with categorisation and permissions enforced at user and role level

Automated Deployment

GitHub Actions pipeline deploying to Azure App Service, so releases are repeatable rather than hand-run

Technical Architecture

Flask with modular blueprints separating authentication, questionnaire delivery, and administration. Cosmos DB holds responses and user records; Blob Storage holds uploaded documents and serves them through time-limited signed links rather than public URLs. The summary generator runs against submitted responses as a distinct step, so it can be re-run without disturbing the underlying data.

Core Components

  • • Flask web framework with modular blueprints
  • • Azure Cosmos DB for scalable data persistence
  • • Role-based authentication and authorisation
  • • Section-based questionnaire engine

Azure & AI Integration

  • • Azure Cosmos DB for document storage
  • • Azure Blob Storage with time-limited access links
  • • OpenAI for summary generation from responses
  • • GitHub Actions CI/CD to Azure App Service

Application Workflow

1. Provisioning

Administrators create accounts with temporary credentials and assign each one to a tenant and role

2. Completion

Respondents work through a section-based questionnaire, with answers saved as they go rather than on final submit

3. Tracking

Section-level completion state rolls up into an administrator view of who is done, who is partway, and who has not started

4. Review and finalisation

Completed submissions enter a scheduled review round where reviewers comment and return them before anything is locked

5. Summary generation

Finalised responses feed the summary generator, producing a readable per-unit document from structured answers and free-text comments

Engineering notes

Partition key choice is the whole design

In Cosmos DB, partitioning by tenant keeps per-tenant queries to a single partition and makes cost scale with tenant count rather than total volume. Getting this wrong is expensive to undo later.

Review rounds are state, not a status field

Modelling the review period as a first-class entity with its own window and configuration made repeat cycles trivial. An earlier draft used a status flag on each response and could not represent a second round at all.

Save-as-you-go is a data-quality feature

Long questionnaires get abandoned. Persisting per-section rather than on final submit turned partial responses into usable data and removed the single most common support complaint.

Signed links over public blobs

Uploaded documents are never served from a public container. Access goes through short-lived signed URLs generated per request against the caller's role.

Related services

If you are collecting the same structured data from a lot of places and reconciling it by hand, these are the relevant places to start.