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
54 changes: 54 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: hsdb_test
POSTGRES_USER: hsuser
POSTGRES_PASSWORD: hspass
ports:
- 5432:5432
options: >-
--health-cmd="pg_isready -U hsuser -d hsdb_test"
--health-interval=10s
--health-timeout=5s
--health-retries=5

env:
SECRET_KEY: ci-secret-key-not-used-in-prod
DEBUG: "True"
ALLOWED_HOSTS: localhost,127.0.0.1
DATABASE_URL: postgres://hsuser:hspass@localhost:5432/hsdb_test
DJANGO_SETTINGS_MODULE: core.settings.development

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip

- name: Install dependencies
run: pip install -r requirements.txt

- name: Enable pg_trgm extension
run: psql postgres://hsuser:hspass@localhost:5432/hsdb_test -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"

- name: Run migrations
run: python manage.py migrate --noinput

- name: Run tests
run: python manage.py test app --verbosity=2
95 changes: 45 additions & 50 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,55 +1,50 @@
name: Deploy Production

on:
workflow_run:
workflows: ["CI"]
branches: [main]
types:
- completed
workflow_run:
workflows: ["CI"]
branches: [main]
types:
- completed

jobs:
deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' }}

runs-on: ubuntu-latest

steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}

script: |
set -e

cd /Hs_codes_api

echo "Pulling latest images..."
docker compose pull

echo "Starting updated containers..."
docker compose up -d

echo "Waiting for application..."
sleep 15

echo "Running migrations..."
docker compose exec -T web python manage.py migrate --noinput

echo "Checking health endpoint..."

for i in $(seq 1 20); do
if curl -fsS http://localhost:8001/api/v1/health/ > /dev/null; then
echo "Health check passed"
exit 0
fi

echo "Waiting for healthy container..."
sleep 5
done

echo "Health check failed"
exit 1
deploy:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest

steps:
- name: Deploy to VPS
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ secrets.VPS_PORT }}
script: |
set -e
cd /Hs_codes_api

echo "Pulling latest code..."
git pull origin main

echo "Building and starting containers..."
docker compose up -d --build

echo "Waiting for application to start..."
sleep 15

echo "Running migrations..."
docker compose exec -T web python manage.py migrate --noinput

echo "Checking health endpoint..."
for i in $(seq 1 20); do
if curl -fsS http://localhost:8001/api/v1/health/ > /dev/null; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i/20 — waiting..."
sleep 5
done

echo "Health check failed after 100s"
exit 1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ build/
*.egg-info/

celerybeat-schedule
core/media/
logfile
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

RUN groupadd -r appuser && useradd -r -g appuser appuser
RUN groupadd -r appuser && \
useradd -r -g appuser -m -d /home/appuser appuser

COPY --from=builder /install /usr/local

Expand Down
23 changes: 16 additions & 7 deletions app/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ class Migration(migrations.Migration):

operations = [
TrigramExtension(),

migrations.CreateModel(
name="Category",
fields=[
Expand Down Expand Up @@ -65,7 +64,9 @@ class Migration(migrations.Migration):
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(blank=True, null=True, verbose_name="last login"),
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
Expand All @@ -92,15 +93,21 @@ class Migration(migrations.Migration):
),
(
"first_name",
models.CharField(blank=True, max_length=150, verbose_name="first name"),
models.CharField(
blank=True, max_length=150, verbose_name="first name"
),
),
(
"last_name",
models.CharField(blank=True, max_length=150, verbose_name="last name"),
models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
),
(
"email",
models.EmailField(blank=True, max_length=254, verbose_name="email address"),
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
),
(
"is_staff",
Expand All @@ -120,7 +127,9 @@ class Migration(migrations.Migration):
),
(
"date_joined",
models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined"),
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
),
),
(
"role",
Expand Down Expand Up @@ -210,4 +219,4 @@ class Migration(migrations.Migration):
],
},
),
]
]
2 changes: 1 addition & 1 deletion app/services/category_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@

def get_or_create_category_for_hs_code(hs_code: str) -> "Category":
"""Derive category from HS code chapter prefix and get or create it."""
from .models import Category
from app.models import Category

chapter = hs_code[:2]
name = HS_CHAPTER_CATEGORIES.get(chapter, f"Chapter {chapter}")
Expand Down
15 changes: 9 additions & 6 deletions app/services/file_upload_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@ class UploadResult:
def process_hs_code_csv(uploaded_file) -> UploadResult:
text = _decode_file(uploaded_file)
rows = _parse_csv(text)

print(text)

hs_code_file = HsCodeFile.objects.create(hs_code_file=uploaded_file)
objects, skipped_blank = _build_objects(rows, hs_code_file)

created_objects = HsCode.objects.bulk_create(objects, ignore_conflicts=True)
incoming_codes = [obj.hs_code for obj in objects]
existing_count = HsCode.objects.filter(hs_code__in=incoming_codes).count()

HsCode.objects.bulk_create(objects, ignore_conflicts=True)

created = len(objects) - existing_count

return UploadResult(
created=len(created_objects),
skipped_duplicates=len(objects) - len(created_objects),
created=created,
skipped_duplicates=existing_count,
skipped_blank_rows=skipped_blank,
total_rows=len(rows),
hs_code_file_id=hs_code_file.pk,
Expand Down Expand Up @@ -83,4 +86,4 @@ def _build_objects(rows, hs_code_file):
)
)

return objects, skipped_blank
return objects, skipped_blank
Loading
Loading