diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml
deleted file mode 100644
index 7db53b6..0000000
--- a/.gitea/workflows/ci.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: CI
-
-# Same checks deploy-to-s3.yml gates on, run before a change reaches main.
-# main itself is excluded because the deploy workflow already runs them there;
-# without branches-ignore every merge would run the suite twice.
-on:
- push:
- branches-ignore:
- - main
- pull_request:
-
-jobs:
- checks:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
-
- - name: Set up Node
- uses: actions/setup-node@v4
- with:
- node-version: '22'
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - name: Install Python dependencies
- run: |
- python -m pip install --upgrade pip
- pip install -e ".[dev]"
- pip install -r backend/requirements.txt
-
- - name: Run checks
- run: bash scripts/ci-checks.sh
diff --git a/.gitea/workflows/deploy-to-s3.yml b/.gitea/workflows/deploy-to-s3.yml
index c5883f0..5e2b169 100644
--- a/.gitea/workflows/deploy-to-s3.yml
+++ b/.gitea/workflows/deploy-to-s3.yml
@@ -1,64 +1,25 @@
name: Deploy to S3
-# main only. Everything else is covered by ci.yml, which runs the same checks
-# without deploying.
on:
push:
- branches:
+ branches:
- main
jobs:
- # Nothing was verified before this existed: a frontend that failed to compile
- # would zip and ship exactly like a working one. `deploy` now needs this job,
- # so a red main does not reach the bucket.
- checks:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
-
- # 22 to match frontend/Dockerfile, so CI resolves the same tree the
- # production image builds from.
- - name: Set up Node
- uses: actions/setup-node@v4
- with:
- node-version: '22'
-
- # 3.11 is the floor in pyproject.toml and the version the project's conda
- # env runs.
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.11'
-
- - name: Install Python dependencies
- run: |
- python -m pip install --upgrade pip
- pip install -e ".[dev]"
- pip install -r backend/requirements.txt
-
- - name: Run checks
- run: bash scripts/ci-checks.sh
-
deploy:
- needs: checks
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- # frontend/node_modules is excluded, and that is safe because of what
- # happens to this object downstream. CodeDeploy pulls it, extracts to
- # /opt/codedeploy-extracted-5, copies the tree to
- # /home/ec2-user/utopia-ai-hr-ats-portal-deployment-group and runs
- # `docker compose --env-file ./backend/.env up -d --build`. The only Node
- # service is the frontend, whose image does `npm ci` from the lockfile,
- # and frontend/.dockerignore excludes node_modules/ from the build context
- # outright. So the committed tree was carried into every artifact and then
- # thrown away unread. It was 90 MB of a 33 MB compressed upload.
- #
- # node_modules is still tracked in git, which is the reason it was here at
- # all. Untracking it is a separate change and affects other branches.
+ - name: Configure AWS credentials
+ env:
+ AWS_ACCESS_KEY_ID: ${{ secrets.DEVOPS_USER_AWS_ACCESS_KEY_ID }}
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.DEVOPS_USER_AWS_SECRET_ACCESS_KEY }}
+ AWS_DEFAULT_REGION: us-east-1
+ run: |
+ echo "AWS credentials configured"
+
- name: Archive project
run: |
apt-get update -y
@@ -66,9 +27,8 @@ jobs:
zip -r utopia-ai-hr-ats-portal.zip . \
-x ".git/*" \
-x ".gitea/*" \
- -x ".gitignore" \
- -x "frontend/node_modules/*" \
- -x "*.DS_Store"
+ -x ".gitignore/*" \
+ -x "*.DS_Store"
- name: Install AWS CLI
run: |
@@ -77,18 +37,19 @@ jobs:
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -q awscliv2.zip
./aws/install
- aws --version
+ aws --version
- # The credentials live only on this step. There used to be a separate
- # "Configure AWS credentials" step above that set the same three variables
- # and then only echoed a message — env: is scoped to its own step, so
- # those values were discarded before anything could use them. It was doing
- # nothing, and it read as though credentials were set up globally.
- name: Upload files to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.DEVOPS_USER_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEVOPS_USER_AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: us-east-1
run: |
- echo "Uploading repo contents to S3..."
- aws s3 cp utopia-ai-hr-ats-portal.zip s3://utopia-ai-s3-repo-bucket/utopia-ai-hr-ats-portal.zip
+ echo "Uploading repo contents to S3..."
+ aws s3 cp utopia-ai-hr-ats-portal.zip s3://utopia-ai-s3-repo-bucket/utopia-ai-hr-ats-portal.zip
+
+
+
+
+
+
diff --git a/backend/job/candidate/views.py b/backend/job/candidate/views.py
index 390b001..d2ff7dd 100644
--- a/backend/job/candidate/views.py
+++ b/backend/job/candidate/views.py
@@ -1713,26 +1713,9 @@ class CandidateView:
email=_norm_email(row.get("email"))
if email not in packed:
continue
- packed[email]["applications"].append(row)
+ # ATS scores are job-assignment history, not applications.
if "candidates" not in packed[email]["present_in"]:
packed[email]["present_in"].append("candidates")
- pipeline_jobs={}
- for email,pack in packed.items():
- jobs=set()
- for row in pack["applications"]:
- if row.get("source") in ("inbox","manual") and row.get("job_post_id"):
- jobs.add(row["job_post_id"])
- pipeline_jobs[email]=jobs
- for email,pack in packed.items():
- jobs=pipeline_jobs.get(email) or set()
- if not jobs:
- continue
- # ATS scores for a job the person already applied to are not a
- # separate application — they duplicate the pipeline row.
- pack["applications"]=[
- row for row in pack["applications"]
- if not (row.get("source")=="ats" and row.get("job_post_id") in jobs)
- ]
for pack in packed.values():
pack["applications"].sort(key=lambda r: r.get("applied_at") or "",reverse=True)
present=pack["present_in"]
diff --git a/frontend/src/components/ReapplicantHistory.jsx b/frontend/src/components/ReapplicantHistory.jsx
index a37d583..22a41fa 100644
--- a/frontend/src/components/ReapplicantHistory.jsx
+++ b/frontend/src/components/ReapplicantHistory.jsx
@@ -64,12 +64,7 @@ export function candidateApplicationsOf(row) {
const self = syntheticCurrentApplication(row)
if (self) items.push(self)
}
- items.sort((a, b) => {
- const ac = isSameApplication(a, current) ? 1 : 0
- const bc = isSameApplication(b, current) ? 1 : 0
- if (ac !== bc) return bc - ac
- return (appliedAtMs(b?.applied_at) ?? 0) - (appliedAtMs(a?.applied_at) ?? 0)
- })
+ items.sort((a, b) => (appliedAtMs(b?.applied_at) ?? 0) - (appliedAtMs(a?.applied_at) ?? 0))
return items
}
diff --git a/frontend/src/screens/CandidateProfile.jsx b/frontend/src/screens/CandidateProfile.jsx
index 6c8357d..56dd373 100644
--- a/frontend/src/screens/CandidateProfile.jsx
+++ b/frontend/src/screens/CandidateProfile.jsx
@@ -19,7 +19,7 @@ import * as formsApi from '../api/forms'
import * as pipelineApi from '../api/pipeline'
import * as s3Api from '../api/s3'
import CandidateFormsTab from './CandidateForms'
-import { PreviousApplications, ReappliedBadge } from '../components/ReapplicantHistory'
+import { PreviousApplications, ReappliedBadge, candidateApplicationsOf } from '../components/ReapplicantHistory'
import { fmtDate, fmtTime, toDate } from '../lib/format'
import { companies, moneyK, pick } from '../data/seed'
@@ -361,7 +361,7 @@ export default function CandidateProfile({
{rating ? `${rating.toFixed(1)} / 5.0` : 'Not rated'}
-
+
{(live.match_summary || live.match_reasoning) && (