Skip to content
Merged
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
20 changes: 10 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ci:
skip: [pytest]
skip: []

default_language_version:
python: python3.13
Expand All @@ -11,6 +11,7 @@ repos:
hooks:
- id: check-yaml
args: [--allow-multiple-documents]
exclude: config\.yaml$
- id: end-of-file-fixer
- id: trailing-whitespace

Expand All @@ -29,6 +30,14 @@ repos:
hooks:
- id: mypy
name: mypy
additional_dependencies:
[
types-PyYAML,
types-requests,
pydantic,
python-dotenv,
pyyaml-env-tag,
]

# docformatter - formats docstrings to follow PEP 257
- repo: https://github.com/pycqa/docformatter
Expand Down Expand Up @@ -62,15 +71,6 @@ repos:
- -r
- src

- repo: local
hooks:
- id: pytest
name: pytest
entry: uv run pytest tests --cov=src
language: system
types: [python]
pass_filenames: false

# prettier - formatting JS, CSS, JSON, Markdown, ...
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ authors = [
]
requires-python = ">=3.13"
readme = "README.md"
dependencies = []
dependencies = [
"python-dotenv>=1.1.0",
"pyyaml-env-tag>=1.1",
]

[tool.uv.sources]
utils = { workspace = true }
Expand All @@ -27,6 +30,8 @@ dev-dependencies = [
"docformatter>=1.7.5",
"ruff>=0.9.10",
"pre-commit>=3.8.0",
"types-PyYAML>=6.0.12",
"types-requests>=2.32",
"utils",
"core",
]
Expand Down
1 change: 1 addition & 0 deletions src/core/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
API_BASE_URL="https://jsonplaceholder.typicode.com"
48 changes: 48 additions & 0 deletions src/core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Core Module

Core application module providing CLI interface and business logic functionality.

## 📁 Structure

```
src/
├── clients/ # HTTP clients and external integrations
├── commands/ # CLI command implementations
├── config/ # Configuration management
│ ├── config.yaml # Application configuration
│ └── logger_config.py
├── exceptions/ # Custom exception classes
├── models/ # Data models and schemas
│ └── config.py # Settings and configuration models
├── services/ # Business logic services
├── utils/ # Core utilities
└── main.py # Application entry point
```

## 🚀 Usage

### Run the application

```bash
uv run core
```

### Show help and available commands

```bash
uv run core --help
```

### Get help for specific command

```bash
uv run core [command] --help
```

## 📋 Available Commands

To see all available commands, run:

```bash
uv run core --help
```
16 changes: 12 additions & 4 deletions src/core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@ name = "core"
version = "0.0.0"
description = "Core functionality"
authors = [
{name="Aleksander Kowalski"}
{ name = "Aleksander Kowalski" }
]
requires-python = ">=3.13"
readme = "README.md"
dependencies = [
"utils",
"typer>=0.12.5"
"utils",
"typer>=0.12.5",
"pydantic>=2.0.0",
"pyyaml>=6.0.0",
"wireup>=0.8.0",
"requests>=2.31.0",
"tabulate>=0.9.0"
]

[project.scripts]
core = "core.cli:app"
core = "src.main:entrypoint"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src"]

[tool.uv.sources]
utils = { workspace = true }
Empty file added src/core/src/__init__.py
Empty file.
Empty file.
32 changes: 32 additions & 0 deletions src/core/src/clients/http_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Any, Dict, Optional

import requests # type: ignore
from ..models.config import Settings
from wireup import service


@service
class HttpClient:
def __init__(self, settings: Settings):
self.base_url = settings.api.base_url.rstrip("/")
self.session = requests.Session()

def get(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
url = f"{self.base_url}{endpoint}"
try:
response = self.session.get(url, params=params)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"HTTP request failed: {e}")
except ValueError as e:
raise Exception(f"Invalid JSON response: {e}")

def close(self):
self.session.close()

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
Empty file.
52 changes: 52 additions & 0 deletions src/core/src/commands/user_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import logging

import typer
from ..config.registry import command_group
from ..services.user_service import UserService
from wireup import service


