Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
41767f7
chore(deps): update dependency django to v5.1.7 [security] (#136)
renovate[bot] Mar 7, 2025
5292c1b
build(deps): bump koa from 2.13.4 to 2.15.4 in /ui (#134)
dependabot[bot] Mar 13, 2025
da19c60
build(deps): bump gunicorn from 22.0.0 to 23.0.0 in /api (#139)
dependabot[bot] Mar 31, 2025
2b5a9bc
build(deps): bump @babel/runtime from 7.15.4 to 7.26.10 in /ui (#137)
dependabot[bot] Mar 31, 2025
ded0d28
build(deps): bump @babel/helpers from 7.15.4 to 7.26.10 in /ui (#138)
dependabot[bot] Mar 31, 2025
2ddd290
chore(deps): update dependency django to v5.1.8 [security] (#141)
renovate[bot] Apr 3, 2025
3da791a
build(deps): bump koa from 2.15.4 to 2.16.1 in /ui (#142)
dependabot[bot] Apr 10, 2025
54a2396
chore : adds more tests and assertions (#143)
rajadilipkolli Apr 10, 2025
0ca5912
add steps to run in local environment
rajadilipkolli Apr 11, 2025
37ac6fd
fix issue with opening host
rajadilipkolli Apr 11, 2025
f06b8e1
fix limitation
rajadilipkolli Apr 11, 2025
9813139
Update run-celery-local.sh
rajadilipkolli May 19, 2025
ac84c5c
Apply suggestions from code review
rajadilipkolli May 19, 2025
55b0ba9
chore(deps): update dependency django to v5.1.9 [security] (#146)
renovate[bot] May 27, 2025
6242e00
Merge branch 'main' into local-setup
rajadilipkolli May 27, 2025
3dc8ff2
chore(deps): update dependency django to v5.1.10 [security] (#147)
renovate[bot] Jun 7, 2025
513e4b3
chore(deps): update dependency requests to v2.32.4 [security] (#149)
renovate[bot] Jun 10, 2025
a5fdf7c
chore(deps): update dependency djangorestframework-simplejwt to v5.5.…
renovate[bot] Sep 4, 2025
aa16f10
chore(deps): update dependency django to v5.1.12 [security] (#157)
renovate[bot] Sep 10, 2025
2427b28
chore(deps): update dependency django to v5.1.13 [security] (#158)
renovate[bot] Oct 2, 2025
6848486
build(deps): bump pbkdf2 from 3.1.2 to 3.1.3 in /ui (#150)
dependabot[bot] Oct 4, 2025
92753b8
build(deps): bump koa from 2.16.1 to 2.16.2 in /ui (#151)
dependabot[bot] Oct 4, 2025
ebb7bc9
build(deps): bump brace-expansion from 1.1.11 to 1.1.12 in /ui (#156)
dependabot[bot] Oct 4, 2025
1c08fab
build(deps): bump sha.js from 2.4.11 to 2.4.12 in /ui (#152)
dependabot[bot] Oct 4, 2025
6eada56
build(deps): bump cipher-base from 1.0.4 to 1.0.6 in /ui (#153)
dependabot[bot] Oct 4, 2025
2ac2cc1
Merge branch 'main' into local-setup
rajadilipkolli Oct 4, 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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ ui/.pnp.loader.mjs
!ui/.yarn/plugins
!ui/.yarn/releases
!ui/.yarn/sdks
!ui/.yarn/versions
!ui/.yarn/versions
api/static/
api/beat.pid
9 changes: 9 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,12 @@ vscode:
extensions:
- ms-python.python
- Vue.volar

ports:
- port: 3000
visibility: public
- port: 5050
visibility: public
- port: 8000
visibility: public
onOpen: open-browser
53 changes: 51 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,59 @@ username: admin
password: foliom4n
```

This will build the container images for backend, frontend and all dependent services
and may take quite a while to finish.

---

This will build the container images for backend, frontend and all dependent services
and may take quite a while to finish.
### Running Locally (Minimal Docker Setup)

If you want to run the application locally with minimal Docker usage (only loading timedb and cache services in Docker), follow these steps:

#### Step 1: Start Local Environment

**On Windows:**
```bash
run-local.bat
```

**On Linux/Mac:**
```bash
./run-local.sh
```

This will:
1. Start only the required Docker containers (timedb, cache, and pgadmin)
2. Set up a Python virtual environment
3. Install all Python dependencies
4. Configure the Django environment to connect to your local Docker containers
5. Run database migrations
6. Start the Django API server
7. Install Node.js dependencies for the UI
8. Start the UI development server

#### Step 2: Start Celery for Background Tasks (Optional)

**On Windows:**
```bash
run-celery-local.bat
```

This will start Celery worker and beat scheduler processes for background tasks.

#### Step 3: Access Your Application

Once everything is running, you can access:
- API: http://localhost:8000
- UI: http://localhost:3000
- PGAdmin: http://localhost:5050 (login: pgadmin4@pgadmin.org / admin)

#### Step 4: Shutting Down

Stop the Django and UI processes, then stop the Docker containers:
```bash
docker-compose -f docker-compose.local.yml down
```

## Features

Expand Down
7 changes: 7 additions & 0 deletions api/.env.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DEBUG=on
SECRET_KEY='%k8!x4pm=mf!iqz^jws)ijn=)-md-uf_i=^mya1t*d!4f#^74k'
DATABASE_URL=psql://postgres:foliop4sswd@localhost:15432/folioman
CACHE_URL=rediscache://localhost:16379/1?client_class=django_redis.client.DefaultClient&timeout=86400
CELERY_BROKER_URL=redis://localhost:16379/3
CELERY_RESULT_BACKEND=redis://localhost:16379/5
ENVIRONMENT="dev"
6 changes: 4 additions & 2 deletions api/mutualfunds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ class Portfolio(models.Model):
pan = models.CharField(max_length=10, null=True, blank=True)

class Meta:
unique_together = ("user_id", "name")
constraints = [
models.UniqueConstraint(fields=['user_id', 'name'], name='unique_user_name')
]

def __str__(self):
return self.name
Expand All @@ -103,7 +105,7 @@ class Folio(models.Model):
"""Mutual Fund Folio"""

amc = models.ForeignKey(AMC, models.PROTECT)
portfolio = models.ForeignKey(Portfolio, models.CASCADE, related_name="folios")
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name="folios")
number = models.CharField(max_length=128, unique=True)
pan = models.CharField(max_length=10, null=True, blank=True)
kyc = models.BooleanField(default=False)
Expand Down
58 changes: 55 additions & 3 deletions api/mutualfunds/tests.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from decimal import Decimal
from django.test import TestCase
from tablib import Dataset

from mutualfunds.importers.cas import import_cas
from mutualfunds.models import Portfolio, Folio
from mutualfunds.importers.master import FundSchemeResource
from mutualfunds.models import Portfolio, Folio, FundScheme


class TestImportCas(TestCase):
Expand All @@ -21,17 +24,25 @@ def test_import_cas_valid_email_name(self):
result = import_cas(self.data, self.user_id)
self.assertIsNotNone(result)

# Additional assertions
portfolio = Portfolio.objects.get(email="test@example.com")
self.assertEqual(portfolio.name, "Test User")
self.assertEqual(portfolio.user_id, self.user_id)

def test_import_cas_invalid_email(self):
# Test case for invalid email
self.data["investor_info"]["email"] = ""
with self.assertRaises(ValueError):
import_cas(self.data, self.user_id)

# Additional assertions
self.assertFalse(Portfolio.objects.filter(email="").exists())

def test_import_cas_new_folio_creation(self):
# Test case for creating a new folio
self.data["folios"] = [{
"folio": "123",
"KYC" : "OK",
"KYC": "OK",
"PANKYC": "OK",
"PAN": "ABCDE1234F",
"schemes": [{
Expand Down Expand Up @@ -61,8 +72,30 @@ def test_import_cas_new_folio_creation(self):
import_cas(self.data, self.user_id)
self.assertEqual(Folio.objects.count(), 1)

# Additional assertions
folio = Folio.objects.get(number="123")
self.assertTrue(folio.kyc)
self.assertTrue(folio.pan_kyc)
self.assertEqual(folio.pan, "ABCDE1234F")

# Additional assertions for transactions and schemes
schemes = folio.schemes.all()
self.assertEqual(schemes.count(), 1)

scheme = schemes.first()
self.assertEqual(scheme.scheme_id, FundScheme.objects.get(isin="INF846K01EW2").id)

transactions = scheme.transactions.all()
self.assertEqual(transactions.count(), 1)

transaction = transactions.first()
self.assertEqual(transaction.amount, Decimal('1000.0'))
self.assertEqual(transaction.balance, Decimal('23.711'))
self.assertEqual(transaction.nav, Decimal('42.1747'))
self.assertEqual(transaction.units, Decimal('23.711'))
self.assertEqual(transaction.description, "Purchase")
def test_import_cas_missing_kyc(self):
# Test case for creating a new folio
# Test case for missing KYC
self.data["folios"] = [{
"folio": "124",
"PANKYC": "OK",
Expand Down Expand Up @@ -95,4 +128,23 @@ def test_import_cas_missing_kyc(self):
import_cas(self.data, self.user_id)
self.assertEqual(Folio.objects.count(), 1)


class TestDependencies(TestCase):

def test_import_export(self):
# Test django-import-export functionality
resource = FundSchemeResource()
dataset = Dataset(headers=["sid", "name", "rta", "plan", "rta_code", "amc_code", "amfi_code", "isin", "start_date", "end_date", "amc_id", "category_id"])
dataset.append([1, "Test Scheme", "Test RTA", "DIRECT", "123", "456", "789", "INF123456789", "2025-01-01", "2025-12-31", 1, 1])
result = resource.import_data(dataset, dry_run=True)
self.assertFalse(result.has_errors(), "Import should not have errors")

def test_tablib(self):
# Test tablib[pandas] functionality
dataset = Dataset(headers=["Name", "Age"])
dataset.headers = ["Name", "Age"]
dataset.append(["Alice", 30])
dataset.append(["Bob", 25])
self.assertEqual(len(dataset), 2, "Dataset should have 2 rows")

# Additional test cases can be added here to cover other functionalities
8 changes: 4 additions & 4 deletions api/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
casparser[mupdf]==0.5.5
celery==5.4.0
Django==5.1.5
Django==5.1.13
django-celery-beat==2.7.0
django-celery-results==2.5.1
django-click>=2.3.0
django-environ==0.11.2
django-import-export==2.9.0
django-redis==5.4.0
djangorestframework==3.15.2
djangorestframework-simplejwt==5.3.1
gunicorn==22.0.0
djangorestframework-simplejwt==5.5.1
gunicorn==23.0.0
lxml
numpy
pandas
psycopg2
python-dateutil>=2.8.1
pytz>=2021.1
redis==5.2.1
requests==2.32.3
requests==2.32.4
sentry-sdk==2.19.2
tablib[pandas]==3.3.0
xirr==0.1.8
8 changes: 7 additions & 1 deletion api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"""

import datetime
import os
from pathlib import Path

import click.exceptions
Expand All @@ -22,7 +23,12 @@
# set casting, default value
DEBUG=(bool, False)
)
environ.Env.read_env()

# Ensure the .env file is loaded
BASE_DIR = Path(__file__).resolve().parent
ENV_FILE = BASE_DIR / '.env'
if ENV_FILE.exists():
environ.Env.read_env(str(ENV_FILE))

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent
Expand Down
37 changes: 37 additions & 0 deletions docker-compose.local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

services:
timedb:
image: timescale/timescaledb:latest-pg17
restart: unless-stopped
volumes:
- folioman-db:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: foliop4sswd
POSTGRES_DB: folioman
ports:
- "127.0.0.1:15432:5432"
cache:
image: redis:alpine
command: [ "redis-server", "--appendonly", "yes" ]
volumes:
- folioman-cache:/data
ports:
- "127.0.0.1:16379:6379"
pgadmin:
image: dpage/pgadmin4
extra_hosts: [ 'host.docker.internal:host-gateway' ]
environment:
- PGADMIN_DEFAULT_EMAIL=pgadmin4@pgadmin.org
- PGADMIN_DEFAULT_PASSWORD=admin
- PGADMIN_CONFIG_SERVER_MODE=False
- PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED=False
ports:
- "5050:80"
depends_on:
- timedb

volumes:
folioman-db:
folioman-cache:

5 changes: 2 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '3.4'

x-app: &default-app
#image: backend-image:latest
Expand Down Expand Up @@ -30,7 +29,7 @@ x-celery: &celery-app

services:
timedb:
image: timescale/timescaledb:latest-pg16
image: timescale/timescaledb:latest-pg17
restart: unless-stopped
volumes:
- folioman-db:/var/lib/postgresql/data
Expand All @@ -54,7 +53,7 @@ services:
- ./ui:/home/node/ui
- folioman-ui-node-modules:/home/node/ui/node_modules
working_dir: /home/node/ui
command: sh -c "yarn install && npm run build && npm run start"
command: sh -c "yarn install && yarn run build && yarn run start"
api:
build: ./api
command: bash -c "/wait &&
Expand Down
37 changes: 37 additions & 0 deletions run-celery-local.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@echo off
REM Script to run Celery locally with minimal Docker usage on Windows

REM Make sure Redis is running
echo Checking if Redis is running...
docker-compose -f docker-compose.local.yml ps | findstr "cache" | findstr "Up" >nul
if errorlevel 1 (
echo Redis is not running. Starting required Docker services...
docker-compose -f docker-compose.local.yml up -d cache
timeout /t 5
)

REM Activate virtual environment
call .venv\Scripts\activate.bat

cd api

REM Check if .env exists (should have been created by run-local.bat)
if not exist .env (
echo Setting up local environment file...
copy .env.local .env
)

REM Start Celery beat
echo Starting Celery beat...
start "Celery Beat" celery -A taskman beat -l INFO --pidfile=./beat.pid

REM Start Celery worker
echo Starting Celery worker...
start "Celery Worker" celery -A taskman worker -l INFO

echo ================================================
echo Celery services are now running
echo ================================================
echo Press Ctrl+C in each terminal window to stop the services

cmd /k
37 changes: 37 additions & 0 deletions run-celery-local.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash
# Script to run Celery locally with minimal Docker usage on Linux/macOS

# Make sure Redis is running
echo "Checking if Redis is running..."
if ! docker-compose -f docker-compose.local.yml ps | grep -q "cache.*Up"; then
echo "Redis is not running. Starting required Docker services..."
docker-compose -f docker-compose.local.yml up -d cache
sleep 5
fi

# Activate virtual environment from .venv folder instead of venv
source .venv/bin/activate

cd api || { echo "Error: Cannot change to api directory"; exit 1; }

# Check if .env exists (should have been created by run-local.sh)
if [ ! -f .env ]; then
echo "Setting up local environment file..."
cp .env.local .env
fi

# Start Celery beat in background
echo "Starting Celery beat..."
celery -A taskman beat -l INFO --pidfile=./beat.pid --detach

# Start Celery worker in background
echo "Starting Celery worker..."
celery -A taskman worker -l INFO --detach

echo "================================================"
echo "Celery services are now running"
echo "================================================"
echo "Run 'pkill -f celery' to stop all Celery processes"

# Keep terminal open
exec $SHELL
Loading