Based in Vitoria-Gasteiz

Pencho

Web development student. I spot a real problem and build the tool to solve it.

Selected work

Four personal projects.
Choose one to explore.

Expiry Tracker

Expiry dates turned into a clear status for every product.

Project 1 of 4Close

Expiry
Tracker

Each perishable product has one expiry date at the front and another in storage. Instead of displaying dates alone, the system compares them and assigns each product a clear status.

Used in working restaurants

The inputs for each product

Front expiry date, storage expiry date, boxes in storage and later dates waiting behind.

calculateProductStatus()server/services/productStateService.js
  • Is there an expiry date in storage?
  • Does it match the front date?
  • Is it the last box? Are more waiting behind?

Front, then switch

Front and storage expiry dates differ.

Front, then depleted

No expiry date in storage.

Unclassified

Same date, more than one box.

Open, then switch

Same date, last box, later dates behind.

Open, then depleted

Same date, last box, nothing behind.


Alert window

Within the window,
raise an alert.

From today through the next 14 days, inclusive. Everything else can wait.

Today+7+14, alert ends+21+28
DanoninoFront: 21 Oct, within the alert window
FilipinosFront: 13 Nov, outside the alert window

Historical demo data as of 20 Oct 2026.

Design decisions

Problem

Perishable products are split between the front and storage. If nobody checks the dates in time, they expire.

Constraints

Several restaurants share the infrastructure, people have different responsibilities, and the connection is not always reliable.

Decision

Calculate each product’s status from its dates and stock, and flag dates from today through the next 14 days, inclusive.

Workspaces and roles

One platform.
Separate workspaces.

Each restaurant only sees and receives its own data.

AdministratorPlatform and restaurantsSupervisorTheir restaurant’s catalogue and staffShift managerDates and quantities
Another restaurant

Three roles with progressively narrower permissions. No workspace can see another’s data.

When the network drops

The connection drops.
Work carries on.

Available in the expiry module only.

Online

Every change reaches the server immediately.

Offline

The app keeps working. Each change is stored in a local queue (IndexedDB) until the connection returns.

Back online

The queue is sent to the server, which shares the changes with the rest of the restaurant.

Actual screenshot, 20 Oct 2026

What the restaurant sees.

Each group in the list matches one of the statuses leaving the prism:

  • Front, then switch
  • Front, then depleted
  • Open, then switch

The “1” beside the heading is Danonino: the only product within the alert window.

Actual product and expiry-date list in Expiry Tracker (Spanish interface). Danonino is the only product within the alert window.
Technologies
React, Node.js, MongoDB and Socket.IO
My role
Product design and specification. Implemented with Claude Code.

Reitera

Spaced repetition: your answer determines when you revisit each topic.

Project 2 of 4Close

Reitera

Spaced repetition for students. After each review, say how it went and Reitera calculates when you should return to that topic.

Live at reitera.dev

Four futures

Your answer decides
when you return.

Two “Normal” reviews. Today, on day 14, one topic branches into four possible futures.

Each arc’s height or width represents the days until the next review. The solid line is the past; a dotted line is a possible future.

Actual rules: ReviewSessionService.calculateNextReviewDate()

At 30 days or more, the topic is mastered: no further review is scheduled.

Read the example

Today is day 14. The current interval is 10 days and the factor is 2.5. Your answer determines what happens next:

The chart’s four outcomes
AnswerCalculationOutcome
AgainReset to 1 dayReview on day 15
Hard10 × 2.5 × 0.8 = 20 daysReview on day 34
Normal10 × 2.5 = 25 daysReview on day 39
Easy10 × 2.5 × 1.3 = 32.5 → 33 daysTopic mastered; no next date

Intervals are rounded up. Once an interval reaches 30 days, the topic is mastered and no further reviews are scheduled.

How it works

The chart above shows one moment in this cycle.

  1. Organise

    Subjects, topics and exams.

  2. Review

    When you finish, choose Easy, Normal, Hard or Again, with an optional score from 1 to 10.

  3. Reitera schedules

    It calculates the next date. Once the interval reaches 30 days, the topic is mastered.

  4. Track progress

    35 days of activity, weak spots and topics at risk.

How the interval changes

Four answers,
four rules.

Applied to interval × current factor:

Again
Reset to 1 day; factor −0.2
Hard
Interval ×0.8; factor −0.15
Normal
Interval ×1; factor unchanged
Easy
Interval ×1.3; factor +0.15

Intervals are rounded up. The factor is then adjusted, with a minimum of 1.3.

Actual screenshot: completing a review

Reitera’s Spanish review dialog for the SQL Queries topic: a review rating with Easy, Normal (selected), Hard and Again options, an optional score and a confirmation button.
The decision happens here: a rating at the end of each review.

Design decisions

