Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,6 @@ cython_debug/
marimo/_static/
marimo/_lsp/
__marimo__/

# macOS
.DS_Store
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
FROM python:3.11-slim

WORKDIR /app

# Copy requirements first for better caching
COPY requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose Streamlit default port
EXPOSE 8501

# Run Streamlit
CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0"]
24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.PHONY: install run test docker-build docker-run help

help:
@echo "Available targets:"
@echo " install - Install dependencies from requirements.txt"
@echo " run - Run the Streamlit application locally"
@echo " test - Run tests with pytest"
@echo " docker-build - Build Docker image"
@echo " docker-run - Run application in Docker container"

install:
pip install -r requirements.txt

run:
streamlit run app.py

test:
pytest -q

docker-build:
docker build -t streamlit-hello-microservice .

docker-run:
docker run -p 8501:8501 streamlit-hello-microservice
183 changes: 172 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,179 @@
# microservicio
# 👋 Streamlit Hello Microservice

A python microservicio.
![Python](https://img.shields.io/badge/python-3.11-blue.svg)
![Streamlit](https://img.shields.io/badge/streamlit-1.40.2-red.svg)
![License](https://img.shields.io/badge/license-MIT-green.svg)

## Getting Started
Microservicio simple con Streamlit que muestra "Hello World" y permite saludar de forma personalizada.

TODO: Add instructions for setting up and running this microservicio.
## ✨ Características

## Contributing
- Muestra "Hello World" en la interfaz
- Permite ingresar un nombre para recibir un saludo personalizado ("Hola, <nombre>")
- Logging básico de saludos generados
- Estructura modular lista para extender como microservicio
- Totalmente dockerizado
- Tests con pytest

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
## 🚀 Inicio Rápido

## License
### Opción 1: Ejecución Local

MIT License
```bash
# Clonar el repositorio
git clone https://github.com/svg153-org/streamlit-hello-microservice.git
cd streamlit-hello-microservice

# Instalar dependencias
make install

# Ejecutar la aplicación
make run
```

La aplicación estará disponible en http://localhost:8501

### Opción 2: Docker

```bash
# Construir la imagen
make docker-build

# Ejecutar el contenedor
make docker-run
```

La aplicación estará disponible en http://localhost:8501

## 📦 Instalación

### Requisitos

- Python 3.11+
- pip

### Dependencias

```bash
pip install -r requirements.txt
```

Las dependencias principales son:
- `streamlit==1.40.2` - Framework web para la interfaz
- `pytest==8.3.4` - Framework de testing

## 🔧 Uso

### Ejecución Manual

```bash
streamlit run app.py
```

### Uso Programático

```python
from service import greet

# Generar un saludo
mensaje = greet("Juan")
print(mensaje) # Output: Hola, Juan
```

## 🧪 Tests

Ejecutar los tests:

```bash
make test
```

O directamente con pytest:

```bash
pytest -q
```

## 📁 Estructura del Proyecto

```
streamlit-hello-microservice/
├── app.py # Aplicación Streamlit principal
├── service/
│ ├── __init__.py
│ └── greeting.py # Lógica del servicio de saludos
├── tests/
│ ├── __init__.py
│ └── test_greeting.py # Tests del servicio
├── requirements.txt # Dependencias Python
├── Dockerfile # Configuración Docker
├── Makefile # Comandos útiles
├── .gitignore # Archivos ignorados por git
├── LICENSE # Licencia MIT
└── README.md # Este archivo
```

## 🐳 Docker

### Construcción

```bash
docker build -t streamlit-hello-microservice .
```

### Ejecución

```bash
docker run -p 8501:8501 streamlit-hello-microservice
```

El Dockerfile usa `python:3.11-slim` como base e instala todas las dependencias necesarias.

## 📝 Makefile Targets

| Target | Descripción |
|--------|-------------|
| `make install` | Instala las dependencias desde requirements.txt |
| `make run` | Ejecuta la aplicación Streamlit localmente |
| `make test` | Ejecuta los tests con pytest |
| `make docker-build` | Construye la imagen Docker |
| `make docker-run` | Ejecuta el contenedor Docker |
| `make help` | Muestra todos los targets disponibles |

## 🔍 Logging

La aplicación incluye logging básico que registra cada saludo generado:

```
2025-11-15 12:00:00 - service.greeting - INFO - Greeting generated for: Juan
```

## 🚧 Próximos Pasos

Este proyecto está estructurado para facilitar su extensión como microservicio completo:

- [ ] Agregar API REST con FastAPI
- [ ] Implementar más funcionalidades de saludo
- [ ] Agregar base de datos para persistencia
- [ ] Implementar CI/CD
- [ ] Agregar más tests de integración

## 🤝 Contribución

1. Fork el repositorio
2. Crea una rama para tu feature (`git checkout -b feature/nueva-funcionalidad`)
3. Commit tus cambios (`git commit -m 'Agrega nueva funcionalidad'`)
4. Push a la rama (`git push origin feature/nueva-funcionalidad`)
5. Abre un Pull Request

## 📄 Licencia

Este proyecto está bajo la Licencia MIT. Ver el archivo [LICENSE](LICENSE) para más detalles.

## 👥 Autores

- svg153-org - [GitHub](https://github.com/svg153-org)

---

⚡ **Demo en menos de 1 minuto**: `git clone && make install && make run`
27 changes: 27 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Streamlit Hello World Microservice."""
import streamlit as st
from service import greet

# Set page config
st.set_page_config(
page_title="Hello Microservice",
page_icon="👋",
)

# Title and description
st.title("👋 Hello World Microservice")
st.write("Bienvenido a este microservicio de saludo simple.")

# Display Hello World
st.header("Hello World")
st.success("¡Hello World!")

# Name input section
st.header("Saludo Personalizado")
name = st.text_input("Ingresa tu nombre:", placeholder="Tu nombre aquí")

if name:
greeting = greet(name)
st.success(greeting)
else:
st.info("👆 Ingresa tu nombre arriba para recibir un saludo personalizado.")
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
streamlit==1.40.2
pytest==8.3.4
4 changes: 4 additions & 0 deletions service/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Greeting service module."""
from .greeting import greet

__all__ = ['greet']
23 changes: 23 additions & 0 deletions service/greeting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Greeting service with logging."""
import logging

# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
Comment on lines +4 to +8
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling logging.basicConfig() at module level can cause conflicts when the module is imported into applications that configure their own logging (like Streamlit). This may lead to duplicate log messages or configuration conflicts. Consider removing the basicConfig call and letting the application configure logging, or check if logging is already configured before calling basicConfig using if not logging.getLogger().hasHandlers().

Suggested change
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Configure logging only if not already configured
if not logging.getLogger().hasHandlers():
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)

Copilot uses AI. Check for mistakes.
logger = logging.getLogger(__name__)


def greet(name: str) -> str:
"""
Generate a greeting message for the given name.

Args:
name: The name to greet

Returns:
A greeting message string
"""
logger.info(f"Greeting generated for: {name}")
return f"Hola, {name}"
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Test package."""
32 changes: 32 additions & 0 deletions tests/test_greeting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Tests for the greeting service."""
import logging
from service.greeting import greet


def test_greet_returns_correct_format():
"""Test that greet returns the correct format."""
result = greet("Juan")
assert result == "Hola, Juan"


def test_greet_with_different_names():
"""Test greet with various names."""
assert greet("María") == "Hola, María"
assert greet("Pedro") == "Hola, Pedro"
assert greet("Ana") == "Hola, Ana"


def test_greet_with_empty_string():
"""Test greet with empty string."""
result = greet("")
assert result == "Hola, "


def test_greet_logs_info(caplog):
"""Test that greet logs an INFO message."""
with caplog.at_level(logging.INFO):
greet("Test")

assert len(caplog.records) == 1
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion len(caplog.records) == 1 is fragile because if logging.basicConfig() is called multiple times (e.g., when running multiple tests), it may create additional log handlers leading to duplicate log records. This could cause the test to fail unexpectedly. Consider using assert len(caplog.records) >= 1 or filtering records by logger name to make the test more robust.

Suggested change
assert len(caplog.records) == 1
assert len(caplog.records) >= 1

Copilot uses AI. Check for mistakes.
assert caplog.records[0].levelname == "INFO"
assert "Greeting generated for: Test" in caplog.text
Loading