Ricardo Rojas

backend / full-stack engineer·apis, automation, system design

selected projects

CFDI Inspector
Python (qt widgets)

Context

Desktop application to view invoices. CFDI 4.0 compliant. A small utility I created for myself. Planing to extend its features into a more premium version.

Architecture Overview

cfdi inspector architecture diagram

Features

  • Load invoices data.
  • Display their concepts.

System Key Decisions

  • Make it as performant as possible with workers.
  • Package the application into an exe.
  • Qt widgets for a more modern look and feel over tinker.
  • Make it work offline.

Screenshot

CFDI Inspector screenshot

Status

in development

github repository

Price Stream; Concurrent price ingestion platform
Scala (http4s, cats, cats-effects, doobie, fs2, circe) · Python (FastAPI, PostgreSQL) · React · JWT

Context

Price Stream is a modular Scala backend service for concurrent price ingestion and historical price tracking across multiple platforms. It exposes operational endpoints for ingestion runs and metrics dashboard visualization.

Architecture Overview

pricestream architecture diagram

Features

  • Ingestion service on startup and scheduled
  • Decoupled service from backend and frontend
  • FastAPI as api gateway to the service
  • JWT auth

System Key Decisions

  • Explicit resource lifecycle management
  • Ingestion runs on fibers
  • Graceful shutdown
  • Concurrent provider processing
  • Observability through dashboard
  • React as frontend for dynamic user interfaces
  • PostgreSQL as a reliable database

Screenshot

PriceStream screenshot PriceStream screenshot

Status

in development

github repository

CobraGen; CLI Tool to defeat Django's boilerplate
Scala

Context

CLI tool to generate Django models from a yaml file.

Architecture Overview

  ____      _               ____