Remembering should not
mean planning.

Problem

Reviewing the same material repeatedly, or reaching the exam having forgotten whole topics.

Constraints

Some topics take more work than others. The next date depends on the previous interval and how the review went.

Technical decision

Recalculate in the service rather than the interface. Save the session and update the topic in a single transaction.

Actual screenshot: today’s reviews

Reitera dashboard in Spanish: 6 pending topics, 8% of the course mastered, and today’s SQL Queries (Databases, 10 days overdue) and IP Addressing (Computer Networks, 7 days overdue), each with a progress bar.
Part of the actual dashboard. The progress bar is visual; it does not determine the next interval.
Technologies
Java 21, Spring Boot, PostgreSQL, JWT and React
My role
I built the backend: API, data model and scheduling logic. React frontend implemented with Claude Code.

Available online, open registration

Anyone can create an account and try it. The intervals in the chart follow the actual review service rules.

Motavia

Official fuel prices, organised to help you choose where to fill up.

Project 3 of 4Close

Motavia

Official fuel prices across Spain, mapped and sorted to help you choose where to fill up. No ads, no sign-up and no infrastructure costs.

Live at motavia.es and listed in the datos.gob.es catalogue

The territory

A whole country.
Point by point.

Scheduled updates every 2 hours

11,386 stations.
One clear answer.

Álava, 95 E5 petrol65 public stations, ranked
1.649 €/L
EROSKI, Ribabellosa
7.54 €
Below the average, for 50 L(1.7998 − 1.649) × 50 L
Provincial average: 1.800

Islands repositioned.
The same geography,
a different frame.

Brightness = 95 E5 petrol price compared with other stations in the province

Each point is a station.
Brighter means cheaper within its province.

11,386 stations. MITECO data from 21/10/2026, 09:17.

Zoom in

Álava, in the light.

65 public stations selling 95 E5 petrol.
Two points in that network, seen up close.

Compare

Light becomes a price.

A: EROSKI

Ribabellosa (Ribera Baja / Erribera Beitia)

1.649 €/L

B: EUROCAM

Nanclares de la Oca (Iruña Oka / Iruña de Oca)

1.679 €/L

Provincial average: 1.7998 €/L

7.54 €

Below the average for 50 L.
Illustrative volume; real prices.

MITECO, 21/10/2026 at 09:17.
95 E5 petrol, public retail prices.
These are not live prices.
Source: Spain’s Ministry for the Ecological Transition (MITECO).

The thinking behind it

The work happens
before you arrive.

0 €per month for infrastructure, using the free tiers of GitHub Actions and Cloudflare Pages.

  1. Every 2 hours

    GitHub Actions downloads and validates MITECO fuel prices, then runs the tests.

  2. Failed checks stop publication

    The last valid version stays online.

  3. 52 files

    One JSON file per province on Cloudflare Pages. No database.

  4. Your browser does the rest

    It downloads only your area, sorts the stations and calculates the savings.

From concept to website

The same area.
The live product.

motavia.es showing Araba/Álava and 95-octane petrol: the cheapest-stations list led by Eroski at €1.649/L, alongside a province map with each station’s price.
motavia.es in Araba/ÁlavaScreenshot of the live website: the map and list as visitors see them.Same area, data captured on 20/10/2026, 23:23
motavia.es on mobile showing Araba/Álava and 95-octane petrol: a map around Vitoria-Gasteiz and the cheapest-stations list, with Eroski at 1.649 €/L and Eurocam at 1.679 €/L.
motavia.es in Araba/ÁlavaScreenshot of the live website: the map and list as visitors see them.Same area, data captured on 21/10/2026, 09:17
Technologies
Astro, TypeScript, MapLibre GL, zod, GitHub Actions and Cloudflare Pages
My role
My concept and direction. Implemented with Claude Code.

DAW Notebook

Scattered recordings and course materials turned into what to study today.

Project 4 of 4Close

DAW Notebook

A personal study system supported by AI agents. It brings together recordings, course material and scattered tasks to answer one question: what should I study today?

In use during the 2026–27 academic year

From scattered material
to one clear decision.

From scattered material
to one clear decision.

Memory

Checklists and notes are kept: every change is saved in Git.

Output

A guide for each topic and a schedule that fits the time actually available each day.

Priorities

First, the teacher’s assessment guidance; then, the PDF. Any differences are flagged.

Input

Class recordings in .vtt format, topic PDFs and course schedules, exactly as received.

Today
  1. Scattered sources

    Everything arrives separately.

    Recordings in .vtt format, course PDFs and schedules. Organisation starts with the actual material.

  2. Transformation

    Priorities give it shape.

    First, the teacher’s assessment guidance; then, the PDF. Discrepancies are flagged.

    CLAUDE.md and AGENTS.md
  3. Clarity

    One clear decision.

    A guide for each topic and a schedule that fits the time actually available each day.

  4. Memory

    Nothing is lost.

    Checklists and notes: every change is saved in Git.

