Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
69b66c1
chore: update e2e workflow to simplify commands and add processing se…
May 14, 2025
77ac53d
chore: update Node.js version to 20 in e2e workflow
May 14, 2025
6bc647c
refactor: improve type definitions and clean up props handling in Sur…
May 14, 2025
2de3bea
docs: enhance README with detailed CI process and checks
May 14, 2025
9b67d25
fix: correct Python version in ruff configuration and tidy comments i…
May 14, 2025
17461fa
refactor: remove unused test_get_task_status function from test_task.py
May 15, 2025
e9cded9
fix: update test command in workflow and README to use 'uv run pytest'
May 15, 2025
5d563c9
docs: add CI section to README with GitHub Actions details
May 15, 2025
c6d32a5
feat: add frontend and processing service linting workflows; remove o…
May 15, 2025
b320c2a
fix: correct typo in survey-pipeline.yml
May 15, 2025
fffe9da
fix: rename lint job to frontend-lint in GitHub Actions workflow
May 15, 2025
bc15cb0
feat: add Checkstyle configuration and enhance Survey-related classes…
May 15, 2025
c4f7ea0
feat: implement integration tests with Testcontainers and add initial…
May 15, 2025
16d11da
feat: enhance database schema handling and update Task model with Enu…
May 15, 2025
f9722c1
fix: remove unused import of Task model in database.py and clean up i…
May 15, 2025
e6bf1d7
chore: consolidate CI workflows into a single formulaai-ci.yml and re…
May 15, 2025
9e9d048
feat: enhance CI workflow to allow manual job execution with force op…
May 15, 2025
f77e373
chore: remove workflow_dispatch inputs and simplify job conditions in…
May 15, 2025
4703003
docs: add Continuous Integration (CI) section to project documentation
May 15, 2025
c6b99e1
feat: implement path detection for CI jobs to optimize workflow execu…
May 15, 2025
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
17 changes: 0 additions & 17 deletions .github/workflows/e2e.yml

This file was deleted.

112 changes: 112 additions & 0 deletions .github/workflows/formulaai-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: FormulAI CI

on:
pull_request:

jobs:
detect-changes:
name: Detect changed paths
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
processing: ${{ steps.filter.outputs.processing }}
survey: ${{ steps.filter.outputs.survey }}
steps:
- uses: actions/checkout@v3

- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
frontend:
- 'frontend/**'
processing:
- 'processing/**'
survey:
- 'survey/**'

lint-frontend:
name: Lint Frontend
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: npm install
- run: npm run lint

e2e:
name: E2E Tests
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 20
- run: npm install
- run: npm run dev &
- run: npx wait-on http://localhost:5173
- run: npm run test:e2e

processing-tests:
name: Processing Service Tests
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.processing == 'true'
defaults:
run:
working-directory: processing
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install uv and ruff
run: |
pip install uv
pip install ruff
- name: Sync dependencies
run: uv sync
- name: Run lint ruff
run: ruff check .
- name: Run tests
run: uv run pytest

survey-tests:
name: Survey Service Tests
runs-on: ubuntu-latest
needs: detect-changes
if: needs.detect-changes.outputs.survey == 'true'
defaults:
run:
working-directory: survey
steps:
- uses: actions/checkout@v3
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Run lint (Checkstyle)
run: ./mvnw checkstyle:check
- name: Run tests
run: ./mvnw clean verify jacoco:report
- name: Check coverage >= 80%
run: ./mvnw jacoco:check
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,23 @@ The application will be available at `http://localhost`.

