Back to portfolio

PepaDB

1 / 3

The project

Our client studies the provenance of Palaeolithic stone tools, where each artefact’s raw material came from, how old it is, and what it is made of. That data lived in a database that was hard to explore: answering a question meant exporting everything into a spreadsheet, and there was no good way to see artefacts on a map or to run the lab’s machine-learning models against a filtered selection. We set out to build one web platform that lets researchers query, visualise, and analyse the Paleolithic Era Provenance Analysis database without ever leaving the browser.

The customer

Our client was Carlos Odriozola, an archaeology researcher at the University of Seville, who works on the provenance analysis of Palaeolithic lithic material. The project was run as an academic engagement: a real external stakeholder with a real dataset and a real server, commissioned through Leiden University’s software-engineering course.

We held four formal client meetings with Carlos across the semester, with one team member acting as the primary client liaison to keep the conversation consistent. Between meetings we maintained a shared product backlog and an agreed API contract so requirements stayed traceable. Client feedback fed directly back into the product, a round of UI changes (About page, navigation, branding, homepage copy) came straight out of a mid-project meeting. The main friction was external dependencies: a terms-and-conditions page stayed blocked on the client’s lawyer, and deploying to the client’s machine surfaced a VPN constraint we had not anticipated.

The team

We were a team of six, running SCRUM with two-week sprints (standups, planning, review, and retrospective each sprint). Scrum Master: ran sprint planning and retrospectives, owned the sprint documentation and the assignments, and led the final proposal and demo video. Product Manager / client lead: owned the product backlog and the API contract, and was the main point of contact with the client. The rest of the team split along frontend, backend & API, ML services, and testing / DevOps lines.

Work was organised around milestones and GitHub issues, with a branching flow of feature → development → staging → main and pull-request review before anything merged. Every PR ran the full CI pipeline (linting, unit tests, a frontend build, and end-to-end browser tests), so collaboration was gated on green checks rather than on trust. Roughly: one person led frontend architecture, two carried the backend and API layer, two co-led the ML integration and its explainability, and one owned the automated-testing infrastructure and deployment.

Challenges we overcame Deployment behind a VPN. The client’s server sat behind a VPN that broke the networking our Kubernetes setup relied on. Rather than sink more time into it, we switched to Docker Compose over SSH, less orchestration, but it actually ran in the client’s environment. Explainable ML, not just predictions. Wiring SHAP through both models end-to-end (backend and frontend) so predictions came with reasons. Protecting sensitive data with coordinate masking while still serving a useful map. Learning SCRUM by doing it: adapting the process week to week as the system grew.

A genuinely deployed, documented, handed-over system: about 463 commits and 59 merged pull requests from seven contributors between February and June, turned into one coherent platform map, data table, statistics, multi-format export, and two explainable ML services, that now runs on the client’s own infrastructure.

The technologies

Languages

  • Python: backend API and the two ML services.
  • JavaScript (with HTML/CSS): the single-page frontend.
  • SQL: querying the spatial/relational database.

Important frameworks and libraries

  • React 19 + Vite + React Router: frontend application and routing.
  • Leaflet and Recharts: the interactive map and the statistics charts.
  • FastAPI + SQLAlchemy: the REST backend and database access layer.
  • SHAP: explainability for both ML models.

Geospatial

  • GeoServer (WMS/WFS): serves map layers and spatial features, proxied through the API.
  • Custom coordinate masking ( HMAC-SHA256 ) to obfuscate sensitive site locations.

Tooling

  • Ruff: Python linting and formatting (also in CI).
  • ESLint / Prettier: frontend linting and formatting (also in CI).
  • pytest: backend unit tests (also in CI).
  • Playwright: end-to-end browser tests (also in CI).

Caching & deployment

  • In-process TTL cache with optional Redis for shared ML results.
  • Docker Compose over SSH to the client’s server at the University of Seville, with a GitHub
  • Actions CI/CD pipeline.