Input: 53 classes, 40 PDFs and 9 course schedules. Output: 20 guides, 86 flagged discrepancies, 27 PDF corrections and 42 versions in Git.Figures from the notebook itself, as of 20/10/2026.

Inside the notebook

A real example, from class to study plan.

The process shown above, step by step, using real excerpts from a specific notebook commit.

Python, topic 2: identity and decimals (commit e9b54a5)

Assign

The schedule suggests.
The content confirms.

Each recording arrives with a number rather than a topic. Its date places it in a course week; the schedule suggests that week’s topic; the class content confirms or corrects the assignment.

53 classes and 9 course schedules

Python/_clases.mdl. 27
617359 → the class establishes the date → Wednesday 7 Oct
Python/_cronograma.mdl. 15
| week | dates | topic |
| 4 | 05–09/10 | T2 |
Class contentconfirmed
operators is and in → Topic 2, section 2.2

Cross-reference

What was said in class,
against the topic PDF.

Once the class is assigned, each concept is read twice: in the transcript and in the PDF for that topic.

40 PDFs of course material

Class on 7 Oct29:31
In class
The explanation of is presents it as an equality comparison.
Paraphrased from the .vtt transcript
Topic 2, PDFp. 9
Course material
is checks whether two variables refer to the same object in memory.
Identity operators, paraphrased

Check

Either source
can be wrong.

Within the same topic, the class corrects the PDF in one case and the PDF corrects the class in another. The guide keeps both versions, identifies the correct one and includes an example that shows the difference.

86 discrepancies and 27 corrections in 20 guides

⚠ Class ≠ PDFT2, l. 218
is checks object identity; == compares values.
a = [1, 2, 3]; b = [1, 2, 3]
a == b → True
a is b → False
Correct: the PDF’s explanation.
The PDF is wrongT2, l. 126–127, PDF p. 65
The solution uses altura = 1,75. With a comma, Python creates a tuple rather than a decimal.
altura = 1,75
altura * 2 → (1, 75, 1, 75)
altura = 1.75
altura * 2 → 3.5
Correct: altura = 1.75. The class warning agrees with the official Python reference ↗.
And sets the prioritiesskill guia-estudio
1. Explicit guidanceIf the teacher explicitly explains how something is assessed or asked in an exam, that guidance takes priority.
2. Otherwise, the PDFTests are based on the PDF. Where sources conflict without explicit guidance, the PDF takes priority; the class explanation is kept as context, marked with ⚠️. It helps with understanding, rather than serving as the exam definition.
3. External sourcesExternal or newer information never replaces the course sources above. If the PDF or class contains something outdated or incorrect, a brief note explains what has changed, but only when it could cause real confusion or matters for the final project or placement. Exam answers follow the course material.

Plan and track

It fits the time
actually available each day.

Each topic becomes specific tasks with time estimates, spread across the hours available each day. Progress is tracked by topic: studied, quiz completed with a score, and revised.

42 versions en Git: nothing is lost

Agenda.mdl. 10–13
Tue 203 hPython, exercises 02–05, 1 h 15
5 workdays, 3 h2 days off, 7 h
Progreso.mdl. 157–160
T2, Python syntax, quiz in week 5StudiedQuiz completed, score:Revised for the exam

A typical week: around 29 hours. Days off vary each week, and the study plan is rebuilt around them.

RulesDefined in a custom skill (guia-estudio) and in CLAUDE.md: how to assign material, what to check and which source takes priority. The agents apply these rules, and I review the result.LimitsThe schedule assigns topics to weeks, rather than recordings: the content decides. Progress is tracked through checklists and quiz scores, rather than study hours.
Technologies
Claude Code, Codex, Markdown, Obsidian and Git
My role
I defined the problem and workflow, and designed the rules, priorities and structure (a custom skill, CLAUDE.md and AGENTS.md). I use AI agents to process and maintain the files.

The notes remain Markdown files: I can read them in Obsidian, edit them and keep their history in Git.

About

I notice what isn’t working
and look for ways to fix it with code.

I’m Pencho. I’m in the second year of a vocational programme in web application development (DAW), alongside my job. Before programming, I spent years in hospitality, including coordinating teams. That taught me to understand how people actually work before suggesting a solution.

Now I use programming, architecture, automation and AI agents to build small, useful tools. I learn by building, and I want to understand why each decision makes sense, as well as whether it works.

Still learning: I started with Java and Spring Boot on the backend, and I’m now working with JavaScript.

Contact

Have a project, an opportunity or an idea in mind? Get in touch.

pencho.deskov@gmail.com