GitHub: [opendexcom/formul.ai](https://github.com/opendexcom/formul.ai)

## 🤖 Continuous Integration (CI)

This project uses GitHub Actions for CI.
On every pull request, the following checks are automatically run depending on which files are changed:

- **Frontend:**
- Linting (`npm run lint`) if files in `frontend/` are changed
- End-to-end tests (`npm run test:e2e`) always run

- **Processing Service:**
- Dependency sync, linting (ruff), and tests (pytest) if files in `processing/` are changed

- **Survey Service:**
- Linting (Checkstyle) and tests (Maven) if files in `survey/` are changed

You can find the workflow configuration in `.github/workflows/formulaai-ci.yml`.

## 📜 License

This project is licensed under the **GNU Affero General Public License v3.0 (AGPL-3.0)**. See the [LICENSE](./LICENSE) file for details.
Expand Down
9 changes: 9 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ The output will be in the storybook-static/ folder.

---

## 🤖 Continuous Integration (CI)

On every pull request, GitHub Actions will:

- Run `npm run lint` if any files in the `frontend/` directory are changed
- Always run end-to-end tests (`npm run test:e2e`)

See the root `.github/workflows/formulaai-ci.yml` for details.

## Project Structure

```
Expand Down
26 changes: 16 additions & 10 deletions frontend/src/components/Form/SurveyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ import { useEffect, useState } from 'react'
import { getForm, submitForm } from '../../../lib/api'
import { useNavigate } from 'react-router'
import { Card, CardContent } from '@mui/material'
import { withTheme } from '@rjsf/core'
import { IChangeEvent, withTheme } from '@rjsf/core'
import { Theme } from '@rjsf/mui'
import validator from '@rjsf/validator-ajv8'

interface SurveyFormProps {
'form-id'?: string;
}

export const SurveyForm = (props:SurveyFormProps ) => {
export const SurveyForm = ({ 'form-id': formId }: SurveyFormProps) => {
const navigate = useNavigate()
const [formData, setFormData] = useState<any>(null)

type FormDataType = {
schema: object
uiData?: object
} | null

const [formData, setFormData] = useState<FormDataType>(null)

const Form = withTheme(Theme)

Expand All @@ -22,10 +28,10 @@ export const SurveyForm = (props:SurveyFormProps ) => {
const parsedSchema = JSON.parse(schema.schemaJson)
setFormData(parsedSchema)
}
const id = props['form-id'] ? props['form-id'] : ''

const id = formId ? formId : ''
fetchData(id)
}, [])
}, [formId])

const fetchForm = async (id: string) => {
try {
Expand All @@ -43,7 +49,10 @@ export const SurveyForm = (props:SurveyFormProps ) => {
}
}

const handleSubmitForm = async (formData: any, e: React.FormEvent<HTMLFormElement>) => {
const handleSubmitForm = async (
formData: IChangeEvent,
e: React.FormEvent<HTMLFormElement>
) => {
e.preventDefault()

try {
Expand All @@ -69,6 +78,3 @@ export const SurveyForm = (props:SurveyFormProps ) => {
</>
)
}

// const LeftAlignedFormLabel = styled(FormLabel)({
// textAlign: 'left',
15 changes: 14 additions & 1 deletion processing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,18 @@ All tests are located in the `app/tests` folder.
Run all tests using `uv`:

```bash
uv test
uv run pytest
```

---

## Continuous Integration (CI)

This project uses GitHub Actions for continuous integration.
On every pull request that changes files in the `processing/` folder, the following checks are automatically run:

- **Dependency Sync:** Ensures all dependencies are installed using `uv`.
- **Linting:** Runs [ruff](https://docs.astral.sh/ruff/) to check code style and quality.
- **Testing:** Runs all tests using `uv run pytest`.

You can find the workflow configuration in `.github/workflows/formulaai-ci.yml`.
9 changes: 4 additions & 5 deletions processing/app/database.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
from functools import lru_cache

from app.models import Task
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncEngine
from sqlmodel import SQLModel
from sqlmodel import Table

owned_table_names = [Task.__tablename__]

@lru_cache()
def get_owned_tables() -> list[Table]:
owned_tables: list[Table] = []
tables = SQLModel.metadata.sorted_tables
for table in tables:
print(f"Table: {table.name}")
if table.name in owned_table_names:
print(f"Table: {table.name}, schema: {table.schema}")
if table.schema == "processing":
owned_tables.append(table)
print(f"owned_tables: {owned_tables}")
return owned_tables
Expand All @@ -23,10 +21,11 @@ def get_owned_tables() -> list[Table]:
async def create_db_and_tables(engine: AsyncEngine):
async with engine.begin() as conn:
await conn.execute(sa.schema.CreateSchema("processing", if_not_exists=True))
await conn.execute(sa.schema.CreateSchema("survey", if_not_exists=True))
await conn.commit()

async with engine.begin() as conn:
# Check if the database exists
# Check if the database exists
tables_to_create = get_owned_tables()
await conn.run_sync(SQLModel.metadata.create_all, tables=tables_to_create)

Expand Down
10 changes: 9 additions & 1 deletion processing/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from sqlmodel import Relationship
from sqlmodel import SQLModel
from sqlmodel import String
from sqlalchemy import Enum as PgEnum


class TaskStatus(StrEnum):
Expand All @@ -26,7 +27,14 @@ class Task(SQLModel, table=True):
# TODO: parametrize default_factory depending on database engine (sync/async),
# since async engine use only offset-naive datetime
created_at: datetime = Field(default_factory=lambda: datetime.now())
status: TaskStatus = Field(default=TaskStatus.NULL)
status: TaskStatus = Field(
default=TaskStatus.NULL,
sa_column=Column(
PgEnum(TaskStatus, name="taskstatus", schema="processing"),
nullable=False,
default=TaskStatus.NULL,
)
)
result: Optional[str] | None = Field(default=None)


Expand Down
39 changes: 0 additions & 39 deletions processing/app/tests/test_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,6 @@
client = TestClient(app)


def test_get_task_status():
local_task_id = uuid4()
created_task: Task = Task(
id=local_task_id,
survey_id=uuid4(),
status=TaskStatus.NULL,
result=None,
)

def get_task_service_mock():
class MockTaskService:
async def get_task_by_id(self, task_id: UUID4) -> Task:
if task_id != local_task_id:
raise NotFoundError(f"Task with ID {task_id} not found")
return created_task

return MockTaskService()

app.dependency_overrides[get_task_service] = get_task_service_mock

response = client.get(f"/tasks/{local_task_id}/status")

assert response.status_code == 200

expected_response = TaskResponse(
id=created_task.id,
survey_id=created_task.survey_id,
created_at=created_task.created_at,
status=created_task.status,
)

assert response.json() == {
"id": str(expected_response.id),
"survey_id": str(expected_response.survey_id),
"created_at": expected_response.created_at.isoformat(),
"status": expected_response.status,
}


def test_get_completed_task_file():
local_task_id = uuid4()
local_survey_id = uuid4()
Expand Down
6 changes: 3 additions & 3 deletions processing/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ checkers = [
]

[tool.ruff]
target-version = "py313"
lint.select = ["E", "F", "W", "C", "N"]
lint.ignore = ["E501", "E711"] # Example: Ignore line length errors
target-version = "py312"
select = ["E", "F", "W", "C", "N"]
ignore = ["E501", "E711"] # Example: Ignore line length errors
exclude = ["migrations", "tests"]

line-length = 120
Expand Down
Loading