/ ___|___ | |__  _ __ __ _ / ___| ___ _ __
| |   / _ \| '_ \| '__/ _` | |  _ / _ \ '_ \
| |__| (_) | |_) | | | (_| | |_| |  __/ | | |
\____\___/|_.__/|_|  \__,_|\____|\___|_| |_|
      

Features

  • Good performance
  • Takes input from a yaml file
  • 1 single dependency

System Key Decisions

  • Picked yaml over json or xml formats as it's easier to read by humans
  • Picked Django models as output in order to reduce boilerplate

Usage

Input:


models:
  - name: UserProfile
    fields:
      - name: username
        type: Char
        maxLen: 50
        unique: true

      - name: bio
        type: TextField
        required: false

      - name: age
        type: Integer

      - name: balance
        type: Decimal
        maxDigits: 12
        places: 2

      - name: created_at
        type: DateTime

      - name: is_verified
        type: Boolean

  - name: Post
    fields:
      - name: author
        type: ForeignKey
        to: UserProfile
        onUpdate: CASCADE

      - name: title
        type: Char
        maxLen: 200

      - name: content
        type: TextField

      - name: published_date
        type: Date
        required: false
      

Output:


from django.db import models


class Post(models.Model):
    author = models.ForeignKey("UserProfile", on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    content = models.TextField()
    published_date = models.DateField(null=True, blank=True)

class UserProfile(models.Model):
    username = models.CharField(max_length=50, unique=True)
    bio = models.TextField(null=True, blank=True)
    age = models.IntegerField()
    balance = models.DecimalField(max_digits=12, decimal_places=2)
    created_at = models.DateTimeField()
    is_verified = models.BooleanField()

       

Status

in development

github repository

KanbAnpp; Kanban Web Application
Python (Django, DRF) · React · WebSockets · JWT · SQLite (tentative)
KanbAnpp logo

Context

A web-based Kanban application designed to support collaborative task management with real-time updates and role-based access control.

Architecture Overview

kanbanpp architecture diagram

Features

  • Real time updates
  • Attachments
  • Tags
  • Roles
  • JWT authentication

System Key Decisions

  • Django + DRF for backend, and quick REST API development
  • Serializers for data validation and transformation
  • JWT authentication for stateless API usage
  • WebSockets for collaborative real-time updates
  • Daphne chosen for simplicity; Nginx + Uvicorn planned when horizontal scaling becomes necessary
  • React as frontend for dynamic user interfaces
  • TanStack Query for data fetching and caching
  • Pytest for testing
  • SQLite for quick prototyping

Screenshot

KanbAnpp screenshot

Status

in development

github repository

SessionBook; Clean Architecture REST API
Java · Spring Boot · JWT · DTOs · MySQL
SessionBook logo

Context

Inspired by my hobby as a guitarist when practicing songs. It's used to track progress of songs you want to learn. A web application exposing a REST API designed following clean architecture principles, with strong separation between domain, application, and infrastructure layers.

Architecture Overview

sessionbook architecture diagram

Features

  • Guitar tab scraping
  • Song practice catalogue
  • JWT authentication

System Key Decisions

  • Spring Boot for a robust and scalable backend framework
  • Explicit layering to enforce separation of concerns
  • DDD for clear domain modeling
  • DTO pattern to decouple domain models from API representations
  • Async for scraping guitar tabs and heavy lifting tasks
  • JWT authentication for stateless clients
  • MySQL for relational data storage
  • React for building user interfaces
  • TypeScript for frontend development

Status

in development

github repository

DevEnvCheck; Windows Developer Environment Auditor
Java 8 · PicoCLI · Windows Internals · Maven · CI/CD (GitHub Actions)
DevEnvCheck logo

Context

A lightweight Java-based CLI tool built after a real recovery incident to diagnose, audit, and validate Windows developer environments. Designed to quickly identify missing tools, broken PATHs, and misconfigured environment variables during onboarding or incident response.

Features

  • Detect installed developer tools and SDKs
  • Version detection via CLI or file metadata
  • Recursive project type scanning (Node, Java, .NET, Python)
  • Safe cleanup of node_modules directories
  • Environment variable diagnostics
  • Exportable audit reports

Key Decisions

  • Java 8 for maximum compatibility with enterprise environments
  • PicoCLI for composable subcommands and argument parsing
  • Windows-native tooling (where, wmic, filesystem probing) instead of fragile heuristics
  • File metadata inspection for GUI tools without CLI version flags (Visual Studio, SSMS)
  • Defensive execution model, avoids launching GUI applications or modifying system state

Usage

clean-node-modules path #asks for confirmation y/N before deleting
clean-node-modules path --silent #no confirmation
clean-node-modules path --dry-run #lists what would be deleted but doesn't delete
check-installation
scan-projects path
env-check path

Status

complete

github repository

KeyScala; Secure CLI Password Manager
Scala · CLI · AES/GCM · JSON Persistence
KeyScala logo

Context

Type-safe CLI password manager focused on security and correctness, using modern cryptographic methods.

Architecture Overview

keyscala architecture diagram

Features

  • Simple CLI usage
  • AES/GCM encryption
  • Custom serialization

Key Decisions

  • AES/GCM for authenticated encryption
  • Secure key management practices
  • Domain-driven modeling to avoid invalid states
  • Minimal dependencies for portability

Usage

# Add an entry
sbt "run --add example.com mykey"

# Edit an entry, you will be prompted for a password again
sbt "run --edit example.com mykey"

# Delete an entry
sbt "run --del example.com mykey"

# List all entries
sbt "run --list"

# Search for an entry (password copied temporarily to clipboard)
sbt "run --search mykey"

# Display help
sbt "run --help"

Status

complete

github repository

AssetFLOW; Asset Transformation Library
Scala · Image Processing · OCR Preprocessing
AssetFLOW logo

Context

Scala library focused on scalable image transformations, including format conversion, thumbnail generation, palette extraction and preprocessing for OCR pipelines.

Architecture Overview

AssetFLOW architecture diagram

Features

  • Thumbnail generation
  • OCR preprocessing fixes
  • Image transformations
  • Palette extraction
  • Web image guidelines reference

Key Decisions

  • Strong DDD principles for clear domain modeling
  • Functional programming principles for composable transformations
  • Modular design for easy extension with new transformations

Usage

//helpers
Pipe()
  .from("in")
  .to("out")
  .inspect("after load") { img =>
    println(img.width, img.height)
  }
  .step(TransformationStep("grayscale", OCR.grayscale))
  .inspect("after grayscale") { img =>
    println("ok")
  }
  .dryRun()

Batch()
  .from("in")
  .to("out")
  .foreach("convert") { (file, out, in) =>
    ImageTransforms.convertTo(file, out, ImageTransforms.Webp)
  }.dryRun()

//folders setup
val inputDir = new File("input")
val outputDir = new File("output")
outputDir.mkdirs()

//listing images
val images = ImageTransforms.listImages(inputDir)
println(s" ${images.size} found ${inputDir.getPath}:")
images.foreach(f => println(s"  - ${f.getName}"))

//converting to webp
val webpResults = ImageTransforms.convertTo(images, outputDir, Webp)
webpResults.foreach {
  case Right(f) => println(s"WebP at: ${f.getName}")
  case Left(err) => println(s"Error: $err")
}

//making thumbnails
val thumbsDesktop = ImageTransforms.createThumbnail(images, outputDir, Desktop)
val thumbsMobile = ImageTransforms.createThumbnail(images, outputDir, Mobile)

println("Thumbnails desktop:")
thumbsDesktop.foreach {
  case Right(f) => println(s"  - ${f.getName}")
  case Left(err) => println(s"  - Error: $err")
}

println("Thumbnails mobile:")
thumbsMobile.foreach {
  case Right(f) => println(s"  - ${f.getName}")
  case Left(err) => println(s"  - Error: $err")
}

//now placeholders
val placeholders = ImageTransforms.generatePlaceholders(
  number = 3,
  width = 200,
  height = 200,
  fillColor = Some(Color.RED),
  applyBlur = true,
  outputDir = outputDir
)

placeholders.foreach {
  case Right(f) => println(s"placeholder generated: ${f.getName}")
  case Left(err) => println(s"Error: $err")
}

//test OCR preprocessing
images.headOption.foreach { imgFile =>
  println("testing ocr processing...")
  val image = ImmutableImage.loader().fromFile(imgFile)
  val processed = OCR.optimize(
    image,
    tilt = 5.0,
    contrastFactor = 1.3,
    threshold = 128,
    doBinarize = true
  )
  val (name, ext) = Common.getNameAndExtension(imgFile.getName)
  val key = Common.timestamp
  val outPath = new File(outputDir, s"${name}_$key${ext.getOrElse("")}").getPath
  processed.output(WebpWriter.MAX_LOSSLESS_COMPRESSION, new File(outPath))
  println(s"OCR processed stored at: $outPath")
}

//create a color
val redColor = RGBColor(100, 50, 200)
println(s"initial color: $redColor, hex=${redColor.toHex}")

//increase channels
val brighter = redColor.increaseAll(50, 30, -100)
println(s"adjusted color: $brighter, hex=${brighter.toHex}")

//mix 'em up
val blueColor = RGBColor(0, 0, 255)
val mixed = redColor.mixWith(blueColor, 0.5)
println(s"50% mix: $mixed, hex=${mixed.toHex}")

//from hex
val fromHex = RGBColor.fromHex("#ff00cc")
println(s"from hex '#ff00cc': $fromHex")

//random color
val randomColor = RGBColor.random()
println(s"random color: $randomColor, hex=${randomColor.toHex}")

val cmyk = CMYKColor(20, 40, 60, 10)
val rgb = RGBColor(100, 150, 200)

//increase specific channel by 10 (using currying)
val brighterMagenta = cmyk.modifyChannel(Magenta)(_ + 10)
println(brighterMagenta)

//using the shortcut increaseChannel
val brighterRed = rgb.increaseChannel(Red, 20)
println(brighterRed)

//more complex HOF: halve yellow
val lessYellow = cmyk.modifyChannel(Yellow)(_ / 2)
println(lessYellow)

WebsiteImageType.summary()
WebsiteImageType.fromName("logo_square") match {
  case Some(img) => println(s"square logo found guidelines for desktop: ${img.desktop} " +
    s" for mobile: ${img.mobile}, ratio=${img.ratio}")
  case None => println("square logo not found")
}
/*
initial color: RGBColor(100,50,200), hex=#6432C8
adjusted color: RGBColor(150,80,100), hex=#965064
50% mix: RGBColor(50,25,227), hex=#3219E3
from hex '#ff00cc': RGBColor(255,0,204)
random color: RGBColor(75,123,240), hex=#4B7BF0

Type                 Desktop (WxH)        Mobile (WxH)         Ratio
----------------------------------------------------------------------
background           2560x1400            360x640              64:35
hero                 1280x720             360x200              16:9
banner               1200x400             360x120              3:1
blog                 1200x800             360x240              3:2
logo_rectangle       400x100              160x40               4:1
logo_square          100x100              60x60                1:1
favicon              16x16                16x16                1:1
social_icon          32x32                48x48                1:1
lightbox             1920x1080            360x640              16:9
thumbnail            300x300              90x90                1:1
product_thumbnail    300x300              150x150              1:1

square logo found guidelines for desktop: 100x100  for mobile: 60x60, ratio=1:1
*/

Status

v0.5.1

github repository

JpnUtils; Unicode-Aware Text Utilities
Scala · Unicode · Text Processing
JpnUtils logo

Architecture Overview

JpnUtils architecture diagram

Context

Small, zero-dependency utility library for Japanese string handling, providing Unicode-aware and language-specific transformations.

Features

  • Zero-dependency
  • String transformations

Key Decisions

  • Zero-dependency for easy integration
  • Comprehensive Unicode support
  • Focus on Japanese text processing needs

Usage

//using the JapaneseUtils singleton
println(JapaneseUtils.containsHiragana("込める")) //true
println(JapaneseUtils.containsKatakana("淋しい")) //false
println(JapaneseUtils.containsKanji("淋しい"))    //true

println(JapaneseUtils.isHiragana('込'))         //false
println(JapaneseUtils.isKatakana('淋'))         //false
println(JapaneseUtils.isKanji('い'))            //false

//using implicits

//hasX methods, works on string
println("当てのない僕は".hasHiragana)             //true
println("満月".hasKanji)                         //true
println("オカエリナサイ".hasKanji)                 //false

//isX, works on char
println('そ'.isHiragana)                       //true
println('た'isKatakana)                        //false
println('林'.isKanji)                          //true

//miscellaneous methods
val testStr = """"this is a test!? yes? it is sir.""""
val strWithReplacedPunctuation = JapaneseUtils
                                    .Punctuation
                                    .replacePunctuation(testStr)
println(strWithReplacedPunctuation)         //"this is a test!? yes? it is sir。"

//「wrap me in single quotes」 and 『wrap me in double quotes』 respectively
println(JapaneseUtils.Punctuation.wrapInSingleQuotes("wrap me in single quotes"))
println(JapaneseUtils.Punctuation.wrapInDoubleQuotes("wrap me in double quotes"))

//2025 update KanaDiacritics
//true
println("俺はテストだぞ".hasDakuten)
//true
println("いっぱいに静かがっぽい".hasHandakuten)

//カタカナ ABC123&%
val s = "カタカナ ABC123&%"
println(HalfWidthConverter.toHalfWidth(s))

Status

complete

github repository

Chordal; Type-Safe Musical DSL
Scala · DSL · Functional Programming
Chordal logo

Context

A weekend project that turned into something bigger. Internal DSL for modeling harmonic structures and chord progressions, separating declarative syntax, semantics, and domain invariants.

Architecture Overview

chordal architecture diagram

Features

  • Library support for music theory concepts
  • EDSL syntax for musical expressions
  • Zero-dependency for easy integration

Key Decisions

  • Strong modeling for musical concepts
  • Functional programming principles for composable constructs
  • Type safety to prevent invalid musical states

Lessons Learned

Over-engineering DSLs too early increased cognitive load. I now bias toward simpler models and evolve abstractions only when pressure exists.

Usage

//work in progress, subject to change
val C  = Note("C")
val D  = Note("D")
val Eb = Note("Eb")

val cMajor = Triad.major(C)
val dMinor = Triad.minor(D)
val ebDim  = Triad.diminished(Eb)

println(cMajor.name) // "C"
println(dMinor.name) // "Dm"
println(ebDim.name)  // "E°"

println(cMajor.asNotes) // List(C, E, G)
println(dMinor.asNotes) // List(D, F, A)

val csus2 = cMajor.sus2
val csus4 = cMajor.sus4

println(csus2.name)    // "Csus2"
println(csus2.asNotes) // List(C, D, G)

println(csus4.asNotes) // List(C, F, G)

val cAdd9 = cMajor.add9
val cAdd6 = cMajor.add6

println(cAdd9.name)    // "Cadd9"
println(cAdd9.asNotes) // List(C, E, G, D)

println(cAdd6.asNotes) // List(C, E, G, A)

val c5 = cMajor.toPowerChord

println(c5.name)    // "(C5)"
println(c5.asNotes) // List(C, G)

val cMaj7 = cMajor.withSeventh(MajorSeventh)
val d7    = dMinor.withSeventh(MinorSeventh)

println(cMaj7.name)    // "Cmaj7"
println(cMaj7.asNotes) // List(C, E, G, B)

println(d7.name)    // "Dm7"
println(d7.asNotes) // List(D, F, A, C)

val cMaj9 =
  cMajor
    .withSeventh(MajorSeventh)
    .addExtension(Ninth)

println(cMaj9.name)    // "Cmaj79"
println(cMaj9.asNotes) // List(C, E, G, B, D)

val key = Triad.major("C".note)

val ii = Triad.minor("D".note)
val V  = Triad.major("G".note)
val I  = Triad.major("C".note)

val ii7   = ii.withSeventh(MinorSeventh)
val V7    = V.withSeventh(MinorSeventh)
val Imaj7 = I.withSeventh(MajorSeventh)

println(List(ii7, V7, Imaj7).map(_.name))

val c1 =
  chord {
    root(C)
    quality(MajorTriad)
    withExtensions(Ninth)
  }

println(c1)

val c2 =
  chord {
    root("D".note)
    sus(Sus4)
  }
println(c2)

val c3 =
  chord {
    root("E".note)
  }
println(c3)
/*
C
Dm
Eb°
(C,E,G)
(D,F,A)
Csus2
(C,D,G)
(C,F,G)
Cadd9
(C,E,G,D)
(C,E,G,A)
(C5)
(C,G)
Cmaj7
(C,E,G,B)
Dm7
(D,F,A,C)
Cmaj79
(C,E,G,B,D)
List(Dm7, G7, Cmaj7)
TriadDesc(C,MajorTriad,List(Ninth))
SuspendedDesc(D,sus4,List())
PowerDesc(E)
*/

Status

in development

github repository

ScalaCron; Cronjob DSL
Scala · DSL · Functional Programming
ScalaCron logo

Context

ScalaCron is an EDSL for generation of cronjob expressions in a zero-dependency library.

Architecture Overview

ScalaCron architecture diagram

Features

  • Library support for cronjob expressions
  • EDSL syntax for easy expression creation
  • Pretty print support
  • Lightweight and zero-dependency

Key Decisions

  • Robust AST for cronjob concepts
  • Functional programming principles for composable constructs
  • Prioritized flexibility and ease of use

Usage

import domain.Models.*
import domain.Models.Minute.given
import dsl.CronDSL.{to, *}
import Days.*
import scala.language.postfixOps

object Main:
  def main(args: Array[String]): Unit =

  val job2 = cron { c =>
    c.minute(* / 5)
    c.hour(9.h to 17.h)
    c.dom(1.dom)
    c.dow(Friday)
  } >> "/usr/bin/backup.sh"
  println(job2?) //same as job.schedule
  println(job2) //whole expression as string

/* Output:
  - Minute        : every 5 minutes
  - Hour          : at 12
  - Day of Month  : at 1
  - Day of Week   : Monday
  */5 12 1 1 /usr/bin/backup.sh
*/

Status

completed

github repository

Monika; Japanese Unicode-Aware Text Utilities
Nim · Unicode · Text Processing
Monika logo

Context

Port of JpnUtils to the Nim language, a small, zero-dependency utility library for Japanese string handling, providing Unicode-aware and language-specific transformations.

Usage

import monika/japaneseutils
import monika/punctuation
import monika/halfwidthconverter

if "こんにちは".hasHiragana:
  echo "Contains Hiragana!"

if "モニカ".hasKatakana:
  echo "Contains Katakana!"

if "学校".hasKanji:
  echo "Contains Kanji!"

# full-width to half-width
echo "ハロー、ワールド!".toHalfWidth
# Output: ハロー、ワールド!

# Check for voiced marks
if "が".hasDakuten:
  echo "This character is voiced."

let msg = "Hello"
echo msg.wrapInSingleQuotes # Output: 「Hello」
echo msg.wrapInDoubleQuotes # Output: 『Hello』

let s = "ガキ"
let h = "が".asRune()
let k = "エ".asRune()

if h.isSome:
  echo h.get.hiraToKata() # Output: カ
else:
  echo "empty string"

if k.isSome:
  echo k.get.kataToHira() # Output: え
else:
  echo "empty string"

let str = "日本語abcカナ"
echo str.containsOnly({Kanji, Katakana})  # false
let summary = str.scriptSummary()

echo summary.hiragana  # 0
echo summary.katakana  # 2
echo summary.kanji     # 3
echo summary.other     # 3

Status

complete

github repository

mvps

PrepOCR; Image Preprocessing for OCR
React · TypeScript · Scala (http4s, fs2, cats, cats-effect)

Context

Web app to prep images before OCR processing. Users upload a batch of images and get back a zip with the optimized versions, ready for cleaner text extraction. It uses AssetFLOW, my own library.

Features

  • Tilt/deskew correction
  • Contrast enhancement
  • Threshold and binarization
  • Batch upload, zip download

Screenshot

PrepOCR screenshot

Status

live

live demo

PrepAsset; Image Transformation Toolkit
React · TypeScript · Scala (http4s, fs2, cats, cats-effect)

Context

Web app for applying image transformations at scale; resize, thumbnails, WebP conversion, autocrop. Users upload a batch and download a zip with the results. Shares its backend with PrepOCR.

Features

  • WebP conversion
  • Resize and thumbnail generation
  • Autocrop
  • Batch upload, zip download

Screenshot

PrepAsset screenshot

Status

live

live demo

Calculadora de Aguinaldo
React

Context

Small SPA to calculate the Mexican year-end bonus (aguinaldo) based on days worked and salary.

Screenshot

Calculadora de Aguinaldo screenshot

Status

live

live demo

stack marks