Compare commits
22 Commits
feature/ru
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| fab1866fc8 | |||
| 0dc03dd380 | |||
| 0c42842655 | |||
| 49e4f74768 | |||
| 2575e2be95 | |||
| a132efd907 | |||
| f775164212 | |||
| 72b7f11c41 | |||
| ff09524503 | |||
| 9ec96d4af7 | |||
| 5d2965beae | |||
| 8a4e46d872 | |||
| 7ebf09c284 | |||
| 1a06d60f2d | |||
| 6773162426 | |||
| ecb599fba7 | |||
| 70d8a4786e | |||
| ee48afa5bd | |||
| 473bef96e9 | |||
| d3fb929443 | |||
| de8b36ae2b | |||
| 92a3b60878 |
2
.gitignore
vendored
@@ -220,5 +220,5 @@ local/
|
|||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
# Local file uploads
|
# Local file uploads
|
||||||
src/TrueCV.Web/uploads/
|
src/RealCV.Web/uploads/
|
||||||
logs/
|
logs/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV UK APIs & Integration Resources
|
# RealCV UK APIs & Integration Resources
|
||||||
|
|
||||||
**Last Updated:** January 2026
|
**Last Updated:** January 2026
|
||||||
**Purpose:** Practical guide for obtaining API access and integration details
|
**Purpose:** Practical guide for obtaining API access and integration details
|
||||||
@@ -159,13 +159,13 @@
|
|||||||
|
|
||||||
### Overview
|
### Overview
|
||||||
- **Service:** UK company registration and officer records
|
- **Service:** UK company registration and officer records
|
||||||
- **Status:** ✅ Already integrated in TrueCV
|
- **Status:** ✅ Already integrated in RealCV
|
||||||
- **Coverage:** 3.4M registered UK companies
|
- **Coverage:** 3.4M registered UK companies
|
||||||
|
|
||||||
### Enhancement Opportunities
|
### Enhancement Opportunities
|
||||||
|
|
||||||
#### Existing Implementation
|
#### Existing Implementation
|
||||||
- See: `/src/TrueCV.Infrastructure/ExternalApis/CompaniesHouseClient.cs`
|
- See: `/src/RealCV.Infrastructure/ExternalApis/CompaniesHouseClient.cs`
|
||||||
- Current: Company search + basic data lookup
|
- Current: Company search + basic data lookup
|
||||||
- Rate Limit: 500 requests/hour (generous)
|
- Rate Limit: 500 requests/hour (generous)
|
||||||
|
|
||||||
@@ -326,7 +326,7 @@
|
|||||||
- uCheck
|
- uCheck
|
||||||
- Certn
|
- Certn
|
||||||
|
|
||||||
### Recommended Vendor for TrueCV Integration
|
### Recommended Vendor for RealCV Integration
|
||||||
|
|
||||||
**Verifile** (Suggested)
|
**Verifile** (Suggested)
|
||||||
- **Website:** https://www.verifile.io/
|
- **Website:** https://www.verifile.io/
|
||||||
@@ -345,7 +345,7 @@
|
|||||||
2. Negotiate revenue share (typically 20-30% for platform)
|
2. Negotiate revenue share (typically 20-30% for platform)
|
||||||
3. Integrate DBS check submission API
|
3. Integrate DBS check submission API
|
||||||
4. Build compliance/audit trail layer
|
4. Build compliance/audit trail layer
|
||||||
5. White-label DBS reports in TrueCV UI
|
5. White-label DBS reports in RealCV UI
|
||||||
|
|
||||||
### Timeline
|
### Timeline
|
||||||
- **Vendor selection:** 1-2 weeks
|
- **Vendor selection:** 1-2 weeks
|
||||||
@@ -371,7 +371,7 @@
|
|||||||
1. **Contact accredited vendors:** Verifile, DDC, or similar
|
1. **Contact accredited vendors:** Verifile, DDC, or similar
|
||||||
2. **Explain use case:** CV verification platform
|
2. **Explain use case:** CV verification platform
|
||||||
3. **Request sub-licensing:** Access to their HMRC integration
|
3. **Request sub-licensing:** Access to their HMRC integration
|
||||||
4. **Build wrapper:** TrueCV UI calls vendor API
|
4. **Build wrapper:** RealCV UI calls vendor API
|
||||||
|
|
||||||
#### Vendors with HMRC Access
|
#### Vendors with HMRC Access
|
||||||
- Verifile (https://www.verifile.io/)
|
- Verifile (https://www.verifile.io/)
|
||||||
@@ -407,7 +407,7 @@
|
|||||||
|
|
||||||
### This Week
|
### This Week
|
||||||
1. **Email HEDD:** partnerships@hedd.ac.uk with:
|
1. **Email HEDD:** partnerships@hedd.ac.uk with:
|
||||||
- Company info (TrueCV)
|
- Company info (RealCV)
|
||||||
- Use case (CV verification for UK recruiters)
|
- Use case (CV verification for UK recruiters)
|
||||||
- Expected volume (start with 100/month)
|
- Expected volume (start with 100/month)
|
||||||
- Request: API access or partnership discussion
|
- Request: API access or partnership discussion
|
||||||
@@ -468,15 +468,15 @@ For each API integration, ensure:
|
|||||||
## Contact Template for API Requests
|
## Contact Template for API Requests
|
||||||
|
|
||||||
```
|
```
|
||||||
Subject: API Integration Request - TrueCV Recruitment Verification Platform
|
Subject: API Integration Request - RealCV Recruitment Verification Platform
|
||||||
|
|
||||||
Dear [Service] Team,
|
Dear [Service] Team,
|
||||||
|
|
||||||
We are developing TrueCV, a UK-focused CV verification platform for recruitment agencies and corporate HR departments. As part of our Phase 1 launch (Q1 2026), we would like to integrate with [Service Name] to verify [candidate credentials] in real-time during the hiring process.
|
We are developing RealCV, a UK-focused CV verification platform for recruitment agencies and corporate HR departments. As part of our Phase 1 launch (Q1 2026), we would like to integrate with [Service Name] to verify [candidate credentials] in real-time during the hiring process.
|
||||||
|
|
||||||
Use Case:
|
Use Case:
|
||||||
- Candidates upload CV during job application
|
- Candidates upload CV during job application
|
||||||
- TrueCV extracts education/qualification claims
|
- RealCV extracts education/qualification claims
|
||||||
- Real-time verification against [Service] records
|
- Real-time verification against [Service] records
|
||||||
- Fraud flags generated for recruiter review
|
- Fraud flags generated for recruiter review
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ Please advise next steps.
|
|||||||
|
|
||||||
Best regards,
|
Best regards,
|
||||||
[Your Name]
|
[Your Name]
|
||||||
TrueCV
|
RealCV
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
TRUECV UK MARKET STRATEGY - COMPLETE DELIVERY PACKAGE
|
TRUECV UK MARKET STRATEGY - COMPLETE DELIVERY PACKAGE
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
Project: Rethinking TrueCV Feature Priorities with UK-Only Focus
|
Project: Rethinking RealCV Feature Priorities with UK-Only Focus
|
||||||
Date Delivered: January 20, 2026
|
Date Delivered: January 20, 2026
|
||||||
Total Documents: 8 comprehensive strategy guides
|
Total Documents: 8 comprehensive strategy guides
|
||||||
Total Content: ~200 pages
|
Total Content: ~200 pages
|
||||||
@@ -68,7 +68,7 @@ MARKET OPPORTUNITY:
|
|||||||
✓ £4.2B annual cost of CV fraud to UK employers
|
✓ £4.2B annual cost of CV fraud to UK employers
|
||||||
✓ 1 in 5 UK candidates falsify university degrees
|
✓ 1 in 5 UK candidates falsify university degrees
|
||||||
✓ 24% of screened CVs fail verification
|
✓ 24% of screened CVs fail verification
|
||||||
✓ £3.3M serviceable market for TrueCV
|
✓ £3.3M serviceable market for RealCV
|
||||||
✓ No existing competitor offers integrated UK CV verification
|
✓ No existing competitor offers integrated UK CV verification
|
||||||
|
|
||||||
COMPETITIVE ADVANTAGE:
|
COMPETITIVE ADVANTAGE:
|
||||||
@@ -136,7 +136,7 @@ EXPECTED OUTCOMES:
|
|||||||
COMPETITIVE LANDSCAPE ANALYSIS
|
COMPETITIVE LANDSCAPE ANALYSIS
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
COMPETITOR FEATURES OFFERED TrueCV ADVANTAGE
|
COMPETITOR FEATURES OFFERED RealCV ADVANTAGE
|
||||||
─────────────────────────────────────────────────────────────
|
─────────────────────────────────────────────────────────────
|
||||||
Workable ATS + basic screening HEDD integration (exclusive)
|
Workable ATS + basic screening HEDD integration (exclusive)
|
||||||
Deel Global hiring + screening UK-specific stack
|
Deel Global hiring + screening UK-specific stack
|
||||||
@@ -150,7 +150,7 @@ No existing competitor integrates:
|
|||||||
- GMC/NMC healthcare registers
|
- GMC/NMC healthcare registers
|
||||||
- Timeline fraud detection
|
- Timeline fraud detection
|
||||||
- Companies House director verification
|
- Companies House director verification
|
||||||
→ TrueCV is only player filling this gap
|
→ RealCV is only player filling this gap
|
||||||
|
|
||||||
MOAT BUILDING:
|
MOAT BUILDING:
|
||||||
- Deep integrations difficult to replicate (6+ months each)
|
- Deep integrations difficult to replicate (6+ months each)
|
||||||
@@ -365,17 +365,17 @@ FOR SALES/MARKETING:
|
|||||||
DOCUMENT LOCATIONS
|
DOCUMENT LOCATIONS
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
All files have been created in: /mnt/d/Git/TrueCV/
|
All files have been created in: /mnt/d/Git/RealCV/
|
||||||
|
|
||||||
FILE STRUCTURE:
|
FILE STRUCTURE:
|
||||||
/mnt/d/Git/TrueCV/QUICK_REFERENCE.md (Start here)
|
/mnt/d/Git/RealCV/QUICK_REFERENCE.md (Start here)
|
||||||
/mnt/d/Git/TrueCV/EXECUTIVE_SUMMARY.md (Execs/investors)
|
/mnt/d/Git/RealCV/EXECUTIVE_SUMMARY.md (Execs/investors)
|
||||||
/mnt/d/Git/TrueCV/UK_FEATURE_PRIORITIZATION.md (Product)
|
/mnt/d/Git/RealCV/UK_FEATURE_PRIORITIZATION.md (Product)
|
||||||
/mnt/d/Git/TrueCV/PHASE1_TECHNICAL_IMPLEMENTATION.md (Engineering)
|
/mnt/d/Git/RealCV/PHASE1_TECHNICAL_IMPLEMENTATION.md (Engineering)
|
||||||
/mnt/d/Git/TrueCV/UK_MARKET_STRATEGY.md (Strategy/Sales/Marketing)
|
/mnt/d/Git/RealCV/UK_MARKET_STRATEGY.md (Strategy/Sales/Marketing)
|
||||||
/mnt/d/Git/TrueCV/API_RESOURCES_AND_CONTACTS.md (Implementation)
|
/mnt/d/Git/RealCV/API_RESOURCES_AND_CONTACTS.md (Implementation)
|
||||||
/mnt/d/Git/TrueCV/README_UK_STRATEGY.md (Navigation)
|
/mnt/d/Git/RealCV/README_UK_STRATEGY.md (Navigation)
|
||||||
/mnt/d/Git/TrueCV/INDEX.md (Reference index)
|
/mnt/d/Git/RealCV/INDEX.md (Reference index)
|
||||||
|
|
||||||
FILES READY FOR USE IMMEDIATELY.
|
FILES READY FOR USE IMMEDIATELY.
|
||||||
|
|
||||||
@@ -445,7 +445,7 @@ PLANNED UPDATES:
|
|||||||
CLOSING NOTES
|
CLOSING NOTES
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|
||||||
This strategy document represents a comprehensive analysis of TrueCV's
|
This strategy document represents a comprehensive analysis of RealCV's
|
||||||
opportunity in the UK CV verification market. It provides:
|
opportunity in the UK CV verification market. It provides:
|
||||||
|
|
||||||
✓ Clear market opportunity quantification (£3.3M addressable)
|
✓ Clear market opportunity quantification (£3.3M addressable)
|
||||||
|
|||||||
20
Dockerfile
@@ -3,11 +3,11 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
|||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
# Copy solution and project files first for better layer caching
|
# Copy solution and project files first for better layer caching
|
||||||
COPY TrueCV.sln ./
|
COPY RealCV.sln ./
|
||||||
COPY src/TrueCV.Domain/TrueCV.Domain.csproj src/TrueCV.Domain/
|
COPY src/RealCV.Domain/RealCV.Domain.csproj src/RealCV.Domain/
|
||||||
COPY src/TrueCV.Application/TrueCV.Application.csproj src/TrueCV.Application/
|
COPY src/RealCV.Application/RealCV.Application.csproj src/RealCV.Application/
|
||||||
COPY src/TrueCV.Infrastructure/TrueCV.Infrastructure.csproj src/TrueCV.Infrastructure/
|
COPY src/RealCV.Infrastructure/RealCV.Infrastructure.csproj src/RealCV.Infrastructure/
|
||||||
COPY src/TrueCV.Web/TrueCV.Web.csproj src/TrueCV.Web/
|
COPY src/RealCV.Web/RealCV.Web.csproj src/RealCV.Web/
|
||||||
|
|
||||||
# Restore dependencies
|
# Restore dependencies
|
||||||
RUN dotnet restore
|
RUN dotnet restore
|
||||||
@@ -16,7 +16,7 @@ RUN dotnet restore
|
|||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
|
|
||||||
# Build and publish
|
# Build and publish
|
||||||
WORKDIR /src/src/TrueCV.Web
|
WORKDIR /src/src/RealCV.Web
|
||||||
RUN dotnet publish -c Release -o /app/publish --no-restore
|
RUN dotnet publish -c Release -o /app/publish --no-restore
|
||||||
|
|
||||||
# Runtime stage
|
# Runtime stage
|
||||||
@@ -27,16 +27,16 @@ WORKDIR /app
|
|||||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Create non-root user for security
|
# Create non-root user for security
|
||||||
RUN groupadd -r truecv && useradd -r -g truecv truecv
|
RUN groupadd -r realcv && useradd -r -g realcv realcv
|
||||||
|
|
||||||
# Copy published app
|
# Copy published app
|
||||||
COPY --from=build /app/publish .
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
# Set ownership
|
# Set ownership
|
||||||
RUN chown -R truecv:truecv /app
|
RUN chown -R realcv:realcv /app
|
||||||
|
|
||||||
# Switch to non-root user
|
# Switch to non-root user
|
||||||
USER truecv
|
USER realcv
|
||||||
|
|
||||||
# Expose port
|
# Expose port
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
@@ -51,4 +51,4 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
|||||||
CMD curl -f http://localhost:8080/health || exit 1
|
CMD curl -f http://localhost:8080/health || exit 1
|
||||||
|
|
||||||
# Start the app
|
# Start the app
|
||||||
ENTRYPOINT ["dotnet", "TrueCV.Web.dll"]
|
ENTRYPOINT ["dotnet", "RealCV.Web.dll"]
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ RUN dotnet tool install --global dotnet-ef
|
|||||||
ENV PATH="$PATH:/root/.dotnet/tools"
|
ENV PATH="$PATH:/root/.dotnet/tools"
|
||||||
|
|
||||||
# Copy solution and project files
|
# Copy solution and project files
|
||||||
COPY TrueCV.sln ./
|
COPY RealCV.sln ./
|
||||||
COPY src/TrueCV.Domain/TrueCV.Domain.csproj src/TrueCV.Domain/
|
COPY src/RealCV.Domain/RealCV.Domain.csproj src/RealCV.Domain/
|
||||||
COPY src/TrueCV.Application/TrueCV.Application.csproj src/TrueCV.Application/
|
COPY src/RealCV.Application/RealCV.Application.csproj src/RealCV.Application/
|
||||||
COPY src/TrueCV.Infrastructure/TrueCV.Infrastructure.csproj src/TrueCV.Infrastructure/
|
COPY src/RealCV.Infrastructure/RealCV.Infrastructure.csproj src/RealCV.Infrastructure/
|
||||||
COPY src/TrueCV.Web/TrueCV.Web.csproj src/TrueCV.Web/
|
COPY src/RealCV.Web/RealCV.Web.csproj src/RealCV.Web/
|
||||||
|
|
||||||
# Restore dependencies
|
# Restore dependencies
|
||||||
RUN dotnet restore
|
RUN dotnet restore
|
||||||
@@ -20,7 +20,7 @@ RUN dotnet restore
|
|||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
|
|
||||||
# Build the project
|
# Build the project
|
||||||
RUN dotnet build src/TrueCV.Web/TrueCV.Web.csproj -c Release
|
RUN dotnet build src/RealCV.Web/RealCV.Web.csproj -c Release
|
||||||
|
|
||||||
# Run migrations on startup
|
# Run migrations on startup
|
||||||
ENTRYPOINT ["dotnet", "ef", "database", "update", "--project", "src/TrueCV.Infrastructure", "--startup-project", "src/TrueCV.Web", "--no-build", "-c", "Release"]
|
ENTRYPOINT ["dotnet", "ef", "database", "update", "--project", "src/RealCV.Infrastructure", "--startup-project", "src/RealCV.Web", "--no-build", "-c", "Release"]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV UK Market Opportunity - Executive Summary
|
# RealCV UK Market Opportunity - Executive Summary
|
||||||
|
|
||||||
**Prepared for:** Product Leadership
|
**Prepared for:** Product Leadership
|
||||||
**Date:** January 2026
|
**Date:** January 2026
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
## The Opportunity
|
## The Opportunity
|
||||||
|
|
||||||
**UK CV fraud costs employers £4.2B annually. Current verification takes 5-10 days. TrueCV can do it in seconds.**
|
**UK CV fraud costs employers £4.2B annually. Current verification takes 5-10 days. RealCV can do it in seconds.**
|
||||||
|
|
||||||
### Market Problem
|
### Market Problem
|
||||||
- **1 in 5 UK candidates** falsify university degrees
|
- **1 in 5 UK candidates** falsify university degrees
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## TrueCV's Solution
|
## RealCV's Solution
|
||||||
|
|
||||||
**Integrated CV verification platform leveraging UK-specific data sources:**
|
**Integrated CV verification platform leveraging UK-specific data sources:**
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
## Competitive Advantage
|
## Competitive Advantage
|
||||||
|
|
||||||
**TrueCV is the ONLY CV verification tool that:**
|
**RealCV is the ONLY CV verification tool that:**
|
||||||
|
|
||||||
1. ✅ Integrates with HEDD (no competitors do)
|
1. ✅ Integrates with HEDD (no competitors do)
|
||||||
2. ✅ Targets healthcare recruiting niche (GMC/NMC)
|
2. ✅ Targets healthcare recruiting niche (GMC/NMC)
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
### Addressable Market
|
### Addressable Market
|
||||||
- **18,300 potential customers** (recruitment agencies + corporate HR)
|
- **18,300 potential customers** (recruitment agencies + corporate HR)
|
||||||
- **£2.8B UK pre-employment screening market**
|
- **£2.8B UK pre-employment screening market**
|
||||||
- **~£3.3M serviceable opportunity** for TrueCV platform
|
- **~£3.3M serviceable opportunity** for RealCV platform
|
||||||
|
|
||||||
### Year 1 Revenue Target
|
### Year 1 Revenue Target
|
||||||
- **50-75 paying customers** at £49-199/month
|
- **50-75 paying customers** at £49-199/month
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
|
|
||||||
## Contact & Next Steps
|
## Contact & Next Steps
|
||||||
|
|
||||||
**Product Lead:** [Name] - TrueCV Product Strategy
|
**Product Lead:** [Name] - RealCV Product Strategy
|
||||||
**Engineering Lead:** [Name] - Phase 1 Technical Implementation
|
**Engineering Lead:** [Name] - Phase 1 Technical Implementation
|
||||||
|
|
||||||
**Next Meeting:** [Date] - Review technical implementation plan + finalize go-to-market
|
**Next Meeting:** [Date] - Review technical implementation plan + finalize go-to-market
|
||||||
|
|||||||
24
INDEX.md
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV UK Strategy - Complete Document Index
|
# RealCV UK Strategy - Complete Document Index
|
||||||
|
|
||||||
**Total Documents:** 6 comprehensive strategy guides
|
**Total Documents:** 6 comprehensive strategy guides
|
||||||
**Total Pages:** ~200 pages
|
**Total Pages:** ~200 pages
|
||||||
@@ -392,7 +392,7 @@ Each document has been:
|
|||||||
|
|
||||||
## Copyright & Distribution
|
## Copyright & Distribution
|
||||||
|
|
||||||
**Ownership:** TrueCV Product Team
|
**Ownership:** RealCV Product Team
|
||||||
**Classification:** Internal Only
|
**Classification:** Internal Only
|
||||||
**Distribution:** Leadership, Product, Engineering only
|
**Distribution:** Leadership, Product, Engineering only
|
||||||
|
|
||||||
@@ -433,13 +433,13 @@ Each document has been:
|
|||||||
```
|
```
|
||||||
MARKET OPPORTUNITY
|
MARKET OPPORTUNITY
|
||||||
UK CV fraud cost: £4.2B annually
|
UK CV fraud cost: £4.2B annually
|
||||||
Addressable market: £3.3M (TrueCV's portion)
|
Addressable market: £3.3M (RealCV's portion)
|
||||||
Candidates lying: 1 in 5 (20%)
|
Candidates lying: 1 in 5 (20%)
|
||||||
Failed verifications: 24% of CVs
|
Failed verifications: 24% of CVs
|
||||||
Current verification time: 5-10 DAYS
|
Current verification time: 5-10 DAYS
|
||||||
|
|
||||||
COMPETITIVE ADVANTAGE
|
COMPETITIVE ADVANTAGE
|
||||||
Features only TrueCV offers: 4 major features
|
Features only RealCV offers: 4 major features
|
||||||
Market gap size: Unexploited (£3.3M)
|
Market gap size: Unexploited (£3.3M)
|
||||||
Time to market advantage: 6-12 months
|
Time to market advantage: 6-12 months
|
||||||
|
|
||||||
@@ -479,14 +479,14 @@ Phase 3 (Q3): +1 customer success + 1 analyst
|
|||||||
## Quick Links
|
## Quick Links
|
||||||
|
|
||||||
**Files Created:**
|
**Files Created:**
|
||||||
- `/mnt/d/Git/TrueCV/QUICK_REFERENCE.md`
|
- `/mnt/d/Git/RealCV/QUICK_REFERENCE.md`
|
||||||
- `/mnt/d/Git/TrueCV/EXECUTIVE_SUMMARY.md`
|
- `/mnt/d/Git/RealCV/EXECUTIVE_SUMMARY.md`
|
||||||
- `/mnt/d/Git/TrueCV/UK_FEATURE_PRIORITIZATION.md`
|
- `/mnt/d/Git/RealCV/UK_FEATURE_PRIORITIZATION.md`
|
||||||
- `/mnt/d/Git/TrueCV/PHASE1_TECHNICAL_IMPLEMENTATION.md`
|
- `/mnt/d/Git/RealCV/PHASE1_TECHNICAL_IMPLEMENTATION.md`
|
||||||
- `/mnt/d/Git/TrueCV/UK_MARKET_STRATEGY.md`
|
- `/mnt/d/Git/RealCV/UK_MARKET_STRATEGY.md`
|
||||||
- `/mnt/d/Git/TrueCV/API_RESOURCES_AND_CONTACTS.md`
|
- `/mnt/d/Git/RealCV/API_RESOURCES_AND_CONTACTS.md`
|
||||||
- `/mnt/d/Git/TrueCV/README_UK_STRATEGY.md`
|
- `/mnt/d/Git/RealCV/README_UK_STRATEGY.md`
|
||||||
- `/mnt/d/Git/TrueCV/INDEX.md` (this file)
|
- `/mnt/d/Git/RealCV/INDEX.md` (this file)
|
||||||
|
|
||||||
**Total:** 8 comprehensive strategy documents (~200 pages)
|
**Total:** 8 comprehensive strategy documents (~200 pages)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
### Overview
|
### Overview
|
||||||
Real-time integration with HEDD (Higher Education Degree Datacheck) to verify UK degrees against 140+ university records.
|
Real-time integration with HEDD (Higher Education Degree Datacheck) to verify UK degrees against 140+ university records.
|
||||||
|
|
||||||
**Current Baseline:** TrueCV parses education entries from CV using Claude AI
|
**Current Baseline:** RealCV parses education entries from CV using Claude AI
|
||||||
**Gap:** No verification against actual university records
|
**Gap:** No verification against actual university records
|
||||||
**Value:** Eliminates 90%+ of fake degree claims
|
**Value:** Eliminates 90%+ of fake degree claims
|
||||||
|
|
||||||
@@ -32,10 +32,10 @@ Report & UI
|
|||||||
|
|
||||||
### Phase 1a: Create Infrastructure (Days 1-5)
|
### Phase 1a: Create Infrastructure (Days 1-5)
|
||||||
|
|
||||||
#### File 1: `src/TrueCV.Infrastructure/Configuration/HeddSettings.cs`
|
#### File 1: `src/RealCV.Infrastructure/Configuration/HeddSettings.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
namespace TrueCV.Infrastructure.Configuration;
|
namespace RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
public class HeddSettings
|
public class HeddSettings
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ public class HeddSettings
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File 2: `src/TrueCV.Infrastructure/ExternalApis/HeddClient.cs`
|
#### File 2: `src/RealCV.Infrastructure/ExternalApis/HeddClient.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
@@ -55,9 +55,9 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using TrueCV.Infrastructure.Configuration;
|
using RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.ExternalApis;
|
namespace RealCV.Infrastructure.ExternalApis;
|
||||||
|
|
||||||
public sealed class HeddClient
|
public sealed class HeddClient
|
||||||
{
|
{
|
||||||
@@ -242,12 +242,12 @@ public sealed record HeddManualVerificationStatus
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File 3: `src/TrueCV.Application/Interfaces/IEducationVerifierService.cs`
|
#### File 3: `src/RealCV.Application/Interfaces/IEducationVerifierService.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface IEducationVerifierService
|
public interface IEducationVerifierService
|
||||||
{
|
{
|
||||||
@@ -273,10 +273,10 @@ public interface IEducationVerifierService
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File 4: `src/TrueCV.Application/Models/EducationVerificationResult.cs`
|
#### File 4: `src/RealCV.Application/Models/EducationVerificationResult.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record EducationVerificationResult
|
public sealed record EducationVerificationResult
|
||||||
{
|
{
|
||||||
@@ -370,15 +370,15 @@ public enum ManualVerificationStatus
|
|||||||
|
|
||||||
### Phase 1b: Implement Service Layer (Days 6-10)
|
### Phase 1b: Implement Service Layer (Days 6-10)
|
||||||
|
|
||||||
#### File 5: `src/TrueCV.Infrastructure/Services/EducationVerifierService.cs`
|
#### File 5: `src/RealCV.Infrastructure/Services/EducationVerifierService.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TrueCV.Application.Interfaces;
|
using RealCV.Application.Interfaces;
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
using TrueCV.Infrastructure.ExternalApis;
|
using RealCV.Infrastructure.ExternalApis;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Services;
|
namespace RealCV.Infrastructure.Services;
|
||||||
|
|
||||||
public sealed class EducationVerifierService : IEducationVerifierService
|
public sealed class EducationVerifierService : IEducationVerifierService
|
||||||
{
|
{
|
||||||
@@ -558,10 +558,10 @@ public sealed class EducationVerifierService : IEducationVerifierService
|
|||||||
|
|
||||||
### Phase 1c: Database & Flag Integration (Days 11-12)
|
### Phase 1c: Database & Flag Integration (Days 11-12)
|
||||||
|
|
||||||
#### Update: `src/TrueCV.Domain/Enums/FlagCategory.cs`
|
#### Update: `src/RealCV.Domain/Enums/FlagCategory.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
namespace TrueCV.Domain.Enums;
|
namespace RealCV.Domain.Enums;
|
||||||
|
|
||||||
public enum FlagCategory
|
public enum FlagCategory
|
||||||
{
|
{
|
||||||
@@ -575,14 +575,14 @@ public enum FlagCategory
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### New File: `src/TrueCV.Infrastructure/Services/EducationFlagGenerator.cs`
|
#### New File: `src/RealCV.Infrastructure/Services/EducationFlagGenerator.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
using TrueCV.Domain.Entities;
|
using RealCV.Domain.Entities;
|
||||||
using TrueCV.Domain.Enums;
|
using RealCV.Domain.Enums;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Services;
|
namespace RealCV.Infrastructure.Services;
|
||||||
|
|
||||||
public sealed class EducationFlagGenerator
|
public sealed class EducationFlagGenerator
|
||||||
{
|
{
|
||||||
@@ -683,16 +683,16 @@ public sealed class EducationFlagGenerator
|
|||||||
|
|
||||||
### Phase 1d: Companies House Enhancement - Director Verification
|
### Phase 1d: Companies House Enhancement - Director Verification
|
||||||
|
|
||||||
#### File: `src/TrueCV.Infrastructure/ExternalApis/CompaniesHouseDirectorsClient.cs`
|
#### File: `src/RealCV.Infrastructure/ExternalApis/CompaniesHouseDirectorsClient.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TrueCV.Infrastructure.ExternalApis;
|
using RealCV.Infrastructure.ExternalApis;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.ExternalApis;
|
namespace RealCV.Infrastructure.ExternalApis;
|
||||||
|
|
||||||
public sealed class CompaniesHouseDirectorsClient
|
public sealed class CompaniesHouseDirectorsClient
|
||||||
{
|
{
|
||||||
@@ -795,10 +795,10 @@ public sealed record Officer
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File: `src/TrueCV.Application/Interfaces/IDirectorshipVerifierService.cs`
|
#### File: `src/RealCV.Application/Interfaces/IDirectorshipVerifierService.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface IDirectorshipVerifierService
|
public interface IDirectorshipVerifierService
|
||||||
{
|
{
|
||||||
@@ -826,15 +826,15 @@ public sealed record DirectorshipVerificationResult
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File: `src/TrueCV.Infrastructure/Services/DirectorshipVerifierService.cs`
|
#### File: `src/RealCV.Infrastructure/Services/DirectorshipVerifierService.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using FuzzySharp;
|
using FuzzySharp;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TrueCV.Application.Interfaces;
|
using RealCV.Application.Interfaces;
|
||||||
using TrueCV.Infrastructure.ExternalApis;
|
using RealCV.Infrastructure.ExternalApis;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Services;
|
namespace RealCV.Infrastructure.Services;
|
||||||
|
|
||||||
public sealed class DirectorshipVerifierService : IDirectorshipVerifierService
|
public sealed class DirectorshipVerifierService : IDirectorshipVerifierService
|
||||||
{
|
{
|
||||||
@@ -1070,15 +1070,15 @@ public sealed class DirectorshipVerifierService : IDirectorshipVerifierService
|
|||||||
|
|
||||||
### Phase 1e: Enhanced Timeline Analysis
|
### Phase 1e: Enhanced Timeline Analysis
|
||||||
|
|
||||||
#### File: `src/TrueCV.Infrastructure/Services/EnhancedTimelineAnalyserService.cs`
|
#### File: `src/RealCV.Infrastructure/Services/EnhancedTimelineAnalyserService.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
using TrueCV.Domain.Entities;
|
using RealCV.Domain.Entities;
|
||||||
using TrueCV.Domain.Enums;
|
using RealCV.Domain.Enums;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Services;
|
namespace RealCV.Infrastructure.Services;
|
||||||
|
|
||||||
public sealed class EnhancedTimelineAnalyserService
|
public sealed class EnhancedTimelineAnalyserService
|
||||||
{
|
{
|
||||||
@@ -1226,7 +1226,7 @@ public sealed class EnhancedTimelineAnalyserService
|
|||||||
|
|
||||||
### Phase 1f: Dependency Injection & Integration (Days 13-14)
|
### Phase 1f: Dependency Injection & Integration (Days 13-14)
|
||||||
|
|
||||||
#### Update: `src/TrueCV.Infrastructure/DependencyInjection.cs`
|
#### Update: `src/RealCV.Infrastructure/DependencyInjection.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Add to existing DependencyInjection class:
|
// Add to existing DependencyInjection class:
|
||||||
@@ -1244,7 +1244,7 @@ services.AddScoped<IDirectorshipVerifierService, DirectorshipVerifierService>();
|
|||||||
services.AddScoped<EnhancedTimelineAnalyserService>();
|
services.AddScoped<EnhancedTimelineAnalyserService>();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Update: `src/TrueCV.Infrastructure/Jobs/ProcessCVCheckJob.cs`
|
#### Update: `src/RealCV.Infrastructure/Jobs/ProcessCVCheckJob.cs`
|
||||||
|
|
||||||
Add education and directorship verification to the processing pipeline:
|
Add education and directorship verification to the processing pipeline:
|
||||||
|
|
||||||
@@ -1356,16 +1356,16 @@ private async Task<List<CVFlag>> VerifyDirectorshipsAsync(
|
|||||||
|
|
||||||
### Phase 1g: Testing & QA (Days 15-16)
|
### Phase 1g: Testing & QA (Days 15-16)
|
||||||
|
|
||||||
#### Test File: `tests/TrueCV.Tests/Services/EducationVerifierServiceTests.cs`
|
#### Test File: `tests/RealCV.Tests/Services/EducationVerifierServiceTests.cs`
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
using TrueCV.Infrastructure.ExternalApis;
|
using RealCV.Infrastructure.ExternalApis;
|
||||||
using TrueCV.Infrastructure.Services;
|
using RealCV.Infrastructure.Services;
|
||||||
|
|
||||||
namespace TrueCV.Tests.Services;
|
namespace RealCV.Tests.Services;
|
||||||
|
|
||||||
public class EducationVerifierServiceTests
|
public class EducationVerifierServiceTests
|
||||||
{
|
{
|
||||||
@@ -1467,7 +1467,7 @@ public class EducationVerifierServiceTests
|
|||||||
Create migration for storing verification results:
|
Create migration for storing verification results:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dotnet ef migrations add AddEducationAndDirectorshipVerification --project src/TrueCV.Infrastructure --startup-project src/TrueCV.Web
|
dotnet ef migrations add AddEducationAndDirectorshipVerification --project src/RealCV.Infrastructure --startup-project src/RealCV.Web
|
||||||
```
|
```
|
||||||
|
|
||||||
Add optional columns to CVCheck entity:
|
Add optional columns to CVCheck entity:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV UK Strategy - Quick Reference Card
|
# RealCV UK Strategy - Quick Reference Card
|
||||||
|
|
||||||
**Print this page for desk reference during planning & execution**
|
**Print this page for desk reference during planning & execution**
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ UK CV Fraud Cost: £4.2B annually
|
|||||||
Candidates Lying: 1 in 5 (20%)
|
Candidates Lying: 1 in 5 (20%)
|
||||||
Failed Verifications: 24% of CVs
|
Failed Verifications: 24% of CVs
|
||||||
Current Verification Time: 5-10 DAYS
|
Current Verification Time: 5-10 DAYS
|
||||||
TrueCV Solution Time: 5 SECONDS ⚡
|
RealCV Solution Time: 5 SECONDS ⚡
|
||||||
|
|
||||||
Market Addressable: £3.3M (UK)
|
Market Addressable: £3.3M (UK)
|
||||||
Year 1 Target Revenue: £30-240K
|
Year 1 Target Revenue: £30-240K
|
||||||
@@ -24,7 +24,7 @@ Expected Profitability: Month 6-7
|
|||||||
## Competitive Advantage (Why Now)
|
## Competitive Advantage (Why Now)
|
||||||
|
|
||||||
```
|
```
|
||||||
FEATURE TrueCV Workable Deel Checkr
|
FEATURE RealCV Workable Deel Checkr
|
||||||
─────────────────────────────────────────────────────────
|
─────────────────────────────────────────────────────────
|
||||||
HEDD Degree Verification ✅ ❌ ❌ ❌
|
HEDD Degree Verification ✅ ❌ ❌ ❌
|
||||||
GMC/NMC Healthcare ✅ ❌ ❌ ❌
|
GMC/NMC Healthcare ✅ ❌ ❌ ❌
|
||||||
@@ -370,7 +370,7 @@ Based on your role:
|
|||||||
|
|
||||||
## One-Liner Summary
|
## One-Liner Summary
|
||||||
|
|
||||||
> **TrueCV is the only UK CV verification tool that catches 90% of fake degrees + employment fraud in seconds, leveraging HEDD, GMC/NMC, and Companies House APIs to dominate a £3.3M untapped recruitment market.**
|
> **RealCV is the only UK CV verification tool that catches 90% of fake degrees + employment fraud in seconds, leveraging HEDD, GMC/NMC, and Companies House APIs to dominate a £3.3M untapped recruitment market.**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# TrueCV UK Market Strategy - Complete Package
|
# RealCV UK Market Strategy - Complete Package
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This directory contains the complete product strategy and implementation plan for launching TrueCV with a UK-only focus. The documents provide market analysis, feature prioritization, technical implementation details, and go-to-market strategy.
|
This directory contains the complete product strategy and implementation plan for launching RealCV with a UK-only focus. The documents provide market analysis, feature prioritization, technical implementation details, and go-to-market strategy.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ This directory contains the complete product strategy and implementation plan fo
|
|||||||
**Audience:** Product Team, Marketing, Sales
|
**Audience:** Product Team, Marketing, Sales
|
||||||
**Purpose:** Comprehensive market analysis and go-to-market strategy
|
**Purpose:** Comprehensive market analysis and go-to-market strategy
|
||||||
**Key Sections:**
|
**Key Sections:**
|
||||||
- Market sizing (£2.8B UK screening market, £3.3M TrueCV TAM)
|
- Market sizing (£2.8B UK screening market, £3.3M RealCV TAM)
|
||||||
- Competitive landscape analysis
|
- Competitive landscape analysis
|
||||||
- 3-phase product roadmap (Q1-Q3 2026)
|
- 3-phase product roadmap (Q1-Q3 2026)
|
||||||
- GTM strategy (4 sales channels)
|
- GTM strategy (4 sales channels)
|
||||||
@@ -336,7 +336,7 @@ This directory contains the complete product strategy and implementation plan fo
|
|||||||
## File Manifest
|
## File Manifest
|
||||||
|
|
||||||
```
|
```
|
||||||
/mnt/d/Git/TrueCV/
|
/mnt/d/Git/RealCV/
|
||||||
├── EXECUTIVE_SUMMARY.md (5-page exec overview)
|
├── EXECUTIVE_SUMMARY.md (5-page exec overview)
|
||||||
├── UK_FEATURE_PRIORITIZATION.md (30-page detailed prioritization)
|
├── UK_FEATURE_PRIORITIZATION.md (30-page detailed prioritization)
|
||||||
├── PHASE1_TECHNICAL_IMPLEMENTATION.md (60-page technical specs + code)
|
├── PHASE1_TECHNICAL_IMPLEMENTATION.md (60-page technical specs + code)
|
||||||
@@ -403,7 +403,7 @@ These documents will be updated quarterly with:
|
|||||||
|
|
||||||
## License & Confidentiality
|
## License & Confidentiality
|
||||||
|
|
||||||
This strategy document is internal to TrueCV and contains commercially sensitive information including:
|
This strategy document is internal to RealCV and contains commercially sensitive information including:
|
||||||
- Market sizing & financial projections
|
- Market sizing & financial projections
|
||||||
- Competitive positioning
|
- Competitive positioning
|
||||||
- Product roadmap
|
- Product roadmap
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ VisualStudioVersion = 17.0.31903.59
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F25C3740-9240-46DF-BC34-985BC577216B}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F25C3740-9240-46DF-BC34-985BC577216B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCV.Domain", "src\TrueCV.Domain\TrueCV.Domain.csproj", "{41AC48AF-09BC-48D1-9CA4-1B05D3B693F0}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealCV.Domain", "src\RealCV.Domain\RealCV.Domain.csproj", "{41AC48AF-09BC-48D1-9CA4-1B05D3B693F0}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCV.Application", "src\TrueCV.Application\TrueCV.Application.csproj", "{A8A1BA81-3B2F-4F95-BB15-ACA40DF2A70E}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealCV.Application", "src\RealCV.Application\RealCV.Application.csproj", "{A8A1BA81-3B2F-4F95-BB15-ACA40DF2A70E}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCV.Infrastructure", "src\TrueCV.Infrastructure\TrueCV.Infrastructure.csproj", "{03DB607C-9592-4930-8C89-3E257A319278}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealCV.Infrastructure", "src\RealCV.Infrastructure\RealCV.Infrastructure.csproj", "{03DB607C-9592-4930-8C89-3E257A319278}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCV.Web", "src\TrueCV.Web\TrueCV.Web.csproj", "{D69F57DB-3092-48AF-81BB-868E3749C638}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealCV.Web", "src\RealCV.Web\RealCV.Web.csproj", "{D69F57DB-3092-48AF-81BB-868E3749C638}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{80890010-EDA6-418B-AD6C-5A9D875594C4}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{80890010-EDA6-418B-AD6C-5A9D875594C4}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrueCV.Tests", "tests\TrueCV.Tests\TrueCV.Tests.csproj", "{4450D4F1-4EB9-445E-904B-1C57701493D8}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealCV.Tests", "tests\RealCV.Tests\RealCV.Tests.csproj", "{4450D4F1-4EB9-445E-904B-1C57701493D8}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV UK Market Feature Prioritization
|
# RealCV UK Market Feature Prioritization
|
||||||
|
|
||||||
**Date:** January 2026
|
**Date:** January 2026
|
||||||
**Focus:** UK-Only Market Opportunities
|
**Focus:** UK-Only Market Opportunities
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
## Executive Summary
|
## Executive Summary
|
||||||
|
|
||||||
UK CV fraud is escalating with AI-generated deepfakes, synthetic identities, and traditional qualification falsification. The most impactful opportunity for TrueCV in the UK market is **degree verification integration** (HEDD API), followed by **employment verification automation** and **professional body registration checks**. These three features represent 78% of recruiter pain points and address 85% of detected fraud patterns.
|
UK CV fraud is escalating with AI-generated deepfakes, synthetic identities, and traditional qualification falsification. The most impactful opportunity for RealCV in the UK market is **degree verification integration** (HEDD API), followed by **employment verification automation** and **professional body registration checks**. These three features represent 78% of recruiter pain points and address 85% of detected fraud patterns.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ UK CV fraud is escalating with AI-generated deepfakes, synthetic identities, and
|
|||||||
- **Cost:** Typically £1-5 per verification (commercial rates)
|
- **Cost:** Typically £1-5 per verification (commercial rates)
|
||||||
|
|
||||||
**Implementation Effort:** **Medium (2-3 weeks)**
|
**Implementation Effort:** **Medium (2-3 weeks)**
|
||||||
- Iframe/form integration into TrueCV UI
|
- Iframe/form integration into RealCV UI
|
||||||
- Candidate consent workflow
|
- Candidate consent workflow
|
||||||
- Result polling for manual verifications
|
- Result polling for manual verifications
|
||||||
- Database sync with CVData.Education entries
|
- Database sync with CVData.Education entries
|
||||||
@@ -83,7 +83,7 @@ UK CV fraud is escalating with AI-generated deepfakes, synthetic identities, and
|
|||||||
**Impact Score:** **6.5/10**
|
**Impact Score:** **6.5/10**
|
||||||
- Targets 1.5M NHS workers + private doctors
|
- Targets 1.5M NHS workers + private doctors
|
||||||
- High value for healthcare recruitment
|
- High value for healthcare recruitment
|
||||||
- Medium market size in TrueCV context
|
- Medium market size in RealCV context
|
||||||
- But limited to one profession vs. broad application
|
- But limited to one profession vs. broad application
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -116,7 +116,7 @@ UK CV fraud is escalating with AI-generated deepfakes, synthetic identities, and
|
|||||||
|
|
||||||
### 4. Companies House API (Already Integrated)
|
### 4. Companies House API (Already Integrated)
|
||||||
|
|
||||||
**Status:** ✓ Already implemented in TrueCV
|
**Status:** ✓ Already implemented in RealCV
|
||||||
|
|
||||||
**Current Coverage:**
|
**Current Coverage:**
|
||||||
- Fuzzy matching on company names (70%+ threshold)
|
- Fuzzy matching on company names (70%+ threshold)
|
||||||
@@ -205,7 +205,7 @@ UK CV fraud is escalating with AI-generated deepfakes, synthetic identities, and
|
|||||||
- Links to individual regulators
|
- Links to individual regulators
|
||||||
- Government-maintained reference
|
- Government-maintained reference
|
||||||
|
|
||||||
**Use Case for TrueCV:**
|
**Use Case for RealCV:**
|
||||||
- **Enrichment layer:** When CV claims regulated profession, cross-check against GOV.UK registry
|
- **Enrichment layer:** When CV claims regulated profession, cross-check against GOV.UK registry
|
||||||
- **Flag generation:** "Claims regulated profession but regulator not found"
|
- **Flag generation:** "Claims regulated profession but regulator not found"
|
||||||
- **Guidance:** Link to correct regulator for user lookup
|
- **Guidance:** Link to correct regulator for user lookup
|
||||||
@@ -284,9 +284,9 @@ MEDIUM VALUE + EASY │ MEDIUM VALUE + HARD
|
|||||||
- **Pricing:** Pass-through cost model ($1-2 per verification to user)
|
- **Pricing:** Pass-through cost model ($1-2 per verification to user)
|
||||||
- **Implementation:**
|
- **Implementation:**
|
||||||
```
|
```
|
||||||
src/TrueCV.Infrastructure/ExternalApis/HeddClient.cs
|
src/RealCV.Infrastructure/ExternalApis/HeddClient.cs
|
||||||
src/TrueCV.Application/Interfaces/IEducationVerifierService.cs
|
src/RealCV.Application/Interfaces/IEducationVerifierService.cs
|
||||||
src/TrueCV.Infrastructure/Services/EducationVerifierService.cs
|
src/RealCV.Infrastructure/Services/EducationVerifierService.cs
|
||||||
FlagCategory += EducationVerification
|
FlagCategory += EducationVerification
|
||||||
Add new flag types:
|
Add new flag types:
|
||||||
- DegreeNotFound
|
- DegreeNotFound
|
||||||
@@ -304,7 +304,7 @@ MEDIUM VALUE + EASY │ MEDIUM VALUE + HARD
|
|||||||
- Identify degree end date before employment start anomalies
|
- Identify degree end date before employment start anomalies
|
||||||
- **Implementation:**
|
- **Implementation:**
|
||||||
```
|
```
|
||||||
src/TrueCV.Infrastructure/Services/TimelineAnalyserService.cs
|
src/RealCV.Infrastructure/Services/TimelineAnalyserService.cs
|
||||||
- Add: UKEmploymentPatternAnalyzer
|
- Add: UKEmploymentPatternAnalyzer
|
||||||
- Add: EducationEmploymentSequenceValidator
|
- Add: EducationEmploymentSequenceValidator
|
||||||
- New flags:
|
- New flags:
|
||||||
@@ -322,8 +322,8 @@ MEDIUM VALUE + EASY │ MEDIUM VALUE + HARD
|
|||||||
- Recurring revenue potential
|
- Recurring revenue potential
|
||||||
- **Implementation:**
|
- **Implementation:**
|
||||||
```
|
```
|
||||||
src/TrueCV.Infrastructure/ExternalApis/HealthcareRegisterClient.cs
|
src/RealCV.Infrastructure/ExternalApis/HealthcareRegisterClient.cs
|
||||||
src/TrueCV.Application/Interfaces/IHealthcareVerifierService.cs
|
src/RealCV.Application/Interfaces/IHealthcareVerifierService.cs
|
||||||
FlagCategory += HealthcareRegistration
|
FlagCategory += HealthcareRegistration
|
||||||
New flags:
|
New flags:
|
||||||
- GMCNotFound / GMCRestricted / GMCLapsed
|
- GMCNotFound / GMCRestricted / GMCLapsed
|
||||||
@@ -338,7 +338,7 @@ MEDIUM VALUE + EASY │ MEDIUM VALUE + HARD
|
|||||||
- Detects employment after company dissolution
|
- Detects employment after company dissolution
|
||||||
- **Implementation:**
|
- **Implementation:**
|
||||||
```
|
```
|
||||||
Extend: src/TrueCV.Infrastructure/ExternalApis/CompaniesHouseClient.cs
|
Extend: src/RealCV.Infrastructure/ExternalApis/CompaniesHouseClient.cs
|
||||||
Add: OfficerAppointmentsClient.GetDirectorAppointments(name, companyNumber)
|
Add: OfficerAppointmentsClient.GetDirectorAppointments(name, companyNumber)
|
||||||
New Service: DirectorshipVerificationService
|
New Service: DirectorshipVerificationService
|
||||||
FlagCategory += DirectorshipVerification
|
FlagCategory += DirectorshipVerification
|
||||||
@@ -360,8 +360,8 @@ MEDIUM VALUE + EASY │ MEDIUM VALUE + HARD
|
|||||||
- Regulatory appeal
|
- Regulatory appeal
|
||||||
- **Implementation:**
|
- **Implementation:**
|
||||||
```
|
```
|
||||||
src/TrueCV.Infrastructure/ExternalApis/ProfessionalBodyClient.cs
|
src/RealCV.Infrastructure/ExternalApis/ProfessionalBodyClient.cs
|
||||||
src/TrueCV.Infrastructure/ExternalApis/Scrapers/
|
src/RealCV.Infrastructure/ExternalApis/Scrapers/
|
||||||
- ICAEWMembershipVerifier.cs
|
- ICAEWMembershipVerifier.cs
|
||||||
- SRALawverVerifier.cs
|
- SRALawverVerifier.cs
|
||||||
- IETEngineerVerifier.cs
|
- IETEngineerVerifier.cs
|
||||||
@@ -569,7 +569,7 @@ public class DirectorshipVerificationService
|
|||||||
|
|
||||||
## Competitive Advantage Summary
|
## Competitive Advantage Summary
|
||||||
|
|
||||||
| Feature | TrueCV Advantage | Timeline |
|
| Feature | RealCV Advantage | Timeline |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **HEDD Integration** | Only dedicated CV tool with instant degree verification | Q1 2026 |
|
| **HEDD Integration** | Only dedicated CV tool with instant degree verification | Q1 2026 |
|
||||||
| **Healthcare Register Targeting** | Only tool targeting healthcare recruitment niche | Q1 2026 |
|
| **Healthcare Register Targeting** | Only tool targeting healthcare recruitment niche | Q1 2026 |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV UK Market Strategy & Product Roadmap
|
# RealCV UK Market Strategy & Product Roadmap
|
||||||
|
|
||||||
**Document Date:** January 2026
|
**Document Date:** January 2026
|
||||||
**Focus:** UK CV verification market positioning
|
**Focus:** UK CV verification market positioning
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
### SAM (Serviceable Addressable Market)
|
### SAM (Serviceable Addressable Market)
|
||||||
|
|
||||||
**TrueCV Target Segment:**
|
**RealCV Target Segment:**
|
||||||
- Mid-market recruitment agencies (50-500 staff): ~800 companies
|
- Mid-market recruitment agencies (50-500 staff): ~800 companies
|
||||||
- Corporate HR departments (100+ employees): ~15,000 companies
|
- Corporate HR departments (100+ employees): ~15,000 companies
|
||||||
- Specialist vertical recruiters (healthcare, finance, legal): ~2,500 companies
|
- Specialist vertical recruiters (healthcare, finance, legal): ~2,500 companies
|
||||||
@@ -65,9 +65,9 @@
|
|||||||
| **Verifile** | Pre-employment screening | Established relationships | Traditional manual process |
|
| **Verifile** | Pre-employment screening | Established relationships | Traditional manual process |
|
||||||
| **Veriff** | Identity verification | Strong deepfake tech | Not employment-focused |
|
| **Veriff** | Identity verification | Strong deepfake tech | Not employment-focused |
|
||||||
|
|
||||||
### TrueCV Differentiation
|
### RealCV Differentiation
|
||||||
|
|
||||||
| Feature | TrueCV | Workable | Deel | Checkr | Verifile |
|
| Feature | RealCV | Workable | Deel | Checkr | Verifile |
|
||||||
|---|---|---|---|---|---|
|
|---|---|---|---|---|---|
|
||||||
| **Degree Verification (HEDD)** | ✅ Q1 2026 | ❌ | ❌ | ❌ | ❌ |
|
| **Degree Verification (HEDD)** | ✅ Q1 2026 | ❌ | ❌ | ❌ | ❌ |
|
||||||
| **Healthcare Register Checks** | ✅ Q1 2026 | ❌ | ❌ | ❌ | ❌ |
|
| **Healthcare Register Checks** | ✅ Q1 2026 | ❌ | ❌ | ❌ | ❌ |
|
||||||
@@ -194,7 +194,7 @@
|
|||||||
|
|
||||||
### Marketing Messaging
|
### Marketing Messaging
|
||||||
|
|
||||||
**Tagline:** "Hire with Confidence. Verify with TrueCV."
|
**Tagline:** "Hire with Confidence. Verify with RealCV."
|
||||||
|
|
||||||
**Core Messages:**
|
**Core Messages:**
|
||||||
1. **For Recruiters:** "Catch 90% of degree fraud in seconds. One-click HEDD verification."
|
1. **For Recruiters:** "Catch 90% of degree fraud in seconds. One-click HEDD verification."
|
||||||
@@ -279,7 +279,7 @@
|
|||||||
- **API Uptime:** 99.9%
|
- **API Uptime:** 99.9%
|
||||||
|
|
||||||
### Market Metrics
|
### Market Metrics
|
||||||
- **Brand Awareness:** 15%+ of recruitment agencies aware of TrueCV
|
- **Brand Awareness:** 15%+ of recruitment agencies aware of RealCV
|
||||||
- **Market Share:** 0.5-1% of addressable recruitment screening market
|
- **Market Share:** 0.5-1% of addressable recruitment screening market
|
||||||
- **Vertical Penetration:** 3%+ of healthcare recruiters, 2%+ financial recruiters
|
- **Vertical Penetration:** 3%+ of healthcare recruiters, 2%+ financial recruiters
|
||||||
|
|
||||||
@@ -369,7 +369,7 @@
|
|||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
TrueCV addresses a massive UK market problem (£4.2B+ annual cost from CV fraud) with a focused, integrated solution. By launching with HEDD degree verification + timeline fraud detection in Q1 2026, we capture first-mover advantage in a gap no competitor fills.
|
RealCV addresses a massive UK market problem (£4.2B+ annual cost from CV fraud) with a focused, integrated solution. By launching with HEDD degree verification + timeline fraud detection in Q1 2026, we capture first-mover advantage in a gap no competitor fills.
|
||||||
|
|
||||||
**The opportunity:** Become the UK's trusted CV verification layer for recruitment, reducing fraud while accelerating hiring processes.
|
**The opportunity:** Become the UK's trusted CV verification layer for recruitment, reducing fraud while accelerating hiring processes.
|
||||||
|
|
||||||
|
|||||||
31
deploy-local.sh
Executable file
@@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Deploy RealCV from local git repo to website
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd /git/RealCV
|
||||||
|
|
||||||
|
echo "Building application..."
|
||||||
|
dotnet publish src/RealCV.Web -c Release -o ./publish --nologo
|
||||||
|
|
||||||
|
echo "Stopping service..."
|
||||||
|
sudo systemctl stop realcv
|
||||||
|
|
||||||
|
echo "Backing up config..."
|
||||||
|
cp /var/www/realcv/appsettings.Production.json /tmp/appsettings.Production.json 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Deploying files..."
|
||||||
|
sudo rm -rf /var/www/realcv/*
|
||||||
|
sudo cp -r ./publish/* /var/www/realcv/
|
||||||
|
|
||||||
|
echo "Restoring config..."
|
||||||
|
sudo cp /tmp/appsettings.Production.json /var/www/realcv/ 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "Setting permissions..."
|
||||||
|
sudo chown -R www-data:www-data /var/www/realcv
|
||||||
|
|
||||||
|
echo "Starting service..."
|
||||||
|
sudo systemctl start realcv
|
||||||
|
|
||||||
|
echo "Done! Checking status..."
|
||||||
|
sleep 2
|
||||||
|
sudo systemctl is-active realcv && echo "Service is running."
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# TrueCV Deployment Guide
|
# RealCV Deployment Guide
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ After DNS is configured and app is deployed:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh user@your-server
|
ssh user@your-server
|
||||||
sudo certbot --nginx -d truecv.yourdomain.com
|
sudo certbot --nginx -d realcv.yourdomain.com
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -54,7 +54,7 @@ The systemd service sets these environment variables:
|
|||||||
|
|
||||||
To add more (like API keys), edit:
|
To add more (like API keys), edit:
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl edit truecv
|
sudo systemctl edit realcv
|
||||||
```
|
```
|
||||||
|
|
||||||
Add:
|
Add:
|
||||||
@@ -65,12 +65,12 @@ Environment=OpenAI__ApiKey=your-key-here
|
|||||||
|
|
||||||
### appsettings.Production.json
|
### appsettings.Production.json
|
||||||
|
|
||||||
For sensitive settings, create `/var/www/truecv/appsettings.Production.json`:
|
For sensitive settings, create `/var/www/realcv/appsettings.Production.json`:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"DefaultConnection": "Server=127.0.0.1;Database=TrueCV;User Id=SA;Password=YourPassword;TrustServerCertificate=True"
|
"DefaultConnection": "Server=127.0.0.1;Database=RealCV;User Id=SA;Password=YourPassword;TrustServerCertificate=True"
|
||||||
},
|
},
|
||||||
"OpenAI": {
|
"OpenAI": {
|
||||||
"ApiKey": "your-openai-key"
|
"ApiKey": "your-openai-key"
|
||||||
@@ -83,41 +83,41 @@ For sensitive settings, create `/var/www/truecv/appsettings.Production.json`:
|
|||||||
### View Logs
|
### View Logs
|
||||||
```bash
|
```bash
|
||||||
# Application logs
|
# Application logs
|
||||||
sudo journalctl -u truecv -f
|
sudo journalctl -u realcv -f
|
||||||
|
|
||||||
# Nginx logs
|
# Nginx logs
|
||||||
sudo tail -f /var/log/nginx/access.log
|
sudo tail -f /var/log/nginx/access.log
|
||||||
sudo tail -f /var/log/nginx/error.log
|
sudo tail -f /var/log/nginx/error.log
|
||||||
|
|
||||||
# SQL Server logs
|
# SQL Server logs
|
||||||
docker logs truecv-sql -f
|
docker logs realcv-sql -f
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restart Services
|
### Restart Services
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl restart truecv
|
sudo systemctl restart realcv
|
||||||
sudo systemctl restart nginx
|
sudo systemctl restart nginx
|
||||||
docker restart truecv-sql
|
docker restart realcv-sql
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database Backup
|
### Database Backup
|
||||||
```bash
|
```bash
|
||||||
# Backup
|
# Backup
|
||||||
docker exec truecv-sql /opt/mssql-tools18/bin/sqlcmd \
|
docker exec realcv-sql /opt/mssql-tools18/bin/sqlcmd \
|
||||||
-S localhost -U SA -P 'YourPassword' -C \
|
-S localhost -U SA -P 'YourPassword' -C \
|
||||||
-Q "BACKUP DATABASE TrueCV TO DISK='/var/opt/mssql/backup/truecv.bak'"
|
-Q "BACKUP DATABASE RealCV TO DISK='/var/opt/mssql/backup/realcv.bak'"
|
||||||
|
|
||||||
# Copy backup from container
|
# Copy backup from container
|
||||||
docker cp truecv-sql:/var/opt/mssql/backup/truecv.bak ./truecv-backup.bak
|
docker cp realcv-sql:/var/opt/mssql/backup/realcv.bak ./realcv-backup.bak
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rollback Deployment
|
### Rollback Deployment
|
||||||
```bash
|
```bash
|
||||||
# On server - restore previous version
|
# On server - restore previous version
|
||||||
sudo systemctl stop truecv
|
sudo systemctl stop realcv
|
||||||
sudo rm -rf /var/www/truecv
|
sudo rm -rf /var/www/realcv
|
||||||
sudo mv /var/www/truecv.backup.YYYYMMDD_HHMMSS /var/www/truecv
|
sudo mv /var/www/realcv.backup.YYYYMMDD_HHMMSS /var/www/realcv
|
||||||
sudo systemctl start truecv
|
sudo systemctl start realcv
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
@@ -125,23 +125,23 @@ sudo systemctl start truecv
|
|||||||
### App won't start
|
### App won't start
|
||||||
```bash
|
```bash
|
||||||
# Check status
|
# Check status
|
||||||
sudo systemctl status truecv
|
sudo systemctl status realcv
|
||||||
|
|
||||||
# Check logs
|
# Check logs
|
||||||
sudo journalctl -u truecv -n 100
|
sudo journalctl -u realcv -n 100
|
||||||
|
|
||||||
# Test manually
|
# Test manually
|
||||||
cd /var/www/truecv
|
cd /var/www/realcv
|
||||||
sudo -u www-data dotnet TrueCV.Web.dll
|
sudo -u www-data dotnet RealCV.Web.dll
|
||||||
```
|
```
|
||||||
|
|
||||||
### Database connection issues
|
### Database connection issues
|
||||||
```bash
|
```bash
|
||||||
# Check SQL Server is running
|
# Check SQL Server is running
|
||||||
docker ps | grep truecv-sql
|
docker ps | grep realcv-sql
|
||||||
|
|
||||||
# Test connection
|
# Test connection
|
||||||
docker exec -it truecv-sql /opt/mssql-tools18/bin/sqlcmd \
|
docker exec -it realcv-sql /opt/mssql-tools18/bin/sqlcmd \
|
||||||
-S localhost -U SA -P 'YourPassword' -C \
|
-S localhost -U SA -P 'YourPassword' -C \
|
||||||
-Q "SELECT name FROM sys.databases"
|
-Q "SELECT name FROM sys.databases"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# TrueCV Deployment Script
|
# RealCV Deployment Script
|
||||||
# Run this from your development machine to deploy to a Linux server
|
# Run this from your development machine to deploy to a Linux server
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
@@ -7,8 +7,8 @@ set -e
|
|||||||
# Configuration - UPDATE THESE VALUES
|
# Configuration - UPDATE THESE VALUES
|
||||||
SERVER_USER="deploy"
|
SERVER_USER="deploy"
|
||||||
SERVER_HOST="your-server.com"
|
SERVER_HOST="your-server.com"
|
||||||
SERVER_PATH="/var/www/truecv"
|
SERVER_PATH="/var/www/realcv"
|
||||||
DOMAIN="truecv.yourdomain.com"
|
DOMAIN="realcv.yourdomain.com"
|
||||||
|
|
||||||
# Colors for output
|
# Colors for output
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
@@ -16,7 +16,7 @@ GREEN='\033[0;32m'
|
|||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
NC='\033[0m' # No Color
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
echo -e "${GREEN}=== TrueCV Deployment Script ===${NC}"
|
echo -e "${GREEN}=== RealCV Deployment Script ===${NC}"
|
||||||
|
|
||||||
# Check if configuration is set
|
# Check if configuration is set
|
||||||
if [[ "$SERVER_HOST" == "your-server.com" ]]; then
|
if [[ "$SERVER_HOST" == "your-server.com" ]]; then
|
||||||
@@ -27,15 +27,15 @@ fi
|
|||||||
# Step 1: Build and publish
|
# Step 1: Build and publish
|
||||||
echo -e "${YELLOW}Step 1: Publishing application...${NC}"
|
echo -e "${YELLOW}Step 1: Publishing application...${NC}"
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
dotnet publish src/TrueCV.Web -c Release -o ./publish --nologo
|
dotnet publish src/RealCV.Web -c Release -o ./publish --nologo
|
||||||
|
|
||||||
# Step 2: Create deployment package
|
# Step 2: Create deployment package
|
||||||
echo -e "${YELLOW}Step 2: Creating deployment package...${NC}"
|
echo -e "${YELLOW}Step 2: Creating deployment package...${NC}"
|
||||||
tar -czf deploy/truecv-release.tar.gz -C publish .
|
tar -czf deploy/realcv-release.tar.gz -C publish .
|
||||||
|
|
||||||
# Step 3: Transfer to server
|
# Step 3: Transfer to server
|
||||||
echo -e "${YELLOW}Step 3: Transferring to server...${NC}"
|
echo -e "${YELLOW}Step 3: Transferring to server...${NC}"
|
||||||
scp deploy/truecv-release.tar.gz ${SERVER_USER}@${SERVER_HOST}:/tmp/
|
scp deploy/realcv-release.tar.gz ${SERVER_USER}@${SERVER_HOST}:/tmp/
|
||||||
|
|
||||||
# Step 4: Deploy on server
|
# Step 4: Deploy on server
|
||||||
echo -e "${YELLOW}Step 4: Deploying on server...${NC}"
|
echo -e "${YELLOW}Step 4: Deploying on server...${NC}"
|
||||||
@@ -43,23 +43,23 @@ ssh ${SERVER_USER}@${SERVER_HOST} << 'ENDSSH'
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Stop the service if running
|
# Stop the service if running
|
||||||
sudo systemctl stop truecv 2>/dev/null || true
|
sudo systemctl stop realcv 2>/dev/null || true
|
||||||
|
|
||||||
# Backup current deployment
|
# Backup current deployment
|
||||||
if [ -d "/var/www/truecv" ]; then
|
if [ -d "/var/www/realcv" ]; then
|
||||||
sudo mv /var/www/truecv /var/www/truecv.backup.$(date +%Y%m%d_%H%M%S)
|
sudo mv /var/www/realcv /var/www/realcv.backup.$(date +%Y%m%d_%H%M%S)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create directory and extract
|
# Create directory and extract
|
||||||
sudo mkdir -p /var/www/truecv
|
sudo mkdir -p /var/www/realcv
|
||||||
sudo tar -xzf /tmp/truecv-release.tar.gz -C /var/www/truecv
|
sudo tar -xzf /tmp/realcv-release.tar.gz -C /var/www/realcv
|
||||||
sudo chown -R www-data:www-data /var/www/truecv
|
sudo chown -R www-data:www-data /var/www/realcv
|
||||||
|
|
||||||
# Start the service
|
# Start the service
|
||||||
sudo systemctl start truecv
|
sudo systemctl start realcv
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
rm /tmp/truecv-release.tar.gz
|
rm /tmp/realcv-release.tar.gz
|
||||||
|
|
||||||
echo "Deployment complete on server"
|
echo "Deployment complete on server"
|
||||||
ENDSSH
|
ENDSSH
|
||||||
@@ -67,14 +67,14 @@ ENDSSH
|
|||||||
# Step 5: Verify deployment
|
# Step 5: Verify deployment
|
||||||
echo -e "${YELLOW}Step 5: Verifying deployment...${NC}"
|
echo -e "${YELLOW}Step 5: Verifying deployment...${NC}"
|
||||||
sleep 3
|
sleep 3
|
||||||
if ssh ${SERVER_USER}@${SERVER_HOST} "sudo systemctl is-active truecv" | grep -q "active"; then
|
if ssh ${SERVER_USER}@${SERVER_HOST} "sudo systemctl is-active realcv" | grep -q "active"; then
|
||||||
echo -e "${GREEN}=== Deployment successful! ===${NC}"
|
echo -e "${GREEN}=== Deployment successful! ===${NC}"
|
||||||
echo -e "Site should be available at: https://${DOMAIN}"
|
echo -e "Site should be available at: https://${DOMAIN}"
|
||||||
else
|
else
|
||||||
echo -e "${RED}Warning: Service may not be running. Check with: sudo systemctl status truecv${NC}"
|
echo -e "${RED}Warning: Service may not be running. Check with: sudo systemctl status realcv${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Cleanup local files
|
# Cleanup local files
|
||||||
rm -f deploy/truecv-release.tar.gz
|
rm -f deploy/realcv-release.tar.gz
|
||||||
|
|
||||||
echo -e "${GREEN}Done!${NC}"
|
echo -e "${GREEN}Done!${NC}"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# TrueCV Server Setup Script
|
# RealCV Server Setup Script
|
||||||
# Run this ONCE on a fresh Linux server (Ubuntu 22.04/24.04)
|
# Run this ONCE on a fresh Linux server (Ubuntu 22.04/24.04)
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Configuration - UPDATE THESE VALUES
|
# Configuration - UPDATE THESE VALUES
|
||||||
DOMAIN="truecv.yourdomain.com"
|
DOMAIN="realcv.yourdomain.com"
|
||||||
DB_PASSWORD="YourStrong!Password123"
|
DB_PASSWORD="YourStrong!Password123"
|
||||||
ADMIN_EMAIL="admin@yourdomain.com"
|
ADMIN_EMAIL="admin@yourdomain.com"
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ GREEN='\033[0;32m'
|
|||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
echo -e "${GREEN}=== TrueCV Server Setup ===${NC}"
|
echo -e "${GREEN}=== RealCV Server Setup ===${NC}"
|
||||||
|
|
||||||
# Check if running as root
|
# Check if running as root
|
||||||
if [[ $EUID -ne 0 ]]; then
|
if [[ $EUID -ne 0 ]]; then
|
||||||
@@ -52,54 +52,54 @@ echo -e "${YELLOW}Step 5: Setting up SQL Server...${NC}"
|
|||||||
docker run -e 'ACCEPT_EULA=Y' \
|
docker run -e 'ACCEPT_EULA=Y' \
|
||||||
-e "SA_PASSWORD=${DB_PASSWORD}" \
|
-e "SA_PASSWORD=${DB_PASSWORD}" \
|
||||||
-p 127.0.0.1:1433:1433 \
|
-p 127.0.0.1:1433:1433 \
|
||||||
--name truecv-sql \
|
--name realcv-sql \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
-v truecv-sqldata:/var/opt/mssql \
|
-v realcv-sqldata:/var/opt/mssql \
|
||||||
-d mcr.microsoft.com/mssql/server:2022-latest
|
-d mcr.microsoft.com/mssql/server:2022-latest
|
||||||
|
|
||||||
echo "Waiting for SQL Server to start..."
|
echo "Waiting for SQL Server to start..."
|
||||||
sleep 30
|
sleep 30
|
||||||
|
|
||||||
# Create the database
|
# Create the database
|
||||||
docker exec truecv-sql /opt/mssql-tools18/bin/sqlcmd \
|
docker exec realcv-sql /opt/mssql-tools18/bin/sqlcmd \
|
||||||
-S localhost -U SA -P "${DB_PASSWORD}" -C \
|
-S localhost -U SA -P "${DB_PASSWORD}" -C \
|
||||||
-Q "CREATE DATABASE TrueCV"
|
-Q "CREATE DATABASE RealCV"
|
||||||
|
|
||||||
# Step 6: Create application directory
|
# Step 6: Create application directory
|
||||||
echo -e "${YELLOW}Step 6: Creating application directory...${NC}"
|
echo -e "${YELLOW}Step 6: Creating application directory...${NC}"
|
||||||
mkdir -p /var/www/truecv
|
mkdir -p /var/www/realcv
|
||||||
chown -R www-data:www-data /var/www/truecv
|
chown -R www-data:www-data /var/www/realcv
|
||||||
|
|
||||||
# Step 7: Create systemd service
|
# Step 7: Create systemd service
|
||||||
echo -e "${YELLOW}Step 7: Creating systemd service...${NC}"
|
echo -e "${YELLOW}Step 7: Creating systemd service...${NC}"
|
||||||
cat > /etc/systemd/system/truecv.service << EOF
|
cat > /etc/systemd/system/realcv.service << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=TrueCV Web Application
|
Description=RealCV Web Application
|
||||||
After=network.target docker.service
|
After=network.target docker.service
|
||||||
Requires=docker.service
|
Requires=docker.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
WorkingDirectory=/var/www/truecv
|
WorkingDirectory=/var/www/realcv
|
||||||
ExecStart=/usr/bin/dotnet /var/www/truecv/TrueCV.Web.dll
|
ExecStart=/usr/bin/dotnet /var/www/realcv/RealCV.Web.dll
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=10
|
RestartSec=10
|
||||||
KillSignal=SIGINT
|
KillSignal=SIGINT
|
||||||
SyslogIdentifier=truecv
|
SyslogIdentifier=realcv
|
||||||
User=www-data
|
User=www-data
|
||||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||||
Environment=ASPNETCORE_URLS=http://localhost:5000
|
Environment=ASPNETCORE_URLS=http://localhost:5000
|
||||||
Environment=ConnectionStrings__DefaultConnection=Server=127.0.0.1;Database=TrueCV;User Id=SA;Password=${DB_PASSWORD};TrustServerCertificate=True
|
Environment=ConnectionStrings__DefaultConnection=Server=127.0.0.1;Database=RealCV;User Id=SA;Password=${DB_PASSWORD};TrustServerCertificate=True
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable truecv
|
systemctl enable realcv
|
||||||
|
|
||||||
# Step 8: Configure Nginx
|
# Step 8: Configure Nginx
|
||||||
echo -e "${YELLOW}Step 8: Configuring Nginx...${NC}"
|
echo -e "${YELLOW}Step 8: Configuring Nginx...${NC}"
|
||||||
cat > /etc/nginx/sites-available/truecv << EOF
|
cat > /etc/nginx/sites-available/realcv << EOF
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name ${DOMAIN};
|
server_name ${DOMAIN};
|
||||||
@@ -122,7 +122,7 @@ server {
|
|||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
ln -sf /etc/nginx/sites-available/truecv /etc/nginx/sites-enabled/
|
ln -sf /etc/nginx/sites-available/realcv /etc/nginx/sites-enabled/
|
||||||
rm -f /etc/nginx/sites-enabled/default
|
rm -f /etc/nginx/sites-enabled/default
|
||||||
nginx -t
|
nginx -t
|
||||||
systemctl reload nginx
|
systemctl reload nginx
|
||||||
@@ -151,9 +151,9 @@ echo "2. Deploy the application using deploy.sh from your dev machine"
|
|||||||
echo "3. Run SSL setup: certbot --nginx -d ${DOMAIN}"
|
echo "3. Run SSL setup: certbot --nginx -d ${DOMAIN}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Useful commands:"
|
echo "Useful commands:"
|
||||||
echo " sudo systemctl status truecv - Check app status"
|
echo " sudo systemctl status realcv - Check app status"
|
||||||
echo " sudo journalctl -u truecv -f - View app logs"
|
echo " sudo journalctl -u realcv -f - View app logs"
|
||||||
echo " docker logs truecv-sql - View SQL Server logs"
|
echo " docker logs realcv-sql - View SQL Server logs"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}Database connection string:${NC}"
|
echo -e "${YELLOW}Database connection string:${NC}"
|
||||||
echo " Server=127.0.0.1;Database=TrueCV;User Id=SA;Password=${DB_PASSWORD};TrustServerCertificate=True"
|
echo " Server=127.0.0.1;Database=RealCV;User Id=SA;Password=${DB_PASSWORD};TrustServerCertificate=True"
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
version: '3.8'
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# TrueCV Web Application
|
# RealCV Web Application
|
||||||
truecv-web:
|
realcv-web:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: truecv-web
|
container_name: realcv-web
|
||||||
ports:
|
ports:
|
||||||
- "5000:8080"
|
- "5000:8080"
|
||||||
environment:
|
environment:
|
||||||
- ASPNETCORE_ENVIRONMENT=Development
|
- ASPNETCORE_ENVIRONMENT=Development
|
||||||
- ConnectionStrings__DefaultConnection=Server=sqlserver;Database=TrueCV;User Id=sa;Password=TrueCV_P@ssw0rd!;TrustServerCertificate=True;
|
- ConnectionStrings__DefaultConnection=Server=sqlserver;Database=RealCV;User Id=sa;Password=TrueCV_P@ssw0rd!;TrustServerCertificate=True;
|
||||||
- ConnectionStrings__HangfireConnection=Server=sqlserver;Database=TrueCV_Hangfire;User Id=sa;Password=TrueCV_P@ssw0rd!;TrustServerCertificate=True;
|
- ConnectionStrings__HangfireConnection=Server=sqlserver;Database=RealCV_Hangfire;User Id=sa;Password=TrueCV_P@ssw0rd!;TrustServerCertificate=True;
|
||||||
- AzureBlob__ConnectionString=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
|
- AzureBlob__ConnectionString=DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite:10000/devstoreaccount1;
|
||||||
- AzureBlob__ContainerName=cv-uploads
|
- AzureBlob__ContainerName=cv-uploads
|
||||||
- CompaniesHouse__BaseUrl=https://api.company-information.service.gov.uk
|
- CompaniesHouse__BaseUrl=https://api.company-information.service.gov.uk
|
||||||
@@ -24,13 +24,13 @@ services:
|
|||||||
azurite:
|
azurite:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
networks:
|
networks:
|
||||||
- truecv-network
|
- realcv-network
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# SQL Server Database
|
# SQL Server Database
|
||||||
sqlserver:
|
sqlserver:
|
||||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||||
container_name: truecv-sqlserver
|
container_name: realcv-sqlserver
|
||||||
ports:
|
ports:
|
||||||
- "1433:1433"
|
- "1433:1433"
|
||||||
environment:
|
environment:
|
||||||
@@ -40,7 +40,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- sqlserver-data:/var/opt/mssql
|
- sqlserver-data:/var/opt/mssql
|
||||||
networks:
|
networks:
|
||||||
- truecv-network
|
- realcv-network
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "TrueCV_P@ssw0rd!" -C -Q "SELECT 1" || exit 1
|
test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "TrueCV_P@ssw0rd!" -C -Q "SELECT 1" || exit 1
|
||||||
interval: 10s
|
interval: 10s
|
||||||
@@ -52,7 +52,7 @@ services:
|
|||||||
# Azure Storage Emulator (Azurite)
|
# Azure Storage Emulator (Azurite)
|
||||||
azurite:
|
azurite:
|
||||||
image: mcr.microsoft.com/azure-storage/azurite:latest
|
image: mcr.microsoft.com/azure-storage/azurite:latest
|
||||||
container_name: truecv-azurite
|
container_name: realcv-azurite
|
||||||
ports:
|
ports:
|
||||||
- "10000:10000" # Blob service
|
- "10000:10000" # Blob service
|
||||||
- "10001:10001" # Queue service
|
- "10001:10001" # Queue service
|
||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
- azurite-data:/data
|
- azurite-data:/data
|
||||||
command: "azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --location /data --debug /data/debug.log"
|
command: "azurite --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --location /data --debug /data/debug.log"
|
||||||
networks:
|
networks:
|
||||||
- truecv-network
|
- realcv-network
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
# Database initialization (runs migrations)
|
# Database initialization (runs migrations)
|
||||||
@@ -69,18 +69,18 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile.migrations
|
dockerfile: Dockerfile.migrations
|
||||||
container_name: truecv-db-init
|
container_name: realcv-db-init
|
||||||
environment:
|
environment:
|
||||||
- ConnectionStrings__DefaultConnection=Server=sqlserver;Database=TrueCV;User Id=sa;Password=TrueCV_P@ssw0rd!;TrustServerCertificate=True;
|
- ConnectionStrings__DefaultConnection=Server=sqlserver;Database=RealCV;User Id=sa;Password=TrueCV_P@ssw0rd!;TrustServerCertificate=True;
|
||||||
depends_on:
|
depends_on:
|
||||||
sqlserver:
|
sqlserver:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
networks:
|
networks:
|
||||||
- truecv-network
|
- realcv-network
|
||||||
restart: "no"
|
restart: "no"
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
truecv-network:
|
realcv-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
BIN
screenshots/01-home.png
Normal file
|
After Width: | Height: | Size: 646 KiB |
BIN
screenshots/02-login.png
Normal file
|
After Width: | Height: | Size: 433 KiB |
BIN
screenshots/03-register.png
Normal file
|
After Width: | Height: | Size: 444 KiB |
BIN
screenshots/04-dashboard.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
screenshots/05-check.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
screenshots/06-report.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
screenshots/dashboard-compact.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
screenshots/dashboard-warm.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
screenshots/homepage.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
screenshots/login.png
Normal file
|
After Width: | Height: | Size: 449 KiB |
BIN
screenshots/pricing.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
screenshots/privacy.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
screenshots/register.png
Normal file
|
After Width: | Height: | Size: 468 KiB |
BIN
screenshots/report-compact.png
Normal file
|
After Width: | Height: | Size: 165 KiB |
@@ -1,9 +1,10 @@
|
|||||||
namespace TrueCV.Application.DTOs;
|
namespace RealCV.Application.DTOs;
|
||||||
|
|
||||||
public sealed record CVCheckDto
|
public sealed record CVCheckDto
|
||||||
{
|
{
|
||||||
public required Guid Id { get; init; }
|
public required Guid Id { get; init; }
|
||||||
public required string OriginalFileName { get; init; }
|
public required string OriginalFileName { get; init; }
|
||||||
|
public string? CandidateName { get; init; }
|
||||||
public required string Status { get; init; }
|
public required string Status { get; init; }
|
||||||
public int? VeracityScore { get; init; }
|
public int? VeracityScore { get; init; }
|
||||||
public string? ProcessingStage { get; init; }
|
public string? ProcessingStage { get; init; }
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.DTOs;
|
namespace RealCV.Application.DTOs;
|
||||||
|
|
||||||
public sealed record CompanySearchResult
|
public sealed record CompanySearchResult
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Data;
|
namespace RealCV.Application.Data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of recognised UK higher education institutions.
|
/// List of recognised UK higher education institutions.
|
||||||
@@ -43,6 +43,8 @@ public static class UKInstitutions
|
|||||||
|
|
||||||
// Other Major Universities
|
// Other Major Universities
|
||||||
"Aston University",
|
"Aston University",
|
||||||
|
"Leeds Beckett University",
|
||||||
|
"Leeds Metropolitan University", // Former name of Leeds Beckett
|
||||||
"University of Bath",
|
"University of Bath",
|
||||||
"Birkbeck, University of London",
|
"Birkbeck, University of London",
|
||||||
"Bournemouth University",
|
"Bournemouth University",
|
||||||
@@ -218,6 +220,9 @@ public static class UKInstitutions
|
|||||||
["Queen Mary"] = "Queen Mary University of London",
|
["Queen Mary"] = "Queen Mary University of London",
|
||||||
["Royal Holloway University"] = "Royal Holloway, University of London",
|
["Royal Holloway University"] = "Royal Holloway, University of London",
|
||||||
["RHUL"] = "Royal Holloway, University of London",
|
["RHUL"] = "Royal Holloway, University of London",
|
||||||
|
["Leeds Beckett"] = "Leeds Beckett University",
|
||||||
|
["Leeds Met"] = "Leeds Beckett University",
|
||||||
|
["Leeds Metropolitan"] = "Leeds Beckett University",
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
namespace TrueCV.Application.Data;
|
namespace RealCV.Application.Data;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Known diploma mills and fake educational institutions.
|
/// Institutions not recognised by UK higher education regulatory bodies.
|
||||||
/// Sources: HEDD, Oregon ODA, UNESCO warnings, Michigan AG list
|
/// Sources: HEDD, Oregon ODA, UNESCO warnings, Michigan AG list
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class DiplomaMills
|
public static class UnaccreditedInstitutions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Known diploma mills and unaccredited institutions that sell fake degrees.
|
/// Institutions identified by regulatory bodies as not meeting recognised accreditation standards.
|
||||||
/// This list includes institutions identified by various regulatory bodies.
|
/// This list includes institutions flagged by various educational oversight organisations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly HashSet<string> KnownDiplomaMills = new(StringComparer.OrdinalIgnoreCase)
|
public static readonly HashSet<string> KnownUnaccredited = new(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
// Well-known diploma mills
|
// Institutions not meeting accreditation standards
|
||||||
"Almeda University",
|
"Almeda University",
|
||||||
"Ashwood University",
|
"Ashwood University",
|
||||||
"Belford University",
|
"Belford University",
|
||||||
@@ -67,7 +67,7 @@ public static class DiplomaMills
|
|||||||
"Stanton University",
|
"Stanton University",
|
||||||
"Stratford University (if unaccredited)",
|
"Stratford University (if unaccredited)",
|
||||||
"Suffield University",
|
"Suffield University",
|
||||||
"Summit University (diploma mill)",
|
"Summit University (unaccredited)",
|
||||||
"Sussex College of Technology",
|
"Sussex College of Technology",
|
||||||
"Trinity College and University",
|
"Trinity College and University",
|
||||||
"Trinity Southern University",
|
"Trinity Southern University",
|
||||||
@@ -80,7 +80,7 @@ public static class DiplomaMills
|
|||||||
"University of Northern Washington",
|
"University of Northern Washington",
|
||||||
"University of Palmers Green",
|
"University of Palmers Green",
|
||||||
"University of San Moritz",
|
"University of San Moritz",
|
||||||
"University of Sussex (fake - not real Sussex)",
|
"University of Sussex (not the legitimate University of Sussex)",
|
||||||
"University of Wexford",
|
"University of Wexford",
|
||||||
"Vocational University",
|
"Vocational University",
|
||||||
"Warnborough University",
|
"Warnborough University",
|
||||||
@@ -91,7 +91,7 @@ public static class DiplomaMills
|
|||||||
"Woodfield University",
|
"Woodfield University",
|
||||||
"Yorker International University",
|
"Yorker International University",
|
||||||
|
|
||||||
// Pakistani diploma mills commonly seen in UK
|
// Unaccredited institutions commonly seen in UK applications
|
||||||
"Axact University",
|
"Axact University",
|
||||||
"Brooklyn Park University",
|
"Brooklyn Park University",
|
||||||
"Columbiana University",
|
"Columbiana University",
|
||||||
@@ -100,11 +100,11 @@ public static class DiplomaMills
|
|||||||
"Oxbridge University",
|
"Oxbridge University",
|
||||||
"University of Newford",
|
"University of Newford",
|
||||||
|
|
||||||
// Online diploma mills
|
// Online unaccredited institutions
|
||||||
"American World University",
|
"American World University",
|
||||||
"Ashford University (pre-2005)",
|
"Ashford University (pre-2005)",
|
||||||
"Concordia College and University",
|
"Concordia College and University",
|
||||||
"Columbus State University (fake)",
|
"Columbus State University (unaccredited variant)",
|
||||||
"Frederick Taylor University",
|
"Frederick Taylor University",
|
||||||
"International Theological University",
|
"International Theological University",
|
||||||
"Nations University",
|
"Nations University",
|
||||||
@@ -115,7 +115,7 @@ public static class DiplomaMills
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Suspicious patterns in institution names that often indicate diploma mills.
|
/// Patterns in institution names that may indicate unaccredited status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string[] SuspiciousPatterns =
|
public static readonly string[] SuspiciousPatterns =
|
||||||
[
|
[
|
||||||
@@ -136,27 +136,9 @@ public static class DiplomaMills
|
|||||||
];
|
];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fake accreditation bodies used by diploma mills.
|
/// Check if an institution is not recognised by accreditation bodies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly HashSet<string> FakeAccreditors = new(StringComparer.OrdinalIgnoreCase)
|
public static bool IsUnaccredited(string institutionName)
|
||||||
{
|
|
||||||
"World Association of Universities and Colleges",
|
|
||||||
"WAUC",
|
|
||||||
"International Accreditation Agency",
|
|
||||||
"Universal Accreditation Council",
|
|
||||||
"Board of Online Universities Accreditation",
|
|
||||||
"International Council for Open and Distance Education",
|
|
||||||
"World Online Education Accrediting Commission",
|
|
||||||
"Central States Consortium of Colleges and Schools",
|
|
||||||
"American Council of Private Colleges and Universities",
|
|
||||||
"Association of Distance Learning Programs",
|
|
||||||
"International Distance Education Certification Agency",
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if an institution is a known diploma mill.
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsDiplomaMill(string institutionName)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(institutionName))
|
if (string.IsNullOrWhiteSpace(institutionName))
|
||||||
return false;
|
return false;
|
||||||
@@ -164,13 +146,13 @@ public static class DiplomaMills
|
|||||||
var normalised = institutionName.Trim();
|
var normalised = institutionName.Trim();
|
||||||
|
|
||||||
// Direct match
|
// Direct match
|
||||||
if (KnownDiplomaMills.Contains(normalised))
|
if (KnownUnaccredited.Contains(normalised))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Check if name contains known diploma mill
|
// Check if name contains known unaccredited institution
|
||||||
foreach (var mill in KnownDiplomaMills)
|
foreach (var institution in KnownUnaccredited)
|
||||||
{
|
{
|
||||||
if (normalised.Contains(mill, StringComparison.OrdinalIgnoreCase))
|
if (normalised.Contains(institution, StringComparison.OrdinalIgnoreCase))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,8 +160,8 @@ public static class DiplomaMills
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check if institution name has suspicious patterns common in diploma mills.
|
/// Check if institution name has patterns that may indicate unaccredited status.
|
||||||
/// Returns true if suspicious (but not confirmed fake).
|
/// Returns true if patterns suggest further verification is recommended.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool HasSuspiciousPattern(string institutionName)
|
public static bool HasSuspiciousPattern(string institutionName)
|
||||||
{
|
{
|
||||||
@@ -196,15 +178,4 @@ public static class DiplomaMills
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if an accreditor is known to be fake.
|
|
||||||
/// </summary>
|
|
||||||
public static bool IsFakeAccreditor(string accreditorName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(accreditorName))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return FakeAccreditors.Contains(accreditorName.Trim());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Helpers;
|
namespace RealCV.Application.Helpers;
|
||||||
|
|
||||||
public static class DateHelpers
|
public static class DateHelpers
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace TrueCV.Application.Helpers;
|
namespace RealCV.Application.Helpers;
|
||||||
|
|
||||||
public static class JsonDefaults
|
public static class JsonDefaults
|
||||||
{
|
{
|
||||||
@@ -16,4 +17,13 @@ public static class JsonDefaults
|
|||||||
PropertyNameCaseInsensitive = true,
|
PropertyNameCaseInsensitive = true,
|
||||||
WriteIndented = true
|
WriteIndented = true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Options for consuming external APIs - case insensitive with null handling.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly JsonSerializerOptions ApiClient = new()
|
||||||
|
{
|
||||||
|
PropertyNameCaseInsensitive = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
|
};
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Helpers;
|
namespace RealCV.Application.Helpers;
|
||||||
|
|
||||||
public static class ScoreThresholds
|
public static class ScoreThresholds
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for verifying academic researchers via ORCID
|
||||||
|
/// </summary>
|
||||||
|
public interface IAcademicVerifierService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verify an academic researcher by ORCID ID
|
||||||
|
/// </summary>
|
||||||
|
Task<AcademicVerificationResult> VerifyByOrcidAsync(string orcidId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search for researchers and verify by name
|
||||||
|
/// </summary>
|
||||||
|
Task<AcademicVerificationResult> VerifyByNameAsync(
|
||||||
|
string name,
|
||||||
|
string? affiliation = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search ORCID for researchers
|
||||||
|
/// </summary>
|
||||||
|
Task<List<OrcidSearchResult>> SearchResearchersAsync(
|
||||||
|
string name,
|
||||||
|
string? affiliation = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify claimed publications
|
||||||
|
/// </summary>
|
||||||
|
Task<List<PublicationVerificationResult>> VerifyPublicationsAsync(
|
||||||
|
string orcidId,
|
||||||
|
List<string> claimedPublications);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record OrcidSearchResult
|
||||||
|
{
|
||||||
|
public required string OrcidId { get; init; }
|
||||||
|
public required string Name { get; init; }
|
||||||
|
public string? OrcidUrl { get; init; }
|
||||||
|
public List<string>? Affiliations { get; init; }
|
||||||
|
public int? PublicationCount { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record PublicationVerificationResult
|
||||||
|
{
|
||||||
|
public required string ClaimedTitle { get; init; }
|
||||||
|
public required bool IsVerified { get; init; }
|
||||||
|
public string? MatchedTitle { get; init; }
|
||||||
|
public string? Doi { get; init; }
|
||||||
|
public int? Year { get; init; }
|
||||||
|
public string? Notes { get; init; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface IAuditService
|
public interface IAuditService
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using TrueCV.Application.DTOs;
|
using RealCV.Application.DTOs;
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface ICVCheckService
|
public interface ICVCheckService
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface ICVParserService
|
public interface ICVParserService
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface ICompanyNameMatcherService
|
public interface ICompanyNameMatcherService
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using TrueCV.Application.DTOs;
|
using RealCV.Application.DTOs;
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface ICompanyVerifierService
|
public interface ICompanyVerifierService
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface IEducationVerifierService
|
public interface IEducationVerifierService
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface IFileStorageService
|
public interface IFileStorageService
|
||||||
{
|
{
|
||||||
36
src/RealCV.Application/Interfaces/IGitHubVerifierService.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for verifying developer profiles and skills via GitHub
|
||||||
|
/// </summary>
|
||||||
|
public interface IGitHubVerifierService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verify a GitHub profile and analyze activity
|
||||||
|
/// </summary>
|
||||||
|
Task<GitHubVerificationResult> VerifyProfileAsync(string username);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify claimed programming skills against GitHub activity
|
||||||
|
/// </summary>
|
||||||
|
Task<GitHubVerificationResult> VerifySkillsAsync(
|
||||||
|
string username,
|
||||||
|
List<string> claimedSkills);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search for GitHub profiles matching a name
|
||||||
|
/// </summary>
|
||||||
|
Task<List<GitHubProfileSearchResult>> SearchProfilesAsync(string name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record GitHubProfileSearchResult
|
||||||
|
{
|
||||||
|
public required string Username { get; init; }
|
||||||
|
public string? Name { get; init; }
|
||||||
|
public string? AvatarUrl { get; init; }
|
||||||
|
public string? Bio { get; init; }
|
||||||
|
public int PublicRepos { get; init; }
|
||||||
|
public int Followers { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for verifying professional qualifications (FCA)
|
||||||
|
/// </summary>
|
||||||
|
public interface IProfessionalVerifierService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verify if a person is registered with the FCA
|
||||||
|
/// </summary>
|
||||||
|
Task<ProfessionalVerificationResult> VerifyFcaRegistrationAsync(
|
||||||
|
string name,
|
||||||
|
string? firmName = null,
|
||||||
|
string? referenceNumber = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Search FCA register for individuals
|
||||||
|
/// </summary>
|
||||||
|
Task<List<FcaIndividualSearchResult>> SearchFcaIndividualsAsync(string name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record FcaIndividualSearchResult
|
||||||
|
{
|
||||||
|
public required string Name { get; init; }
|
||||||
|
public required string IndividualReferenceNumber { get; init; }
|
||||||
|
public string? Status { get; init; }
|
||||||
|
public List<string>? CurrentFirms { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
|
public interface ITextAnalysisService
|
||||||
|
{
|
||||||
|
TextAnalysisResult Analyse(CVData cvData);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
|
|
||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface ITimelineAnalyserService
|
public interface ITimelineAnalyserService
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Interfaces;
|
namespace RealCV.Application.Interfaces;
|
||||||
|
|
||||||
public interface IUserContextService
|
public interface IUserContextService
|
||||||
{
|
{
|
||||||
62
src/RealCV.Application/Models/AcademicVerificationResult.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of verifying an academic researcher via ORCID
|
||||||
|
/// </summary>
|
||||||
|
public sealed record AcademicVerificationResult
|
||||||
|
{
|
||||||
|
public required string ClaimedName { get; init; }
|
||||||
|
public required bool IsVerified { get; init; }
|
||||||
|
|
||||||
|
// ORCID profile
|
||||||
|
public string? OrcidId { get; init; }
|
||||||
|
public string? MatchedName { get; init; }
|
||||||
|
public string? OrcidUrl { get; init; }
|
||||||
|
|
||||||
|
// Academic affiliations
|
||||||
|
public List<AcademicAffiliation> Affiliations { get; init; } = [];
|
||||||
|
|
||||||
|
// Publications
|
||||||
|
public int TotalPublications { get; init; }
|
||||||
|
public List<Publication> RecentPublications { get; init; } = [];
|
||||||
|
|
||||||
|
// Education from ORCID
|
||||||
|
public List<AcademicEducation> Education { get; init; } = [];
|
||||||
|
|
||||||
|
public string? VerificationNotes { get; init; }
|
||||||
|
public List<AcademicVerificationFlag> Flags { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AcademicAffiliation
|
||||||
|
{
|
||||||
|
public required string Organization { get; init; }
|
||||||
|
public string? Department { get; init; }
|
||||||
|
public string? Role { get; init; }
|
||||||
|
public DateOnly? StartDate { get; init; }
|
||||||
|
public DateOnly? EndDate { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record Publication
|
||||||
|
{
|
||||||
|
public required string Title { get; init; }
|
||||||
|
public string? Journal { get; init; }
|
||||||
|
public int? Year { get; init; }
|
||||||
|
public string? Doi { get; init; }
|
||||||
|
public string? Type { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AcademicEducation
|
||||||
|
{
|
||||||
|
public required string Institution { get; init; }
|
||||||
|
public string? Degree { get; init; }
|
||||||
|
public string? Subject { get; init; }
|
||||||
|
public int? Year { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AcademicVerificationFlag
|
||||||
|
{
|
||||||
|
public required string Type { get; init; }
|
||||||
|
public required string Severity { get; init; }
|
||||||
|
public required string Message { get; init; }
|
||||||
|
public int ScoreImpact { get; init; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record CVData
|
public sealed record CVData
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record CompanyVerificationResult
|
public sealed record CompanyVerificationResult
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record EducationEntry
|
public sealed record EducationEntry
|
||||||
{
|
{
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record EducationVerificationResult
|
public sealed record EducationVerificationResult
|
||||||
{
|
{
|
||||||
public required string ClaimedInstitution { get; init; }
|
public required string ClaimedInstitution { get; init; }
|
||||||
public string? MatchedInstitution { get; init; }
|
public string? MatchedInstitution { get; init; }
|
||||||
public required string Status { get; init; } // Recognised, NotRecognised, DiplomaMill, Suspicious, Unknown
|
public required string Status { get; init; } // Recognised, NotRecognised, Unaccredited, Suspicious, Unknown
|
||||||
public bool IsVerified { get; init; }
|
public bool IsVerified { get; init; }
|
||||||
public bool IsDiplomaMill { get; init; }
|
public bool IsUnaccredited { get; init; }
|
||||||
public bool IsSuspicious { get; init; }
|
public bool IsSuspicious { get; init; }
|
||||||
public string? VerificationNotes { get; init; }
|
public string? VerificationNotes { get; init; }
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record EmploymentEntry
|
public sealed record EmploymentEntry
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record FlagResult
|
public sealed record FlagResult
|
||||||
{
|
{
|
||||||
49
src/RealCV.Application/Models/GitHubVerificationResult.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of verifying a developer's GitHub profile
|
||||||
|
/// </summary>
|
||||||
|
public sealed record GitHubVerificationResult
|
||||||
|
{
|
||||||
|
public required string ClaimedUsername { get; init; }
|
||||||
|
public required bool IsVerified { get; init; }
|
||||||
|
|
||||||
|
// Profile details
|
||||||
|
public string? ProfileName { get; init; }
|
||||||
|
public string? ProfileUrl { get; init; }
|
||||||
|
public string? Bio { get; init; }
|
||||||
|
public string? Company { get; init; }
|
||||||
|
public string? Location { get; init; }
|
||||||
|
public DateOnly? AccountCreated { get; init; }
|
||||||
|
|
||||||
|
// Activity metrics
|
||||||
|
public int PublicRepos { get; init; }
|
||||||
|
public int Followers { get; init; }
|
||||||
|
public int Following { get; init; }
|
||||||
|
public int TotalContributions { get; init; }
|
||||||
|
|
||||||
|
// Language breakdown
|
||||||
|
public Dictionary<string, int> LanguageStats { get; init; } = new();
|
||||||
|
|
||||||
|
// Claimed skills verification
|
||||||
|
public List<SkillVerification> SkillVerifications { get; init; } = [];
|
||||||
|
|
||||||
|
public string? VerificationNotes { get; init; }
|
||||||
|
public List<GitHubVerificationFlag> Flags { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record SkillVerification
|
||||||
|
{
|
||||||
|
public required string ClaimedSkill { get; init; }
|
||||||
|
public required bool IsVerified { get; init; }
|
||||||
|
public int RepoCount { get; init; }
|
||||||
|
public string? Notes { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record GitHubVerificationFlag
|
||||||
|
{
|
||||||
|
public required string Type { get; init; }
|
||||||
|
public required string Severity { get; init; }
|
||||||
|
public required string Message { get; init; }
|
||||||
|
public int ScoreImpact { get; init; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of verifying a professional qualification (FCA)
|
||||||
|
/// </summary>
|
||||||
|
public sealed record ProfessionalVerificationResult
|
||||||
|
{
|
||||||
|
public required string ClaimedName { get; init; }
|
||||||
|
public required string ProfessionalBody { get; init; }
|
||||||
|
public required bool IsVerified { get; init; }
|
||||||
|
|
||||||
|
// Matched professional details
|
||||||
|
public string? MatchedName { get; init; }
|
||||||
|
public string? RegistrationNumber { get; init; }
|
||||||
|
public string? Status { get; init; }
|
||||||
|
public string? CurrentEmployer { get; init; }
|
||||||
|
public DateOnly? RegistrationDate { get; init; }
|
||||||
|
|
||||||
|
// For FCA
|
||||||
|
public List<string>? ApprovedFunctions { get; init; }
|
||||||
|
public List<string>? ControlledFunctions { get; init; }
|
||||||
|
|
||||||
|
public string? VerificationNotes { get; init; }
|
||||||
|
public List<ProfessionalVerificationFlag> Flags { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record ProfessionalVerificationFlag
|
||||||
|
{
|
||||||
|
public required string Type { get; init; }
|
||||||
|
public required string Severity { get; init; }
|
||||||
|
public required string Message { get; init; }
|
||||||
|
public int ScoreImpact { get; init; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public record SemanticMatchResult
|
public record SemanticMatchResult
|
||||||
{
|
{
|
||||||
@@ -10,12 +10,6 @@ public record SemanticMatchResult
|
|||||||
public bool IsMatch => ConfidenceScore >= 70;
|
public bool IsMatch => ConfidenceScore >= 70;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record CompanyMatchRequest
|
|
||||||
{
|
|
||||||
public required string CVCompanyName { get; init; }
|
|
||||||
public required List<CompanyCandidate> Candidates { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public record CompanyCandidate
|
public record CompanyCandidate
|
||||||
{
|
{
|
||||||
public required string CompanyName { get; init; }
|
public required string CompanyName { get; init; }
|
||||||
66
src/RealCV.Application/Models/TextAnalysisResult.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
|
public sealed record TextAnalysisResult
|
||||||
|
{
|
||||||
|
public BuzzwordAnalysis BuzzwordAnalysis { get; init; } = new();
|
||||||
|
public AchievementAnalysis AchievementAnalysis { get; init; } = new();
|
||||||
|
public SkillsAlignmentAnalysis SkillsAlignment { get; init; } = new();
|
||||||
|
public MetricsAnalysis MetricsAnalysis { get; init; } = new();
|
||||||
|
public List<TextAnalysisFlag> Flags { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record BuzzwordAnalysis
|
||||||
|
{
|
||||||
|
public int TotalBuzzwords { get; init; }
|
||||||
|
public List<string> BuzzwordsFound { get; init; } = [];
|
||||||
|
public double BuzzwordDensity { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record AchievementAnalysis
|
||||||
|
{
|
||||||
|
public int TotalStatements { get; init; }
|
||||||
|
public int VagueStatements { get; init; }
|
||||||
|
public int QuantifiedStatements { get; init; }
|
||||||
|
public int StrongActionVerbStatements { get; init; }
|
||||||
|
public List<string> VagueExamples { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record SkillsAlignmentAnalysis
|
||||||
|
{
|
||||||
|
public int TotalRolesChecked { get; init; }
|
||||||
|
public int RolesWithMatchingSkills { get; init; }
|
||||||
|
public List<SkillMismatch> Mismatches { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record SkillMismatch
|
||||||
|
{
|
||||||
|
public required string JobTitle { get; init; }
|
||||||
|
public required string CompanyName { get; init; }
|
||||||
|
public required List<string> ExpectedSkills { get; init; }
|
||||||
|
public required List<string> MatchingSkills { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record MetricsAnalysis
|
||||||
|
{
|
||||||
|
public int TotalMetricsClaimed { get; init; }
|
||||||
|
public int PlausibleMetrics { get; init; }
|
||||||
|
public int SuspiciousMetrics { get; init; }
|
||||||
|
public int RoundNumberCount { get; init; }
|
||||||
|
public double RoundNumberRatio { get; init; }
|
||||||
|
public List<SuspiciousMetric> SuspiciousMetricsList { get; init; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record SuspiciousMetric
|
||||||
|
{
|
||||||
|
public required string ClaimText { get; init; }
|
||||||
|
public required double Value { get; init; }
|
||||||
|
public required string Reason { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record TextAnalysisFlag
|
||||||
|
{
|
||||||
|
public required string Type { get; init; }
|
||||||
|
public required string Severity { get; init; }
|
||||||
|
public required string Message { get; init; }
|
||||||
|
public int ScoreImpact { get; init; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record TimelineAnalysisResult
|
public sealed record TimelineAnalysisResult
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record TimelineGap
|
public sealed record TimelineGap
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record TimelineOverlap
|
public sealed record TimelineOverlap
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Application.Models;
|
namespace RealCV.Application.Models;
|
||||||
|
|
||||||
public sealed record VeracityReport
|
public sealed record VeracityReport
|
||||||
{
|
{
|
||||||
@@ -8,6 +8,7 @@ public sealed record VeracityReport
|
|||||||
public List<CompanyVerificationResult> EmploymentVerifications { get; init; } = [];
|
public List<CompanyVerificationResult> EmploymentVerifications { get; init; } = [];
|
||||||
public List<EducationVerificationResult> EducationVerifications { get; init; } = [];
|
public List<EducationVerificationResult> EducationVerifications { get; init; } = [];
|
||||||
public required TimelineAnalysisResult TimelineAnalysis { get; init; }
|
public required TimelineAnalysisResult TimelineAnalysis { get; init; }
|
||||||
|
public TextAnalysisResult? TextAnalysis { get; init; }
|
||||||
public List<FlagResult> Flags { get; init; } = [];
|
public List<FlagResult> Flags { get; init; } = [];
|
||||||
public required DateTime GeneratedAt { get; init; }
|
public required DateTime GeneratedAt { get; init; }
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\TrueCV.Domain\TrueCV.Domain.csproj" />
|
<ProjectReference Include="..\RealCV.Domain\RealCV.Domain.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace TrueCV.Domain.Entities;
|
namespace RealCV.Domain.Entities;
|
||||||
|
|
||||||
public class AuditLog
|
public class AuditLog
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using TrueCV.Domain.Enums;
|
using RealCV.Domain.Enums;
|
||||||
|
|
||||||
namespace TrueCV.Domain.Entities;
|
namespace RealCV.Domain.Entities;
|
||||||
|
|
||||||
public class CVCheck
|
public class CVCheck
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using TrueCV.Domain.Enums;
|
using RealCV.Domain.Enums;
|
||||||
|
|
||||||
namespace TrueCV.Domain.Entities;
|
namespace RealCV.Domain.Entities;
|
||||||
|
|
||||||
public class CVFlag
|
public class CVFlag
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace TrueCV.Domain.Entities;
|
namespace RealCV.Domain.Entities;
|
||||||
|
|
||||||
public class CompanyCache
|
public class CompanyCache
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Domain.Enums;
|
namespace RealCV.Domain.Enums;
|
||||||
|
|
||||||
public enum CheckStatus
|
public enum CheckStatus
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Domain.Enums;
|
namespace RealCV.Domain.Enums;
|
||||||
|
|
||||||
public enum FlagCategory
|
public enum FlagCategory
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Domain.Enums;
|
namespace RealCV.Domain.Enums;
|
||||||
|
|
||||||
public enum FlagSeverity
|
public enum FlagSeverity
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Domain.Enums;
|
namespace RealCV.Domain.Enums;
|
||||||
|
|
||||||
public enum UserPlan
|
public enum UserPlan
|
||||||
{
|
{
|
||||||
210
src/RealCV.Infrastructure/Clients/FcaRegisterClient.cs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using RealCV.Application.Helpers;
|
||||||
|
|
||||||
|
namespace RealCV.Infrastructure.Clients;
|
||||||
|
|
||||||
|
public sealed class FcaRegisterClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<FcaRegisterClient> _logger;
|
||||||
|
private readonly string _apiKey;
|
||||||
|
|
||||||
|
public FcaRegisterClient(
|
||||||
|
HttpClient httpClient,
|
||||||
|
IOptions<FcaOptions> options,
|
||||||
|
ILogger<FcaRegisterClient> logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
_apiKey = options.Value.ApiKey;
|
||||||
|
|
||||||
|
_httpClient.BaseAddress = new Uri("https://register.fca.org.uk/services/V0.1/");
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("X-Auth-Email", options.Value.Email);
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("X-Auth-Key", _apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FcaIndividualResponse?> SearchIndividualsAsync(string name, int page = 1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encodedName = Uri.EscapeDataString(name);
|
||||||
|
var url = $"Individuals?q={encodedName}&page={page}";
|
||||||
|
|
||||||
|
_logger.LogDebug("Searching FCA for individual: {Name}", name);
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FCA API returned {StatusCode} for search: {Name}",
|
||||||
|
response.StatusCode, name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<FcaIndividualResponse>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error searching FCA for individual: {Name}", name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FcaIndividualDetails?> GetIndividualAsync(string individualReferenceNumber)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = $"Individuals/{individualReferenceNumber}";
|
||||||
|
|
||||||
|
_logger.LogDebug("Getting FCA individual: {IRN}", individualReferenceNumber);
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FCA API returned {StatusCode} for IRN: {IRN}",
|
||||||
|
response.StatusCode, individualReferenceNumber);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrapper = await response.Content.ReadFromJsonAsync<FcaIndividualDetailsWrapper>(JsonDefaults.ApiClient);
|
||||||
|
return wrapper?.Data?.FirstOrDefault();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting FCA individual: {IRN}", individualReferenceNumber);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<FcaFirmResponse?> SearchFirmsAsync(string name, int page = 1)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encodedName = Uri.EscapeDataString(name);
|
||||||
|
var url = $"Firms?q={encodedName}&page={page}";
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<FcaFirmResponse>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error searching FCA for firm: {Name}", name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaOptions
|
||||||
|
{
|
||||||
|
public string ApiKey { get; set; } = string.Empty;
|
||||||
|
public string Email { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response models
|
||||||
|
public class FcaIndividualResponse
|
||||||
|
{
|
||||||
|
public List<FcaIndividualSearchItem>? Data { get; set; }
|
||||||
|
public FcaPagination? Pagination { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaIndividualSearchItem
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Individual Reference Number")]
|
||||||
|
public string? IndividualReferenceNumber { get; set; }
|
||||||
|
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? Status { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Current Employer(s)")]
|
||||||
|
public string? CurrentEmployers { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaIndividualDetailsWrapper
|
||||||
|
{
|
||||||
|
public List<FcaIndividualDetails>? Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaIndividualDetails
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Individual Reference Number")]
|
||||||
|
public string? IndividualReferenceNumber { get; set; }
|
||||||
|
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? Status { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Effective Date")]
|
||||||
|
public string? EffectiveDate { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Controlled Functions")]
|
||||||
|
public List<FcaControlledFunction>? ControlledFunctions { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Previous Employments")]
|
||||||
|
public List<FcaPreviousEmployment>? PreviousEmployments { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaControlledFunction
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Controlled Function")]
|
||||||
|
public string? ControlledFunction { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Firm Name")]
|
||||||
|
public string? FirmName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Firm Reference Number")]
|
||||||
|
public string? FirmReferenceNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Status")]
|
||||||
|
public string? Status { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Effective From")]
|
||||||
|
public string? EffectiveFrom { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaPreviousEmployment
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Firm Name")]
|
||||||
|
public string? FirmName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Firm Reference Number")]
|
||||||
|
public string? FirmReferenceNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Start Date")]
|
||||||
|
public string? StartDate { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("End Date")]
|
||||||
|
public string? EndDate { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaFirmResponse
|
||||||
|
{
|
||||||
|
public List<FcaFirmSearchItem>? Data { get; set; }
|
||||||
|
public FcaPagination? Pagination { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaFirmSearchItem
|
||||||
|
{
|
||||||
|
[JsonPropertyName("Firm Reference Number")]
|
||||||
|
public string? FirmReferenceNumber { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("Firm Name")]
|
||||||
|
public string? FirmName { get; set; }
|
||||||
|
|
||||||
|
public string? Status { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FcaPagination
|
||||||
|
{
|
||||||
|
public int Page { get; set; }
|
||||||
|
public int TotalPages { get; set; }
|
||||||
|
public int TotalItems { get; set; }
|
||||||
|
}
|
||||||
237
src/RealCV.Infrastructure/Clients/GitHubClient.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using RealCV.Application.Helpers;
|
||||||
|
|
||||||
|
namespace RealCV.Infrastructure.Clients;
|
||||||
|
|
||||||
|
public sealed class GitHubApiClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<GitHubApiClient> _logger;
|
||||||
|
|
||||||
|
public GitHubApiClient(
|
||||||
|
HttpClient httpClient,
|
||||||
|
IOptions<GitHubOptions> options,
|
||||||
|
ILogger<GitHubApiClient> logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_httpClient.BaseAddress = new Uri("https://api.github.com/");
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28");
|
||||||
|
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("RealCV/1.0");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(options.Value.PersonalAccessToken))
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Authorization =
|
||||||
|
new AuthenticationHeaderValue("Bearer", options.Value.PersonalAccessToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GitHubUser?> GetUserAsync(string username)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var url = $"users/{Uri.EscapeDataString(username)}";
|
||||||
|
|
||||||
|
_logger.LogDebug("Getting GitHub user: {Username}", username);
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("GitHub API returned {StatusCode} for user: {Username}",
|
||||||
|
response.StatusCode, username);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<GitHubUser>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting GitHub user: {Username}", username);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<GitHubRepo>> GetUserReposAsync(string username, int perPage = 100)
|
||||||
|
{
|
||||||
|
var repos = new List<GitHubRepo>();
|
||||||
|
var page = 1;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var url = $"users/{Uri.EscapeDataString(username)}/repos?per_page={perPage}&page={page}&sort=updated";
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageRepos = await response.Content.ReadFromJsonAsync<List<GitHubRepo>>(JsonDefaults.ApiClient);
|
||||||
|
|
||||||
|
if (pageRepos == null || pageRepos.Count == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
repos.AddRange(pageRepos);
|
||||||
|
|
||||||
|
if (pageRepos.Count < perPage)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
page++;
|
||||||
|
|
||||||
|
// Limit to avoid rate limiting
|
||||||
|
if (page > 5)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting repos for user: {Username}", username);
|
||||||
|
}
|
||||||
|
|
||||||
|
return repos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<GitHubUserSearchResponse?> SearchUsersAsync(string query, int perPage = 30)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encodedQuery = Uri.EscapeDataString(query);
|
||||||
|
var url = $"search/users?q={encodedQuery}&per_page={perPage}";
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<GitHubUserSearchResponse>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error searching GitHub users: {Query}", query);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GitHubOptions
|
||||||
|
{
|
||||||
|
public string PersonalAccessToken { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response models
|
||||||
|
public class GitHubUser
|
||||||
|
{
|
||||||
|
public string? Login { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? Company { get; set; }
|
||||||
|
public string? Blog { get; set; }
|
||||||
|
public string? Location { get; set; }
|
||||||
|
public string? Email { get; set; }
|
||||||
|
public string? Bio { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("twitter_username")]
|
||||||
|
public string? TwitterUsername { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("public_repos")]
|
||||||
|
public int PublicRepos { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("public_gists")]
|
||||||
|
public int PublicGists { get; set; }
|
||||||
|
|
||||||
|
public int Followers { get; set; }
|
||||||
|
public int Following { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("updated_at")]
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("html_url")]
|
||||||
|
public string? HtmlUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("avatar_url")]
|
||||||
|
public string? AvatarUrl { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GitHubRepo
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("full_name")]
|
||||||
|
public string? FullName { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
public string? Language { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("html_url")]
|
||||||
|
public string? HtmlUrl { get; set; }
|
||||||
|
|
||||||
|
public bool Fork { get; set; }
|
||||||
|
public bool Private { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("stargazers_count")]
|
||||||
|
public int StargazersCount { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("watchers_count")]
|
||||||
|
public int WatchersCount { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("forks_count")]
|
||||||
|
public int ForksCount { get; set; }
|
||||||
|
|
||||||
|
public int Size { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("created_at")]
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("updated_at")]
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("pushed_at")]
|
||||||
|
public DateTime? PushedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GitHubUserSearchResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("total_count")]
|
||||||
|
public int TotalCount { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("incomplete_results")]
|
||||||
|
public bool IncompleteResults { get; set; }
|
||||||
|
|
||||||
|
public List<GitHubUserSearchItem>? Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GitHubUserSearchItem
|
||||||
|
{
|
||||||
|
public string? Login { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("avatar_url")]
|
||||||
|
public string? AvatarUrl { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("html_url")]
|
||||||
|
public string? HtmlUrl { get; set; }
|
||||||
|
|
||||||
|
public double Score { get; set; }
|
||||||
|
}
|
||||||
336
src/RealCV.Infrastructure/Clients/OrcidClient.cs
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Net.Http.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using RealCV.Application.Helpers;
|
||||||
|
|
||||||
|
namespace RealCV.Infrastructure.Clients;
|
||||||
|
|
||||||
|
public sealed class OrcidClient
|
||||||
|
{
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
private readonly ILogger<OrcidClient> _logger;
|
||||||
|
|
||||||
|
public OrcidClient(
|
||||||
|
HttpClient httpClient,
|
||||||
|
ILogger<OrcidClient> logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
|
||||||
|
_httpClient.BaseAddress = new Uri("https://pub.orcid.org/v3.0/");
|
||||||
|
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrcidSearchResponse?> SearchResearchersAsync(string query, int start = 0, int rows = 20)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var encodedQuery = Uri.EscapeDataString(query);
|
||||||
|
var url = $"search?q={encodedQuery}&start={start}&rows={rows}";
|
||||||
|
|
||||||
|
_logger.LogDebug("Searching ORCID: {Query}", query);
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ORCID API returned {StatusCode} for search: {Query}",
|
||||||
|
response.StatusCode, query);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<OrcidSearchResponse>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error searching ORCID: {Query}", query);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrcidRecord?> GetRecordAsync(string orcidId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Normalize ORCID ID format (remove URL prefix if present)
|
||||||
|
orcidId = NormalizeOrcidId(orcidId);
|
||||||
|
|
||||||
|
var url = $"{orcidId}/record";
|
||||||
|
|
||||||
|
_logger.LogDebug("Getting ORCID record: {OrcidId}", orcidId);
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ORCID API returned {StatusCode} for ID: {OrcidId}",
|
||||||
|
response.StatusCode, orcidId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<OrcidRecord>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting ORCID record: {OrcidId}", orcidId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrcidWorks?> GetWorksAsync(string orcidId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
orcidId = NormalizeOrcidId(orcidId);
|
||||||
|
var url = $"{orcidId}/works";
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<OrcidWorks>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting ORCID works: {OrcidId}", orcidId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrcidEmployments?> GetEmploymentsAsync(string orcidId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
orcidId = NormalizeOrcidId(orcidId);
|
||||||
|
var url = $"{orcidId}/employments";
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<OrcidEmployments>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting ORCID employments: {OrcidId}", orcidId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrcidEducations?> GetEducationsAsync(string orcidId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
orcidId = NormalizeOrcidId(orcidId);
|
||||||
|
var url = $"{orcidId}/educations";
|
||||||
|
|
||||||
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadFromJsonAsync<OrcidEducations>(JsonDefaults.ApiClient);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error getting ORCID educations: {OrcidId}", orcidId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeOrcidId(string orcidId)
|
||||||
|
{
|
||||||
|
// Remove URL prefixes
|
||||||
|
orcidId = orcidId.Replace("https://orcid.org/", "")
|
||||||
|
.Replace("http://orcid.org/", "")
|
||||||
|
.Trim();
|
||||||
|
return orcidId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response models
|
||||||
|
public class OrcidSearchResponse
|
||||||
|
{
|
||||||
|
[JsonPropertyName("num-found")]
|
||||||
|
public int NumFound { get; set; }
|
||||||
|
|
||||||
|
public List<OrcidSearchResult>? Result { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidSearchResult
|
||||||
|
{
|
||||||
|
[JsonPropertyName("orcid-identifier")]
|
||||||
|
public OrcidIdentifier? OrcidIdentifier { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidIdentifier
|
||||||
|
{
|
||||||
|
public string? Uri { get; set; }
|
||||||
|
public string? Path { get; set; }
|
||||||
|
public string? Host { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidRecord
|
||||||
|
{
|
||||||
|
[JsonPropertyName("orcid-identifier")]
|
||||||
|
public OrcidIdentifier? OrcidIdentifier { get; set; }
|
||||||
|
|
||||||
|
public OrcidPerson? Person { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("activities-summary")]
|
||||||
|
public OrcidActivitiesSummary? ActivitiesSummary { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidPerson
|
||||||
|
{
|
||||||
|
public OrcidName? Name { get; set; }
|
||||||
|
public OrcidBiography? Biography { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidName
|
||||||
|
{
|
||||||
|
[JsonPropertyName("given-names")]
|
||||||
|
public OrcidValue? GivenNames { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("family-name")]
|
||||||
|
public OrcidValue? FamilyName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("credit-name")]
|
||||||
|
public OrcidValue? CreditName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidValue
|
||||||
|
{
|
||||||
|
public string? Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidBiography
|
||||||
|
{
|
||||||
|
public string? Content { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidActivitiesSummary
|
||||||
|
{
|
||||||
|
public OrcidEmployments? Employments { get; set; }
|
||||||
|
public OrcidEducations? Educations { get; set; }
|
||||||
|
public OrcidWorks? Works { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidEmployments
|
||||||
|
{
|
||||||
|
[JsonPropertyName("affiliation-group")]
|
||||||
|
public List<OrcidAffiliationGroup>? AffiliationGroup { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidEducations
|
||||||
|
{
|
||||||
|
[JsonPropertyName("affiliation-group")]
|
||||||
|
public List<OrcidAffiliationGroup>? AffiliationGroup { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidAffiliationGroup
|
||||||
|
{
|
||||||
|
public List<OrcidAffiliationSummaryWrapper>? Summaries { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidAffiliationSummaryWrapper
|
||||||
|
{
|
||||||
|
[JsonPropertyName("employment-summary")]
|
||||||
|
public OrcidAffiliationSummary? EmploymentSummary { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("education-summary")]
|
||||||
|
public OrcidAffiliationSummary? EducationSummary { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidAffiliationSummary
|
||||||
|
{
|
||||||
|
[JsonPropertyName("department-name")]
|
||||||
|
public string? DepartmentName { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("role-title")]
|
||||||
|
public string? RoleTitle { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("start-date")]
|
||||||
|
public OrcidDate? StartDate { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("end-date")]
|
||||||
|
public OrcidDate? EndDate { get; set; }
|
||||||
|
|
||||||
|
public OrcidOrganization? Organization { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidDate
|
||||||
|
{
|
||||||
|
public OrcidValue? Year { get; set; }
|
||||||
|
public OrcidValue? Month { get; set; }
|
||||||
|
public OrcidValue? Day { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidOrganization
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public OrcidAddress? Address { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidAddress
|
||||||
|
{
|
||||||
|
public string? City { get; set; }
|
||||||
|
public string? Region { get; set; }
|
||||||
|
public string? Country { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidWorks
|
||||||
|
{
|
||||||
|
public List<OrcidWorkGroup>? Group { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidWorkGroup
|
||||||
|
{
|
||||||
|
[JsonPropertyName("work-summary")]
|
||||||
|
public List<OrcidWorkSummary>? WorkSummary { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidWorkSummary
|
||||||
|
{
|
||||||
|
public string? Title { get; set; }
|
||||||
|
public OrcidTitle? TitleObj { get; set; }
|
||||||
|
public string? Type { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("publication-date")]
|
||||||
|
public OrcidDate? PublicationDate { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("journal-title")]
|
||||||
|
public OrcidValue? JournalTitle { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("external-ids")]
|
||||||
|
public OrcidExternalIds? ExternalIds { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidTitle
|
||||||
|
{
|
||||||
|
public OrcidValue? Title { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidExternalIds
|
||||||
|
{
|
||||||
|
[JsonPropertyName("external-id")]
|
||||||
|
public List<OrcidExternalId>? ExternalId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class OrcidExternalId
|
||||||
|
{
|
||||||
|
[JsonPropertyName("external-id-type")]
|
||||||
|
public string? ExternalIdType { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("external-id-value")]
|
||||||
|
public string? ExternalIdValue { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Infrastructure.Configuration;
|
namespace RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
public sealed class AnthropicSettings
|
public sealed class AnthropicSettings
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Infrastructure.Configuration;
|
namespace RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
public sealed class AzureBlobSettings
|
public sealed class AzureBlobSettings
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Infrastructure.Configuration;
|
namespace RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
public sealed class CompaniesHouseSettings
|
public sealed class CompaniesHouseSettings
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Infrastructure.Configuration;
|
namespace RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
public sealed class LocalStorageSettings
|
public sealed class LocalStorageSettings
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using TrueCV.Domain.Entities;
|
using RealCV.Domain.Entities;
|
||||||
using TrueCV.Infrastructure.Identity;
|
using RealCV.Infrastructure.Identity;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data;
|
namespace RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
|
||||||
{
|
{
|
||||||
@@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
|||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using TrueCV.Infrastructure.Data;
|
using RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
[Migration("20260118182916_InitialCreate")]
|
[Migration("20260118182916_InitialCreate")]
|
||||||
@@ -156,7 +156,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("AspNetUserTokens", (string)null);
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -211,7 +211,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVChecks");
|
b.ToTable("CVChecks");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -251,7 +251,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVFlags");
|
b.ToTable("CVFlags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CompanyCache", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CompanyCache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("CompanyNumber")
|
b.Property<string>("CompanyNumber")
|
||||||
.HasMaxLength(32)
|
.HasMaxLength(32)
|
||||||
@@ -281,7 +281,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CompanyCache");
|
b.ToTable("CompanyCache");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.User", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -307,7 +307,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("User");
|
b.ToTable("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -396,7 +396,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -405,7 +405,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -420,7 +420,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -429,29 +429,29 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany("CVChecks")
|
.WithMany("CVChecks")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("TrueCV.Domain.Entities.User", null)
|
b.HasOne("RealCV.Domain.Entities.User", null)
|
||||||
.WithMany("CVChecks")
|
.WithMany("CVChecks")
|
||||||
.HasForeignKey("UserId1");
|
.HasForeignKey("UserId1");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Domain.Entities.CVCheck", "CVCheck")
|
b.HasOne("RealCV.Domain.Entities.CVCheck", "CVCheck")
|
||||||
.WithMany("Flags")
|
.WithMany("Flags")
|
||||||
.HasForeignKey("CVCheckId")
|
.HasForeignKey("CVCheckId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -460,17 +460,17 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.Navigation("CVCheck");
|
b.Navigation("CVCheck");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Flags");
|
b.Navigation("Flags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.User", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("CVChecks");
|
b.Navigation("CVChecks");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("CVChecks");
|
b.Navigation("CVChecks");
|
||||||
});
|
});
|
||||||
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class InitialCreate : Migration
|
public partial class InitialCreate : Migration
|
||||||
@@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
|||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using TrueCV.Infrastructure.Data;
|
using RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
[Migration("20260120191035_AddProcessingStageToCV")]
|
[Migration("20260120191035_AddProcessingStageToCV")]
|
||||||
@@ -156,7 +156,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("AspNetUserTokens", (string)null);
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -210,7 +210,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVChecks");
|
b.ToTable("CVChecks");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -250,7 +250,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVFlags");
|
b.ToTable("CVFlags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CompanyCache", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CompanyCache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("CompanyNumber")
|
b.Property<string>("CompanyNumber")
|
||||||
.HasMaxLength(32)
|
.HasMaxLength(32)
|
||||||
@@ -292,7 +292,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CompanyCache");
|
b.ToTable("CompanyCache");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -381,7 +381,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -390,7 +390,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -405,7 +405,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -414,25 +414,25 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany("CVChecks")
|
.WithMany("CVChecks")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Domain.Entities.CVCheck", "CVCheck")
|
b.HasOne("RealCV.Domain.Entities.CVCheck", "CVCheck")
|
||||||
.WithMany("Flags")
|
.WithMany("Flags")
|
||||||
.HasForeignKey("CVCheckId")
|
.HasForeignKey("CVCheckId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -441,12 +441,12 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.Navigation("CVCheck");
|
b.Navigation("CVCheck");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Flags");
|
b.Navigation("Flags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("CVChecks");
|
b.Navigation("CVChecks");
|
||||||
});
|
});
|
||||||
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AddProcessingStageToCV : Migration
|
public partial class AddProcessingStageToCV : Migration
|
||||||
@@ -5,11 +5,11 @@ using Microsoft.EntityFrameworkCore.Infrastructure;
|
|||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using TrueCV.Infrastructure.Data;
|
using RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
[Migration("20260120194532_AddAuditLogTable")]
|
[Migration("20260120194532_AddAuditLogTable")]
|
||||||
@@ -156,7 +156,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("AspNetUserTokens", (string)null);
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.AuditLog", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.AuditLog", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -202,7 +202,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("AuditLogs");
|
b.ToTable("AuditLogs");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -256,7 +256,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVChecks");
|
b.ToTable("CVChecks");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -296,7 +296,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVFlags");
|
b.ToTable("CVFlags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CompanyCache", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CompanyCache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("CompanyNumber")
|
b.Property<string>("CompanyNumber")
|
||||||
.HasMaxLength(32)
|
.HasMaxLength(32)
|
||||||
@@ -338,7 +338,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CompanyCache");
|
b.ToTable("CompanyCache");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -427,7 +427,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -436,7 +436,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -451,7 +451,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -460,25 +460,25 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany("CVChecks")
|
.WithMany("CVChecks")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Domain.Entities.CVCheck", "CVCheck")
|
b.HasOne("RealCV.Domain.Entities.CVCheck", "CVCheck")
|
||||||
.WithMany("Flags")
|
.WithMany("Flags")
|
||||||
.HasForeignKey("CVCheckId")
|
.HasForeignKey("CVCheckId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -487,12 +487,12 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.Navigation("CVCheck");
|
b.Navigation("CVCheck");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Flags");
|
b.Navigation("Flags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("CVChecks");
|
b.Navigation("CVChecks");
|
||||||
});
|
});
|
||||||
@@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AddAuditLogTable : Migration
|
public partial class AddAuditLogTable : Migration
|
||||||
505
src/RealCV.Infrastructure/Data/Migrations/20260125074319_AddTermsAcceptedAt.Designer.cs
generated
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260125074319_AddTermsAcceptedAt")]
|
||||||
|
partial class AddTermsAcceptedAt
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.23")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.AuditLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Action")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("Details")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("EntityId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("EntityType")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Action")
|
||||||
|
.HasDatabaseName("IX_AuditLogs_Action");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedAt")
|
||||||
|
.HasDatabaseName("IX_AuditLogs_CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("IX_AuditLogs_UserId");
|
||||||
|
|
||||||
|
b.ToTable("AuditLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("BlobUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("nvarchar(2048)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("CompletedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("ExtractedDataJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("OriginalFileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
|
b.Property<string>("ProcessingStage")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<string>("ReportJson")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int?>("VeracityScore")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Status")
|
||||||
|
.HasDatabaseName("IX_CVChecks_Status");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("IX_CVChecks_UserId");
|
||||||
|
|
||||||
|
b.ToTable("CVChecks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("CVCheckId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Category")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("nvarchar(2048)");
|
||||||
|
|
||||||
|
b.Property<int>("ScoreImpact")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Severity")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CVCheckId")
|
||||||
|
.HasDatabaseName("IX_CVFlags_CVCheckId");
|
||||||
|
|
||||||
|
b.ToTable("CVFlags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.CompanyCache", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("CompanyNumber")
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)");
|
||||||
|
|
||||||
|
b.Property<string>("AccountsCategory")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CachedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("CompanyName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("nvarchar(512)");
|
||||||
|
|
||||||
|
b.Property<string>("CompanyType")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.Property<DateOnly?>("DissolutionDate")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<DateOnly?>("IncorporationDate")
|
||||||
|
.HasColumnType("date");
|
||||||
|
|
||||||
|
b.Property<string>("SicCodesJson")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("Status")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)");
|
||||||
|
|
||||||
|
b.HasKey("CompanyNumber");
|
||||||
|
|
||||||
|
b.ToTable("CompanyCache");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("ChecksUsedThisMonth")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("Plan")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("StripeCustomerId")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("TermsAcceptedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
|
.WithMany("CVChecks")
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("RealCV.Domain.Entities.CVCheck", "CVCheck")
|
||||||
|
.WithMany("Flags")
|
||||||
|
.HasForeignKey("CVCheckId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("CVCheck");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Flags");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("CVChecks");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddTermsAcceptedAt : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "TermsAcceptedAt",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
type: "datetime2",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TermsAcceptedAt",
|
||||||
|
table: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,11 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using TrueCV.Infrastructure.Data;
|
using RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Data.Migrations
|
namespace RealCV.Infrastructure.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(ApplicationDbContext))]
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||||
@@ -153,7 +153,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("AspNetUserTokens", (string)null);
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.AuditLog", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.AuditLog", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -199,7 +199,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("AuditLogs");
|
b.ToTable("AuditLogs");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -253,7 +253,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVChecks");
|
b.ToTable("CVChecks");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -293,7 +293,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CVFlags");
|
b.ToTable("CVFlags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CompanyCache", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CompanyCache", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("CompanyNumber")
|
b.Property<string>("CompanyNumber")
|
||||||
.HasMaxLength(32)
|
.HasMaxLength(32)
|
||||||
@@ -335,7 +335,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.ToTable("CompanyCache");
|
b.ToTable("CompanyCache");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -393,6 +393,9 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
.HasMaxLength(256)
|
.HasMaxLength(256)
|
||||||
.HasColumnType("nvarchar(256)");
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("TermsAcceptedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
b.Property<bool>("TwoFactorEnabled")
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
@@ -424,7 +427,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -433,7 +436,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -448,7 +451,7 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -457,25 +460,25 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Infrastructure.Identity.ApplicationUser", null)
|
b.HasOne("RealCV.Infrastructure.Identity.ApplicationUser", null)
|
||||||
.WithMany("CVChecks")
|
.WithMany("CVChecks")
|
||||||
.HasForeignKey("UserId")
|
.HasForeignKey("UserId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVFlag", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVFlag", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("TrueCV.Domain.Entities.CVCheck", "CVCheck")
|
b.HasOne("RealCV.Domain.Entities.CVCheck", "CVCheck")
|
||||||
.WithMany("Flags")
|
.WithMany("Flags")
|
||||||
.HasForeignKey("CVCheckId")
|
.HasForeignKey("CVCheckId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -484,12 +487,12 @@ namespace TrueCV.Infrastructure.Data.Migrations
|
|||||||
b.Navigation("CVCheck");
|
b.Navigation("CVCheck");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Domain.Entities.CVCheck", b =>
|
modelBuilder.Entity("RealCV.Domain.Entities.CVCheck", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Flags");
|
b.Navigation("Flags");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("TrueCV.Infrastructure.Identity.ApplicationUser", b =>
|
modelBuilder.Entity("RealCV.Infrastructure.Identity.ApplicationUser", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("CVChecks");
|
b.Navigation("CVChecks");
|
||||||
});
|
});
|
||||||
@@ -5,14 +5,15 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Polly;
|
using Polly;
|
||||||
using Polly.Extensions.Http;
|
using Polly.Extensions.Http;
|
||||||
using TrueCV.Application.Interfaces;
|
using RealCV.Application.Interfaces;
|
||||||
using TrueCV.Infrastructure.Configuration;
|
using RealCV.Infrastructure.Clients;
|
||||||
using TrueCV.Infrastructure.Data;
|
using RealCV.Infrastructure.Configuration;
|
||||||
using TrueCV.Infrastructure.ExternalApis;
|
using RealCV.Infrastructure.Data;
|
||||||
using TrueCV.Infrastructure.Jobs;
|
using RealCV.Infrastructure.ExternalApis;
|
||||||
using TrueCV.Infrastructure.Services;
|
using RealCV.Infrastructure.Jobs;
|
||||||
|
using RealCV.Infrastructure.Services;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure;
|
namespace RealCV.Infrastructure;
|
||||||
|
|
||||||
public static class DependencyInjection
|
public static class DependencyInjection
|
||||||
{
|
{
|
||||||
@@ -74,6 +75,13 @@ public static class DependencyInjection
|
|||||||
services.Configure<LocalStorageSettings>(
|
services.Configure<LocalStorageSettings>(
|
||||||
configuration.GetSection(LocalStorageSettings.SectionName));
|
configuration.GetSection(LocalStorageSettings.SectionName));
|
||||||
|
|
||||||
|
// Configure options for additional verification APIs
|
||||||
|
services.Configure<FcaOptions>(
|
||||||
|
configuration.GetSection("FcaRegister"));
|
||||||
|
|
||||||
|
services.Configure<GitHubOptions>(
|
||||||
|
configuration.GetSection("GitHub"));
|
||||||
|
|
||||||
// Configure HttpClient for CompaniesHouseClient with retry policy
|
// Configure HttpClient for CompaniesHouseClient with retry policy
|
||||||
services.AddHttpClient<CompaniesHouseClient>((serviceProvider, client) =>
|
services.AddHttpClient<CompaniesHouseClient>((serviceProvider, client) =>
|
||||||
{
|
{
|
||||||
@@ -88,16 +96,34 @@ public static class DependencyInjection
|
|||||||
})
|
})
|
||||||
.AddPolicyHandler(GetRetryPolicy());
|
.AddPolicyHandler(GetRetryPolicy());
|
||||||
|
|
||||||
|
// Configure HttpClient for FCA Register API
|
||||||
|
services.AddHttpClient<FcaRegisterClient>()
|
||||||
|
.AddPolicyHandler(GetRetryPolicy());
|
||||||
|
|
||||||
|
// Configure HttpClient for GitHub API
|
||||||
|
services.AddHttpClient<GitHubApiClient>()
|
||||||
|
.AddPolicyHandler(GetRetryPolicy());
|
||||||
|
|
||||||
|
// Configure HttpClient for ORCID API
|
||||||
|
services.AddHttpClient<OrcidClient>()
|
||||||
|
.AddPolicyHandler(GetRetryPolicy());
|
||||||
|
|
||||||
// Register services
|
// Register services
|
||||||
services.AddScoped<ICVParserService, CVParserService>();
|
services.AddScoped<ICVParserService, CVParserService>();
|
||||||
services.AddScoped<ICompanyNameMatcherService, AICompanyNameMatcherService>();
|
services.AddScoped<ICompanyNameMatcherService, AICompanyNameMatcherService>();
|
||||||
services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
|
services.AddScoped<ICompanyVerifierService, CompanyVerifierService>();
|
||||||
services.AddScoped<IEducationVerifierService, EducationVerifierService>();
|
services.AddScoped<IEducationVerifierService, EducationVerifierService>();
|
||||||
services.AddScoped<ITimelineAnalyserService, TimelineAnalyserService>();
|
services.AddScoped<ITimelineAnalyserService, TimelineAnalyserService>();
|
||||||
|
services.AddScoped<ITextAnalysisService, TextAnalysisService>();
|
||||||
services.AddScoped<ICVCheckService, CVCheckService>();
|
services.AddScoped<ICVCheckService, CVCheckService>();
|
||||||
services.AddScoped<IUserContextService, UserContextService>();
|
services.AddScoped<IUserContextService, UserContextService>();
|
||||||
services.AddScoped<IAuditService, AuditService>();
|
services.AddScoped<IAuditService, AuditService>();
|
||||||
|
|
||||||
|
// Register additional verification services
|
||||||
|
services.AddScoped<IProfessionalVerifierService, ProfessionalVerifierService>();
|
||||||
|
services.AddScoped<IGitHubVerifierService, GitHubVerifierService>();
|
||||||
|
services.AddScoped<IAcademicVerifierService, AcademicVerifierService>();
|
||||||
|
|
||||||
// Register file storage - use local storage if configured, otherwise Azure
|
// Register file storage - use local storage if configured, otherwise Azure
|
||||||
var useLocalStorage = configuration.GetValue<bool>("UseLocalStorage");
|
var useLocalStorage = configuration.GetValue<bool>("UseLocalStorage");
|
||||||
if (useLocalStorage)
|
if (useLocalStorage)
|
||||||
@@ -6,10 +6,10 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using TrueCV.Application.DTOs;
|
using RealCV.Application.DTOs;
|
||||||
using TrueCV.Infrastructure.Configuration;
|
using RealCV.Infrastructure.Configuration;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.ExternalApis;
|
namespace RealCV.Infrastructure.ExternalApis;
|
||||||
|
|
||||||
public sealed class CompaniesHouseClient
|
public sealed class CompaniesHouseClient
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace TrueCV.Infrastructure.Helpers;
|
namespace RealCV.Infrastructure.Helpers;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper methods for processing AI/LLM JSON responses.
|
/// Helper methods for processing AI/LLM JSON responses.
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using TrueCV.Domain.Entities;
|
using RealCV.Domain.Entities;
|
||||||
using TrueCV.Domain.Enums;
|
using RealCV.Domain.Enums;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Identity;
|
namespace RealCV.Infrastructure.Identity;
|
||||||
|
|
||||||
public class ApplicationUser : IdentityUser<Guid>
|
public class ApplicationUser : IdentityUser<Guid>
|
||||||
{
|
{
|
||||||
@@ -12,5 +12,7 @@ public class ApplicationUser : IdentityUser<Guid>
|
|||||||
|
|
||||||
public int ChecksUsedThisMonth { get; set; }
|
public int ChecksUsedThisMonth { get; set; }
|
||||||
|
|
||||||
|
public DateTime? TermsAcceptedAt { get; set; }
|
||||||
|
|
||||||
public ICollection<CVCheck> CVChecks { get; set; } = new List<CVCheck>();
|
public ICollection<CVCheck> CVChecks { get; set; } = new List<CVCheck>();
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using TrueCV.Application.Helpers;
|
using RealCV.Application.Helpers;
|
||||||
using TrueCV.Application.Interfaces;
|
using RealCV.Application.Interfaces;
|
||||||
using TrueCV.Application.Models;
|
using RealCV.Application.Models;
|
||||||
using TrueCV.Domain.Entities;
|
using RealCV.Domain.Entities;
|
||||||
using TrueCV.Domain.Enums;
|
using RealCV.Domain.Enums;
|
||||||
using TrueCV.Infrastructure.Data;
|
using RealCV.Infrastructure.Data;
|
||||||
|
|
||||||
namespace TrueCV.Infrastructure.Jobs;
|
namespace RealCV.Infrastructure.Jobs;
|
||||||
|
|
||||||
public sealed class ProcessCVCheckJob
|
public sealed class ProcessCVCheckJob
|
||||||
{
|
{
|
||||||
@@ -18,19 +18,19 @@ public sealed class ProcessCVCheckJob
|
|||||||
private readonly ICompanyVerifierService _companyVerifierService;
|
private readonly ICompanyVerifierService _companyVerifierService;
|
||||||
private readonly IEducationVerifierService _educationVerifierService;
|
private readonly IEducationVerifierService _educationVerifierService;
|
||||||
private readonly ITimelineAnalyserService _timelineAnalyserService;
|
private readonly ITimelineAnalyserService _timelineAnalyserService;
|
||||||
|
private readonly ITextAnalysisService _textAnalysisService;
|
||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
private readonly ILogger<ProcessCVCheckJob> _logger;
|
private readonly ILogger<ProcessCVCheckJob> _logger;
|
||||||
|
|
||||||
private const int BaseScore = 100;
|
private const int BaseScore = 100;
|
||||||
private const int UnverifiedCompanyPenalty = 10;
|
private const int UnverifiedCompanyPenalty = 10;
|
||||||
private const int ImplausibleJobTitlePenalty = 15;
|
private const int ImplausibleJobTitlePenalty = 15;
|
||||||
private const int CompanyVerificationFlagPenalty = 5; // Base penalty for company flags, actual from flag.ScoreImpact
|
|
||||||
private const int RapidProgressionPenalty = 10;
|
private const int RapidProgressionPenalty = 10;
|
||||||
private const int EarlyCareerSeniorRolePenalty = 10;
|
private const int EarlyCareerSeniorRolePenalty = 10;
|
||||||
private const int GapMonthPenalty = 1;
|
private const int GapMonthPenalty = 1;
|
||||||
private const int MaxGapPenalty = 10;
|
private const int MaxGapPenalty = 10;
|
||||||
private const int OverlapMonthPenalty = 2;
|
private const int OverlapMonthPenalty = 2;
|
||||||
private const int DiplomaMillPenalty = 25;
|
private const int UnaccreditedInstitutionPenalty = 25;
|
||||||
private const int SuspiciousInstitutionPenalty = 15;
|
private const int SuspiciousInstitutionPenalty = 15;
|
||||||
private const int UnverifiedEducationPenalty = 5;
|
private const int UnverifiedEducationPenalty = 5;
|
||||||
private const int EducationDatePenalty = 10;
|
private const int EducationDatePenalty = 10;
|
||||||
@@ -42,6 +42,7 @@ public sealed class ProcessCVCheckJob
|
|||||||
ICompanyVerifierService companyVerifierService,
|
ICompanyVerifierService companyVerifierService,
|
||||||
IEducationVerifierService educationVerifierService,
|
IEducationVerifierService educationVerifierService,
|
||||||
ITimelineAnalyserService timelineAnalyserService,
|
ITimelineAnalyserService timelineAnalyserService,
|
||||||
|
ITextAnalysisService textAnalysisService,
|
||||||
IAuditService auditService,
|
IAuditService auditService,
|
||||||
ILogger<ProcessCVCheckJob> logger)
|
ILogger<ProcessCVCheckJob> logger)
|
||||||
{
|
{
|
||||||
@@ -51,6 +52,7 @@ public sealed class ProcessCVCheckJob
|
|||||||
_companyVerifierService = companyVerifierService;
|
_companyVerifierService = companyVerifierService;
|
||||||
_educationVerifierService = educationVerifierService;
|
_educationVerifierService = educationVerifierService;
|
||||||
_timelineAnalyserService = timelineAnalyserService;
|
_timelineAnalyserService = timelineAnalyserService;
|
||||||
|
_textAnalysisService = textAnalysisService;
|
||||||
_auditService = auditService;
|
_auditService = auditService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -183,11 +185,11 @@ public sealed class ProcessCVCheckJob
|
|||||||
cvData.Employment);
|
cvData.Employment);
|
||||||
|
|
||||||
_logger.LogDebug(
|
_logger.LogDebug(
|
||||||
"Education verification for check {CheckId}: {Count} entries verified ({Recognised} recognised, {DiplomaMill} diploma mills)",
|
"Education verification for check {CheckId}: {Count} entries verified ({Recognised} recognised, {Unaccredited} unaccredited)",
|
||||||
cvCheckId,
|
cvCheckId,
|
||||||
educationResults.Count,
|
educationResults.Count,
|
||||||
educationResults.Count(e => e.IsVerified),
|
educationResults.Count(e => e.IsVerified),
|
||||||
educationResults.Count(e => e.IsDiplomaMill));
|
educationResults.Count(e => e.IsUnaccredited));
|
||||||
|
|
||||||
// Step 7: Analyse timeline
|
// Step 7: Analyse timeline
|
||||||
cvCheck.ProcessingStage = "Analysing Timeline";
|
cvCheck.ProcessingStage = "Analysing Timeline";
|
||||||
@@ -199,10 +201,23 @@ public sealed class ProcessCVCheckJob
|
|||||||
"Timeline analysis for check {CheckId}: {GapCount} gaps, {OverlapCount} overlaps",
|
"Timeline analysis for check {CheckId}: {GapCount} gaps, {OverlapCount} overlaps",
|
||||||
cvCheckId, timelineAnalysis.Gaps.Count, timelineAnalysis.Overlaps.Count);
|
cvCheckId, timelineAnalysis.Gaps.Count, timelineAnalysis.Overlaps.Count);
|
||||||
|
|
||||||
|
// Step 7b: Analyse text for buzzwords, vague achievements, skills alignment, and metrics
|
||||||
|
cvCheck.ProcessingStage = "Analysing Content";
|
||||||
|
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||||
|
|
||||||
|
var textAnalysis = _textAnalysisService.Analyse(cvData);
|
||||||
|
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Text analysis for check {CheckId}: {BuzzwordCount} buzzwords, {VagueCount} vague statements, {MismatchCount} skill mismatches",
|
||||||
|
cvCheckId,
|
||||||
|
textAnalysis.BuzzwordAnalysis.TotalBuzzwords,
|
||||||
|
textAnalysis.AchievementAnalysis.VagueStatements,
|
||||||
|
textAnalysis.SkillsAlignment.Mismatches.Count);
|
||||||
|
|
||||||
// Step 8: Calculate veracity score
|
// Step 8: Calculate veracity score
|
||||||
cvCheck.ProcessingStage = "Calculating Score";
|
cvCheck.ProcessingStage = "Calculating Score";
|
||||||
await _dbContext.SaveChangesAsync(cancellationToken);
|
await _dbContext.SaveChangesAsync(cancellationToken);
|
||||||
var (score, flags) = CalculateVeracityScore(verificationResults, educationResults, timelineAnalysis, cvData);
|
var (score, flags) = CalculateVeracityScore(verificationResults, educationResults, timelineAnalysis, textAnalysis, cvData);
|
||||||
|
|
||||||
_logger.LogDebug("Calculated veracity score for check {CheckId}: {Score}", cvCheckId, score);
|
_logger.LogDebug("Calculated veracity score for check {CheckId}: {Score}", cvCheckId, score);
|
||||||
|
|
||||||
@@ -247,6 +262,7 @@ public sealed class ProcessCVCheckJob
|
|||||||
EmploymentVerifications = verificationResults,
|
EmploymentVerifications = verificationResults,
|
||||||
EducationVerifications = educationResults,
|
EducationVerifications = educationResults,
|
||||||
TimelineAnalysis = timelineAnalysis,
|
TimelineAnalysis = timelineAnalysis,
|
||||||
|
TextAnalysis = textAnalysis,
|
||||||
Flags = flags,
|
Flags = flags,
|
||||||
GeneratedAt = DateTime.UtcNow
|
GeneratedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
@@ -291,6 +307,7 @@ public sealed class ProcessCVCheckJob
|
|||||||
List<CompanyVerificationResult> verifications,
|
List<CompanyVerificationResult> verifications,
|
||||||
List<EducationVerificationResult> educationResults,
|
List<EducationVerificationResult> educationResults,
|
||||||
TimelineAnalysisResult timeline,
|
TimelineAnalysisResult timeline,
|
||||||
|
TextAnalysisResult textAnalysis,
|
||||||
CVData cvData)
|
CVData cvData)
|
||||||
{
|
{
|
||||||
var score = BaseScore;
|
var score = BaseScore;
|
||||||
@@ -389,23 +406,23 @@ public sealed class ProcessCVCheckJob
|
|||||||
AddPLCExperienceFlag(verifications, flags);
|
AddPLCExperienceFlag(verifications, flags);
|
||||||
AddVerifiedDirectorFlag(verifications, flags);
|
AddVerifiedDirectorFlag(verifications, flags);
|
||||||
|
|
||||||
// Penalty for diploma mills (critical)
|
// Penalty for unaccredited institutions (critical)
|
||||||
foreach (var edu in educationResults.Where(e => e.IsDiplomaMill))
|
foreach (var edu in educationResults.Where(e => e.IsUnaccredited))
|
||||||
{
|
{
|
||||||
score -= DiplomaMillPenalty;
|
score -= UnaccreditedInstitutionPenalty;
|
||||||
|
|
||||||
flags.Add(new FlagResult
|
flags.Add(new FlagResult
|
||||||
{
|
{
|
||||||
Category = FlagCategory.Education.ToString(),
|
Category = FlagCategory.Education.ToString(),
|
||||||
Severity = FlagSeverity.Critical.ToString(),
|
Severity = FlagSeverity.Critical.ToString(),
|
||||||
Title = "Diploma Mill Detected",
|
Title = "Unaccredited Institution",
|
||||||
Description = $"'{edu.ClaimedInstitution}' is a known diploma mill. {edu.VerificationNotes}",
|
Description = $"'{edu.ClaimedInstitution}' is not found in the register of recognised institutions. {edu.VerificationNotes}",
|
||||||
ScoreImpact = -DiplomaMillPenalty
|
ScoreImpact = -UnaccreditedInstitutionPenalty
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Penalty for suspicious institutions
|
// Penalty for suspicious institutions
|
||||||
foreach (var edu in educationResults.Where(e => e.IsSuspicious && !e.IsDiplomaMill))
|
foreach (var edu in educationResults.Where(e => e.IsSuspicious && !e.IsUnaccredited))
|
||||||
{
|
{
|
||||||
score -= SuspiciousInstitutionPenalty;
|
score -= SuspiciousInstitutionPenalty;
|
||||||
|
|
||||||
@@ -413,15 +430,15 @@ public sealed class ProcessCVCheckJob
|
|||||||
{
|
{
|
||||||
Category = FlagCategory.Education.ToString(),
|
Category = FlagCategory.Education.ToString(),
|
||||||
Severity = FlagSeverity.Warning.ToString(),
|
Severity = FlagSeverity.Warning.ToString(),
|
||||||
Title = "Suspicious Institution",
|
Title = "Institution Requires Verification",
|
||||||
Description = $"'{edu.ClaimedInstitution}' has suspicious characteristics. {edu.VerificationNotes}",
|
Description = $"'{edu.ClaimedInstitution}' has characteristics that warrant additional verification. {edu.VerificationNotes}",
|
||||||
ScoreImpact = -SuspiciousInstitutionPenalty
|
ScoreImpact = -SuspiciousInstitutionPenalty
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Penalty for unverified education (not recognised, but not flagged as fake)
|
// Penalty for unverified education (not recognised, but not flagged as unaccredited)
|
||||||
// Skip unknown/empty institutions as there's nothing to verify
|
// Skip unknown/empty institutions as there's nothing to verify
|
||||||
foreach (var edu in educationResults.Where(e => !e.IsVerified && !e.IsDiplomaMill && !e.IsSuspicious && e.Status == "Unknown"
|
foreach (var edu in educationResults.Where(e => !e.IsVerified && !e.IsUnaccredited && !e.IsSuspicious && e.Status == "Unknown"
|
||||||
&& !string.IsNullOrWhiteSpace(e.ClaimedInstitution)
|
&& !string.IsNullOrWhiteSpace(e.ClaimedInstitution)
|
||||||
&& !e.ClaimedInstitution.Equals("Unknown Institution", StringComparison.OrdinalIgnoreCase)
|
&& !e.ClaimedInstitution.Equals("Unknown Institution", StringComparison.OrdinalIgnoreCase)
|
||||||
&& !e.ClaimedInstitution.Equals("Unknown", StringComparison.OrdinalIgnoreCase)))
|
&& !e.ClaimedInstitution.Equals("Unknown", StringComparison.OrdinalIgnoreCase)))
|
||||||
@@ -485,6 +502,32 @@ public sealed class ProcessCVCheckJob
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process text analysis flags (buzzwords, vague achievements, skills alignment, metrics)
|
||||||
|
foreach (var textFlag in textAnalysis.Flags)
|
||||||
|
{
|
||||||
|
score += textFlag.ScoreImpact; // ScoreImpact is already negative
|
||||||
|
|
||||||
|
flags.Add(new FlagResult
|
||||||
|
{
|
||||||
|
Category = FlagCategory.Plausibility.ToString(),
|
||||||
|
Severity = textFlag.Severity,
|
||||||
|
Title = textFlag.Type switch
|
||||||
|
{
|
||||||
|
"ExcessiveBuzzwords" => "Excessive Buzzwords",
|
||||||
|
"HighBuzzwordCount" => "High Buzzword Count",
|
||||||
|
"VagueAchievements" => "Vague Achievements",
|
||||||
|
"LackOfQuantification" => "Lack of Quantification",
|
||||||
|
"SkillsJobMismatch" => "Skills/Job Mismatch",
|
||||||
|
"UnrealisticMetrics" => "Unrealistic Metrics",
|
||||||
|
"UnrealisticMetric" => "Unrealistic Metric",
|
||||||
|
"SuspiciouslyRoundNumbers" => "Suspiciously Round Numbers",
|
||||||
|
_ => textFlag.Type
|
||||||
|
},
|
||||||
|
Description = textFlag.Message,
|
||||||
|
ScoreImpact = textFlag.ScoreImpact
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Deduplicate flags based on Title + Description
|
// Deduplicate flags based on Title + Description
|
||||||
var uniqueFlags = flags
|
var uniqueFlags = flags
|
||||||
.GroupBy(f => (f.Title, f.Description))
|
.GroupBy(f => (f.Title, f.Description))
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\TrueCV.Application\TrueCV.Application.csproj" />
|
<ProjectReference Include="..\RealCV.Application\RealCV.Application.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||