@command_group("user")
@service()
class UserCommands:
def __init__(self, user_service: UserService, logger: logging.Logger):
self.user_service = user_service
self.logger = logger
self.app = typer.Typer(help="User management commands")
self._register_commands()

def _register_commands(self):
self.app.command("get", help="Fetch a specific user by ID")(self.get_user)
self.app.command("list", help="List all users")(self.list_users)
self.app.command("posts", help="Get posts for a specific user")(self.get_user_posts)

def get_user(self, user_id: int = typer.Option(1, "--id", help="User ID to fetch")) -> None:
try:
user = self.user_service.get_user_by_id(user_id)
self.logger.info(f"User: {user.name} ({user.email})")
self.logger.info(f"Company: {user.company.name}")
self.logger.info(f"Address: {user.address.street}, {user.address.city}")
except Exception as e:
self.logger.error(f"Error fetching user: {e}")
raise typer.Exit(1)

def list_users(self) -> None:
try:
users = self.user_service.get_all_users()
self.logger.info(f"Found {len(users)} users:")
for user in users:
self.logger.info(f" {user.id}: {user.name} ({user.email})")
except Exception as e:
self.logger.error(f"Error fetching users: {e}")
raise typer.Exit(1)

def get_user_posts(self, user_id: int = typer.Option(1, "--id", help="User ID")) -> None:
try:
posts = self.user_service.get_user_posts(user_id)
user = self.user_service.get_user_by_id(user_id)
self.logger.info(f"Posts by {user.name}:")
for post in posts:
self.logger.info(f" - {post['title']}")
except Exception as e:
self.logger.error(f"Error fetching posts: {e}")
raise typer.Exit(1)
Empty file added src/core/src/config/__init__.py
Empty file.
30 changes: 30 additions & 0 deletions src/core/src/config/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#https://docs.python.org/3/library/logging.config.html#logging-config-dictschema
logging:
version: 1
formatters:
simple:
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
datefmt: "%Y-%m-%d %H:%M:%S"
handlers:
console:
class: logging.StreamHandler
level: DEBUG
formatter: simple
stream: ext://sys.stdout
loggers:
reports:
level: DEBUG
handlers: [console]
propagate: no
root:
level: INFO
handlers: [console]

api:
base_url: !ENV API_BASE_URL
retry_attempts: 3

app:
name: "Hello World CLI"
version: "1.0.0"
default_greeting: "Hello"
13 changes: 13 additions & 0 deletions src/core/src/config/logger_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import logging
from logging.config import dictConfig

from ..models.config import Settings
from wireup import service


@service
def create_logger(settings: Settings) -> logging.Logger:
dictConfig(settings.logging)
logger = logging.getLogger()
logger.info("✅ Skonfigurowano logger")
return logger
14 changes: 14 additions & 0 deletions src/core/src/config/registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
_command_registry = []


def command_group(name: str):
def decorator(cls):
cls._command_name = name
_command_registry.append(cls)
return cls

return decorator


def get_registered_commands():
return _command_registry
9 changes: 0 additions & 9 deletions src/core/src/core/__init__.py

This file was deleted.

14 changes: 0 additions & 14 deletions src/core/src/core/cli.py

This file was deleted.

Empty file.
32 changes: 32 additions & 0 deletions src/core/src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import typer
import wireup
from .clients.http_client import HttpClient
from .config.registry import get_registered_commands
from .commands.user_commands import UserCommands
from .config.logger_config import create_logger
from .models.config import Settings
from .services.user_service import UserService
from utils import hello as hello_world_from_utils

container = wireup.create_sync_container(
services=[Settings, create_logger, HttpClient, UserService, UserCommands]
)

app = typer.Typer()

for command_class in get_registered_commands():
command_instance = container.get(command_class)
app.add_typer(command_instance.app, name=command_class._command_name)


@app.command(help="Greet someone with a friendly message")
def hello():
hello_world_from_utils()


def entrypoint():
app()


if __name__ == "__main__":
entrypoint()
Empty file added src/core/src/models/__init__.py
Empty file.
Loading