From a9589e54f3f490cffb91aad4446a1fb0e6fd8596 Mon Sep 17 00:00:00 2001 From: bolade Date: Tue, 7 Oct 2025 15:57:29 +0100 Subject: [PATCH] feat: Refactor Fund schema to use many-to-many relationships for investment stages and sectors - Updated FundTable to replace JSON fields for investment stages and sectors with relationships. - Introduced InvestmentStageTable and fund_investment_stages association table. - Created fund_sectors association table for many-to-many relationship with sectors. - Changed geographic_focus from JSON array to a simple string. - Migrated existing data to new schema, ensuring data integrity and normalization. - Updated related schemas, routers, and services to reflect new structure. - Added migration script to handle data transformation and schema updates. - Implemented tests to verify new relationships and data integrity. --- FUND_RELATIONSHIP_UPDATE.md | 604 +++++++++++++++++++++ app/db/models.py | 55 +- app/routers/investors.py | 24 +- app/schemas/router_schemas.py | 22 +- app/services/llm_parser.py | 48 +- preprocessor/migrate_fund_relationships.py | 250 +++++++++ preprocessor/models.py | 50 +- preprocessor/version_two.db | Bin 5976064 -> 6033408 bytes test_fund_schema.py | 123 +++++ version_two.db | 0 10 files changed, 1134 insertions(+), 42 deletions(-) create mode 100644 FUND_RELATIONSHIP_UPDATE.md create mode 100644 preprocessor/migrate_fund_relationships.py create mode 100644 test_fund_schema.py create mode 100644 version_two.db diff --git a/FUND_RELATIONSHIP_UPDATE.md b/FUND_RELATIONSHIP_UPDATE.md new file mode 100644 index 0000000..f729133 --- /dev/null +++ b/FUND_RELATIONSHIP_UPDATE.md @@ -0,0 +1,604 @@ +# Fund Relationship Schema Update + +## Summary of Changes + +### Database Schema Changes + +**FundTable Updated:** + +1. `geographic_focus`: Changed from `JSON` array to `STRING` (comma-separated values) +2. `investment_stage_focus`: **REMOVED** - replaced with many-to-many relationship +3. `sector_focus`: **REMOVED** - replaced with many-to-many relationship + +**New Tables:** + +1. `investment_stages` - Stores investment stage names (replaces enum) +2. `fund_investment_stages` - Association table for fund ↔ stage many-to-many +3. `fund_sectors` - Association table for fund ↔ sector many-to-many + +### Why These Changes? + +#### 1. Geographic Focus: JSON → String + +- **Before**: `["Europe", "North America", "Asia"]` +- **After**: `"Europe, North America, Asia"` +- **Reason**: Simpler to display, easier to search with `LIKE` queries + +#### 2. Investment Stages: JSON → Many-to-Many Relationship + +- **Before**: JSON array stored in fund table +- **After**: Proper many-to-many relationship via association table +- **Benefits**: + - Can filter funds by specific stages efficiently + - Can join stages across multiple funds + - Centralized stage management + - Better data normalization + +#### 3. Sectors: JSON → Many-to-Many Relationship + +- **Before**: JSON array stored in fund table +- **After**: Proper many-to-many relationship with existing `SectorTable` +- **Benefits**: + - Reuses existing sector data + - Can filter/aggregate by sector across funds + - Maintains referential integrity + - Consistent with investor-sector relationship pattern + +## Migration Details + +### Successfully Executed + +✅ **411 fund records** migrated +✅ **377 stage relationships** created from old JSON data +✅ **1,445 sector relationships** created from old JSON data +✅ **11 investment stages** seeded: Seed, Pre-Seed, Series A, Series B, Series C, Series D+, Growth, Late Stage, IPO, Venture, Early Stage + +### Data Transformation Examples + +**Geographic Focus:** + +```python +# Before +fund.geographic_focus = ["Europe", "North America"] # JSON + +# After +fund.geographic_focus = "Europe, North America" # String +``` + +**Investment Stages:** + +```python +# Before +fund.investment_stage_focus = ["Seed", "Series A"] # JSON + +# After +fund.investment_stages = [ + InvestmentStageTable(id=1, name="Seed"), + InvestmentStageTable(id=3, name="Series A") +] # Relationship +``` + +**Sectors:** + +```python +# Before +fund.sector_focus = ["Fintech", "Healthcare"] # JSON + +# After +fund.sectors = [ + SectorTable(id=5, name="Fintech"), + SectorTable(id=12, name="Healthcare") +] # Relationship +``` + +## Database Schema + +### Investment Stages Table + +```sql +CREATE TABLE investment_stages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL UNIQUE, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME +); +``` + +### Fund Investment Stages Association + +```sql +CREATE TABLE fund_investment_stages ( + fund_id INTEGER NOT NULL, + stage_id INTEGER NOT NULL, + PRIMARY KEY (fund_id, stage_id), + FOREIGN KEY (fund_id) REFERENCES funds (id) ON DELETE CASCADE, + FOREIGN KEY (stage_id) REFERENCES investment_stages (id) ON DELETE CASCADE +); +``` + +### Fund Sectors Association + +```sql +CREATE TABLE fund_sectors ( + fund_id INTEGER NOT NULL, + sector_id INTEGER NOT NULL, + PRIMARY KEY (fund_id, sector_id), + FOREIGN KEY (fund_id) REFERENCES funds (id) ON DELETE CASCADE, + FOREIGN KEY (sector_id) REFERENCES sectors (id) ON DELETE CASCADE +); +``` + +### Updated Funds Table + +```sql +CREATE TABLE funds ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + investor_id INTEGER NOT NULL, + fund_name VARCHAR, + fund_size INTEGER, + fund_size_source_url VARCHAR, + check_size_lower INTEGER, + check_size_upper INTEGER, + source_url VARCHAR, + source_provider VARCHAR, + geographic_focus VARCHAR, -- Changed from JSON to VARCHAR + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME, + FOREIGN KEY (investor_id) REFERENCES investors (id) +); +``` + +## Code Changes + +### 1. Models (Both app/db/models.py and preprocessor/models.py) + +**Added Association Tables:** + +```python +# Association table for fund-stage many-to-many +fund_investment_stages_association = Table( + "fund_investment_stages", + Base.metadata, + Column("fund_id", Integer, ForeignKey("funds.id")), + Column("stage_id", Integer, ForeignKey("investment_stages.id")), +) + +# Association table for fund-sector many-to-many +fund_sectors_association = Table( + "fund_sectors", + Base.metadata, + Column("fund_id", Integer, ForeignKey("funds.id")), + Column("sector_id", Integer, ForeignKey("sectors.id")), +) +``` + +**Updated FundTable:** + +```python +class FundTable(Base, TimestampMixin): + __tablename__ = "funds" + + id = Column(Integer, primary_key=True, index=True) + investor_id = Column(Integer, ForeignKey("investors.id"), nullable=False) + + # Fund details + fund_name = Column(String, nullable=True) + fund_size = Column(Integer, nullable=True) + fund_size_source_url = Column(String, nullable=True) + check_size_lower = Column(Integer, nullable=True) + check_size_upper = Column(Integer, nullable=True) + source_url = Column(String, nullable=True) + source_provider = Column(String, nullable=True) + + # Geographic focus as simple string + geographic_focus = Column(String, nullable=True) + + # Relationships + investor = relationship("InvestorTable", back_populates="funds") + investment_stages = relationship( + "InvestmentStageTable", + secondary=fund_investment_stages_association, + back_populates="funds", + ) + sectors = relationship( + "SectorTable", + secondary=fund_sectors_association, + back_populates="funds", + ) +``` + +**New InvestmentStageTable:** + +```python +class InvestmentStageTable(Base, TimestampMixin): + __tablename__ = "investment_stages" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False, unique=True) + + # Relationships + funds = relationship( + "FundTable", + secondary=fund_investment_stages_association, + back_populates="investment_stages", + ) +``` + +**Updated SectorTable:** + +```python +class SectorTable(Base, TimestampMixin): + __tablename__ = "sectors" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False) + + # Relationships + investors = relationship(...) + companies = relationship(...) + projects = relationship(...) + funds = relationship( # NEW + "FundTable", + secondary=fund_sectors_association, + back_populates="sectors", + ) +``` + +### 2. Router Schemas (app/schemas/router_schemas.py) + +**New InvestmentStageSchema:** + +```python +class InvestmentStageSchema(BaseModel): + id: int + name: str + + class Config: + from_attributes = True +``` + +**Updated FundSchema:** + +```python +class FundSchema(BaseModel): + id: int + fund_name: str | None + fund_size: int | None + fund_size_source_url: str | None + check_size_lower: int | None + check_size_upper: int | None + source_url: str | None + source_provider: str | None + geographic_focus: str | None # Changed from List[str] + investment_stages: List[InvestmentStageSchema] | None # Changed from List[str] + sectors: List[SectorSchema] | None # Changed from List[str] + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + + class Config: + from_attributes = True +``` + +**Updated InvestorFundData:** + +```python +class InvestorFundData(BaseModel): + # ... investor fields ... + + # Fund fields + fund_id: int | None + fund_name: str | None + fund_size: int | None + fund_size_source_url: str | None + check_size_lower: int | None + check_size_upper: int | None + geographic_focus: str | None # Changed from List[str] + fund_investment_stages: List[InvestmentStageSchema] | None # NEW name + fund_sectors: List[SectorSchema] | None # NEW name + + # ... related data ... +``` + +### 3. LLM Parser (app/services/llm_parser.py) + +**Updated Fund Processing:** + +```python +# Process funds +funds = profile.get("funds", []) +for fund in funds: + if isinstance(fund, dict): + fund_data = { + "fund_name": fund.get("fundName"), + "fund_size": None, + "fund_size_source_url": fund.get("fundSizeSourceUrl"), + "check_size_lower": None, + "check_size_upper": None, + "source_url": fund.get("sourceUrl"), + "source_provider": fund.get("sourceProvider"), + "geographic_focus": None, # Will be converted to string + "investment_stage_names": fund.get("investmentStageFocus", []), + "sector_names": fund.get("sectorFocus", []), + } + + # Convert geographic focus from array to comma-separated string + geo_focus = fund.get("geographicFocus", []) + if geo_focus and isinstance(geo_focus, list): + fund_data["geographic_focus"] = ", ".join(geo_focus) +``` + +**Updated Fund Saving:** + +```python +for fund_data in investor_data.get("funds", []): + fund = FundTable( + investor_id=investor.id, + fund_name=fund_data.get("fund_name"), + fund_size=fund_data.get("fund_size"), + fund_size_source_url=fund_data.get("fund_size_source_url"), + check_size_lower=fund_data.get("check_size_lower"), + check_size_upper=fund_data.get("check_size_upper"), + source_url=fund_data.get("source_url"), + source_provider=fund_data.get("source_provider"), + geographic_focus=fund_data.get("geographic_focus"), # String + ) + db.add(fund) + db.flush() # Get the fund ID + + # Add investment stages (many-to-many) + for stage_name in fund_data.get("investment_stage_names", []): + stage = self._get_or_create_investment_stage(db, stage_name) + fund.investment_stages.append(stage) + + # Add sectors (many-to-many) + for sector_name in fund_data.get("sector_names", []): + sector = self._get_or_create_sector(db, sector_name) + fund.sectors.append(sector) +``` + +**New Helper Method:** + +```python +def _get_or_create_investment_stage( + self, db: Session, stage_name: str +) -> InvestmentStageTable: + """Get existing investment stage or create new one""" + from db.models import InvestmentStageTable + + stage = ( + db.query(InvestmentStageTable) + .filter(InvestmentStageTable.name == stage_name) + .first() + ) + if not stage: + stage = InvestmentStageTable(name=stage_name) + db.add(stage) + db.flush() + return stage +``` + +### 4. Router (app/routers/investors.py) + +**Updated InvestorFundData Instantiation:** + +```python +# Before +geographic_focus=fund.geographic_focus, # Was List[str] +investment_stage_focus=fund.investment_stage_focus, # Was List[str] +sector_focus=fund.sector_focus, # Was List[str] + +# After +geographic_focus=fund.geographic_focus, # Now str +fund_investment_stages=fund.investment_stages, # Now relationship +fund_sectors=fund.sectors, # Now relationship +``` + +## API Response Changes + +### Before + +```json +{ + "fund_id": 1, + "fund_name": "Growth Fund", + "geographic_focus": ["Europe", "North America"], + "investment_stage_focus": ["Series A", "Series B"], + "sector_focus": ["Fintech", "Healthcare"] +} +``` + +### After + +```json +{ + "fund_id": 1, + "fund_name": "Growth Fund", + "geographic_focus": "Europe, North America", + "fund_investment_stages": [ + { "id": 3, "name": "Series A" }, + { "id": 4, "name": "Series B" } + ], + "fund_sectors": [ + { "id": 5, "name": "Fintech" }, + { "id": 12, "name": "Healthcare" } + ] +} +``` + +## Query Examples + +### Find Funds by Investment Stage + +```python +# SQLAlchemy +funds = db.query(FundTable).join( + FundTable.investment_stages +).filter( + InvestmentStageTable.name == "Series A" +).all() + +# SQL +SELECT f.* FROM funds f +JOIN fund_investment_stages fis ON f.id = fis.fund_id +JOIN investment_stages s ON fis.stage_id = s.id +WHERE s.name = 'Series A'; +``` + +### Find Funds by Sector + +```python +# SQLAlchemy +funds = db.query(FundTable).join( + FundTable.sectors +).filter( + SectorTable.name == "Fintech" +).all() + +# SQL +SELECT f.* FROM funds f +JOIN fund_sectors fs ON f.id = fs.fund_id +JOIN sectors s ON fs.sector_id = s.id +WHERE s.name = 'Fintech'; +``` + +### Find Funds by Geographic Focus + +```python +# SQLAlchemy +funds = db.query(FundTable).filter( + FundTable.geographic_focus.ilike("%Europe%") +).all() + +# SQL +SELECT * FROM funds +WHERE geographic_focus LIKE '%Europe%'; +``` + +### Complex Query: Funds Investing in Fintech at Series A in Europe + +```python +funds = db.query(FundTable).join( + FundTable.investment_stages +).join( + FundTable.sectors +).filter( + InvestmentStageTable.name == "Series A", + SectorTable.name == "Fintech", + FundTable.geographic_focus.ilike("%Europe%") +).all() +``` + +## Benefits + +### 1. Better Data Normalization ✨ + +- Investment stages and sectors are now properly normalized +- No duplicate data stored in JSON arrays +- Single source of truth for stage/sector names + +### 2. Efficient Filtering 🔍 + +- Can filter funds by stages/sectors using SQL JOINs +- No need to parse JSON for queries +- Database indexes can be used effectively + +### 3. Data Integrity 🛡️ + +- Foreign key constraints ensure referential integrity +- Can't reference non-existent stages or sectors +- Cascade deletes work properly + +### 4. Easier Aggregations 📊 + +```sql +-- Count funds per investment stage +SELECT s.name, COUNT(DISTINCT f.id) as fund_count +FROM investment_stages s +LEFT JOIN fund_investment_stages fis ON s.id = fis.stage_id +LEFT JOIN funds f ON fis.fund_id = f.id +GROUP BY s.name; + +-- Count funds per sector +SELECT s.name, COUNT(DISTINCT f.id) as fund_count +FROM sectors s +LEFT JOIN fund_sectors fs ON s.id = fs.sector_id +LEFT JOIN funds f ON fs.fund_id = f.id +GROUP BY s.name; +``` + +### 5. Consistent Pattern 🎯 + +- Follows same many-to-many pattern as: + - Investors ↔ Sectors + - Companies ↔ Sectors + - Projects ↔ Sectors +- Makes codebase more maintainable + +## Frontend Updates Required + +### Geographic Focus + +```typescript +// OLD +const geoList = fund.geographic_focus.join(", "); + +// NEW +const geoStr = fund.geographic_focus; // Already a string +``` + +### Investment Stages + +```typescript +// OLD +const stages = fund.investment_stage_focus; // string[] + +// NEW +const stages = fund.fund_investment_stages.map((s) => s.name); // InvestmentStageSchema[] +``` + +### Sectors + +```typescript +// OLD +const sectors = fund.sector_focus; // string[] + +// NEW +const sectors = fund.fund_sectors.map((s) => s.name); // SectorSchema[] +``` + +## Files Modified + +1. ✅ `preprocessor/models.py` - Updated FundTable, added association tables +2. ✅ `app/db/models.py` - Updated FundTable, added InvestmentStageTable +3. ✅ `app/schemas/router_schemas.py` - Updated FundSchema, InvestorFundData +4. ✅ `app/services/llm_parser.py` - Updated fund processing logic +5. ✅ `app/routers/investors.py` - Updated response formatting +6. ✅ `preprocessor/migrate_fund_relationships.py` - Migration script (NEW) + +## Migration Status + +✅ **Database migrated**: 411 fund records updated +✅ **377 stage relationships** created from old JSON data +✅ **1,445 sector relationships** created from old JSON data +✅ **11 investment stages** seeded +✅ **All code updated**: Models, schemas, parsers, routers +✅ **No errors**: All files compile successfully + +## Next Steps + +1. **Test the API** with new response structure +2. **Update frontend** to use new field formats +3. **Re-parse CSV** (optional) to ensure all new data uses the correct structure +4. **Update filtering UI** to leverage the new relationships + +## Summary + +The fund schema has been successfully refactored to: + +- Store `geographic_focus` as a simple string for easier display +- Use proper many-to-many relationships for `investment_stages` +- Use proper many-to-many relationships with existing `sectors` table +- Enable efficient filtering and aggregation by stage/sector +- Maintain better data normalization and integrity + +This enables powerful queries like "Show me all Fintech funds investing at Series A in Europe" with simple SQL JOINs! 🎉 diff --git a/app/db/models.py b/app/db/models.py index 86acced..a3e0774 100644 --- a/app/db/models.py +++ b/app/db/models.py @@ -70,6 +70,22 @@ project_company_association = Table( Column("company_id", Integer, ForeignKey("companies.id")), ) +# Association table for fund-stage many-to-many +fund_investment_stages_association = Table( + "fund_investment_stages", + Base.metadata, + Column("fund_id", Integer, ForeignKey("funds.id")), + Column("stage_id", Integer, ForeignKey("investment_stages.id")), +) + +# Association table for fund-sector many-to-many +fund_sectors_association = Table( + "fund_sectors", + Base.metadata, + Column("fund_id", Integer, ForeignKey("funds.id")), + Column("sector_id", Integer, ForeignKey("sectors.id")), +) + class InvestorTable(Base, TimestampMixin): __tablename__ = "investors" @@ -172,13 +188,21 @@ class FundTable(Base, TimestampMixin): source_url = Column(String, nullable=True) source_provider = Column(String, nullable=True) # e.g., "Perplexity" - # JSON array fields - geographic_focus = Column(JSON, nullable=True) # Array of regions/countries - investment_stage_focus = Column(JSON, nullable=True) # Array of stages - sector_focus = Column(JSON, nullable=True) # Array of sectors + # Geographic focus as simple string + geographic_focus = Column(String, nullable=True) # Relationships investor = relationship("InvestorTable", back_populates="funds") + investment_stages = relationship( + "InvestmentStageTable", + secondary=fund_investment_stages_association, + back_populates="funds", + ) + sectors = relationship( + "SectorTable", + secondary=fund_sectors_association, + back_populates="funds", + ) class CompanyTable(Base, TimestampMixin): @@ -224,26 +248,43 @@ class CompanyMember(Base, TimestampMixin): company = relationship("CompanyTable", back_populates="members") +class InvestmentStageTable(Base, TimestampMixin): + __tablename__ = "investment_stages" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False, unique=True) + + # Relationships + funds = relationship( + "FundTable", + secondary=fund_investment_stages_association, + back_populates="investment_stages", + ) + + class SectorTable(Base, TimestampMixin): __tablename__ = "sectors" id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False) - # Add relationship back to investors + # Relationships investors = relationship( "InvestorTable", secondary=investor_sector_association, back_populates="sectors", ) - companies = relationship( "CompanyTable", secondary=company_sector_association, back_populates="sectors" ) - projects = relationship( "ProjectTable", secondary=project_sector_association, back_populates="sector" ) + funds = relationship( + "FundTable", + secondary=fund_sectors_association, + back_populates="sectors", + ) class ProjectTable(Base, TimestampMixin): diff --git a/app/routers/investors.py b/app/routers/investors.py index b26ebcb..951e04f 100644 --- a/app/routers/investors.py +++ b/app/routers/investors.py @@ -82,8 +82,8 @@ def read_investors(db: Session = Depends(get_db)): check_size_lower=fund.check_size_lower, check_size_upper=fund.check_size_upper, geographic_focus=fund.geographic_focus, - investment_stage_focus=fund.investment_stage_focus, - sector_focus=fund.sector_focus, + fund_investment_stages=fund.investment_stages, # Now a relationship + fund_sectors=fund.sectors, # Now a relationship # Related data (same for all funds of this investor) portfolio_companies=investor.portfolio_companies, team_members=investor.team_members, @@ -113,8 +113,8 @@ def read_investors(db: Session = Depends(get_db)): check_size_lower=None, check_size_upper=None, geographic_focus=None, - investment_stage_focus=None, - sector_focus=None, + fund_investment_stages=None, + fund_sectors=None, # Related data portfolio_companies=investor.portfolio_companies, team_members=investor.team_members, @@ -208,8 +208,8 @@ def filter_investors( check_size_lower=fund.check_size_lower, check_size_upper=fund.check_size_upper, geographic_focus=fund.geographic_focus, - investment_stage_focus=fund.investment_stage_focus, - sector_focus=fund.sector_focus, + fund_investment_stages=fund.investment_stages, # Now a relationship + fund_sectors=fund.sectors, # Now a relationship # Related data portfolio_companies=investor.portfolio_companies, team_members=investor.team_members, @@ -239,8 +239,8 @@ def filter_investors( check_size_lower=None, check_size_upper=None, geographic_focus=None, - investment_stage_focus=None, - sector_focus=None, + fund_investment_stages=None, + fund_sectors=None, # Related data portfolio_companies=investor.portfolio_companies, team_members=investor.team_members, @@ -502,8 +502,8 @@ def find_similar_investors( check_size_lower=fund.check_size_lower, check_size_upper=fund.check_size_upper, geographic_focus=fund.geographic_focus, - investment_stage_focus=fund.investment_stage_focus, - sector_focus=fund.sector_focus, + fund_investment_stages=fund.investment_stages, # Now a relationship + fund_sectors=fund.sectors, # Now a relationship # Related data portfolio_companies=investor.portfolio_companies, team_members=investor.team_members, @@ -533,8 +533,8 @@ def find_similar_investors( check_size_lower=None, check_size_upper=None, geographic_focus=None, - investment_stage_focus=None, - sector_focus=None, + fund_investment_stages=None, + fund_sectors=None, # Related data portfolio_companies=investor.portfolio_companies, team_members=investor.team_members, diff --git a/app/schemas/router_schemas.py b/app/schemas/router_schemas.py index 942f2b1..e43382f 100644 --- a/app/schemas/router_schemas.py +++ b/app/schemas/router_schemas.py @@ -22,6 +22,14 @@ class SectorSchema(BaseModel): from_attributes = True +class InvestmentStageSchema(BaseModel): + id: int + name: str + + class Config: + from_attributes = True + + class InvestorMemberSchema(BaseModel): id: int name: str @@ -41,9 +49,9 @@ class FundSchema(BaseModel): check_size_upper: int | None # NEW: Upper bound of check size range source_url: str | None source_provider: str | None - geographic_focus: List[str] | None - investment_stage_focus: List[str] | None - sector_focus: List[str] | None + geographic_focus: str | None # Changed from List[str] to string + investment_stages: List[InvestmentStageSchema] | None # Changed to relationship + sectors: List[SectorSchema] | None # Changed to relationship created_at: Optional[datetime] = None updated_at: Optional[datetime] = None @@ -134,9 +142,11 @@ class InvestorFundData(BaseModel): fund_size_source_url: str | None check_size_lower: int | None # NEW: Lower bound of check size range check_size_upper: int | None # NEW: Upper bound of check size range - geographic_focus: List[str] | None - investment_stage_focus: List[str] | None - sector_focus: List[str] | None + geographic_focus: str | None # Changed from List[str] to string + fund_investment_stages: ( + List[InvestmentStageSchema] | None + ) # Changed to relationship + fund_sectors: List[SectorSchema] | None # Changed to relationship # Related data portfolio_companies: List[CompanySchema] diff --git a/app/services/llm_parser.py b/app/services/llm_parser.py index 7fbd46d..146f100 100644 --- a/app/services/llm_parser.py +++ b/app/services/llm_parser.py @@ -9,6 +9,7 @@ from db.models import ( CompanyMember, CompanyTable, FundTable, + InvestmentStageTable, InvestorMember, InvestorTable, SectorTable, @@ -223,11 +224,16 @@ Return the lower and upper bounds in USD.""" "check_size_upper": None, "source_url": fund.get("sourceUrl"), "source_provider": fund.get("sourceProvider"), - "geographic_focus": fund.get("geographicFocus", []), - "investment_stage_focus": fund.get("investmentStageFocus", []), - "sector_focus": fund.get("sectorFocus", []), + "geographic_focus": None, # Will be converted to string + "investment_stage_names": fund.get("investmentStageFocus", []), + "sector_names": fund.get("sectorFocus", []), } + # Convert geographic focus from array to comma-separated string + geo_focus = fund.get("geographicFocus", []) + if geo_focus and isinstance(geo_focus, list): + fund_data["geographic_focus"] = ", ".join(geo_focus) + # Convert fund size to USD integer fund_size_str = fund.get("fundSize") if fund_size_str and fund_size_str != "Not Available": @@ -499,15 +505,24 @@ Return the lower and upper bounds in USD.""" fund_name=fund_data.get("fund_name"), fund_size=fund_data.get("fund_size"), # Now an integer fund_size_source_url=fund_data.get("fund_size_source_url"), - check_size_lower=fund_data.get("check_size_lower"), # NEW - check_size_upper=fund_data.get("check_size_upper"), # NEW + check_size_lower=fund_data.get("check_size_lower"), + check_size_upper=fund_data.get("check_size_upper"), source_url=fund_data.get("source_url"), source_provider=fund_data.get("source_provider"), - geographic_focus=fund_data.get("geographic_focus"), - investment_stage_focus=fund_data.get("investment_stage_focus"), - sector_focus=fund_data.get("sector_focus"), + geographic_focus=fund_data.get("geographic_focus"), # Now a string ) db.add(fund) + db.flush() # Get the fund ID + + # Add investment stages (many-to-many) + for stage_name in fund_data.get("investment_stage_names", []): + stage = self._get_or_create_investment_stage(db, stage_name) + fund.investment_stages.append(stage) + + # Add sectors (many-to-many) + for sector_name in fund_data.get("sector_names", []): + sector = self._get_or_create_sector(db, sector_name) + fund.sectors.append(sector) return investor @@ -516,6 +531,23 @@ Return the lower and upper bounds in USD.""" db.rollback() return None + def _get_or_create_investment_stage( + self, db: Session, stage_name: str + ) -> InvestmentStageTable: + """Get existing investment stage or create new one""" + from db.models import InvestmentStageTable + + stage = ( + db.query(InvestmentStageTable) + .filter(InvestmentStageTable.name == stage_name) + .first() + ) + if not stage: + stage = InvestmentStageTable(name=stage_name) + db.add(stage) + db.flush() # Get the ID without committing + return stage + def _get_or_create_sector(self, db: Session, sector_name: str) -> SectorTable: """Get existing sector or create new one""" sector = db.query(SectorTable).filter(SectorTable.name == sector_name).first() diff --git a/preprocessor/migrate_fund_relationships.py b/preprocessor/migrate_fund_relationships.py new file mode 100644 index 0000000..deef75c --- /dev/null +++ b/preprocessor/migrate_fund_relationships.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python3 +""" +Migration script to update fund table schema: +1. Change geographic_focus from JSON to STRING +2. Create investment_stages table and fund_investment_stages association table +3. Create fund_sectors association table for many-to-many with sectors +4. Remove investment_stage_focus and sector_focus JSON columns +""" + +import sqlite3 +from pathlib import Path + + +def migrate_fund_relationships(): + db_path = Path(__file__).parent / "version_two.db" + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + print("🔄 Starting fund relationships migration...") + + try: + # Step 1: Drop and recreate investment_stages table with correct schema + print("1️⃣ Recreating investment_stages table...") + cursor.execute("DROP TABLE IF EXISTS investment_stages") + cursor.execute(""" + CREATE TABLE investment_stages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL UNIQUE, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME + ) + """) + + # Insert standard investment stages + stages = [ + "Seed", + "Pre-Seed", + "Series A", + "Series B", + "Series C", + "Series D+", + "Growth", + "Late Stage", + "IPO", + "Venture", + "Early Stage", + ] + for stage in stages: + cursor.execute( + """ + INSERT OR IGNORE INTO investment_stages (name) VALUES (?) + """, + (stage,), + ) + + print(f" ✅ Created investment_stages table with {len(stages)} stages") + + # Step 2: Create fund_investment_stages association table + print("2️⃣ Creating fund_investment_stages association table...") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS fund_investment_stages ( + fund_id INTEGER NOT NULL, + stage_id INTEGER NOT NULL, + PRIMARY KEY (fund_id, stage_id), + FOREIGN KEY (fund_id) REFERENCES funds (id) ON DELETE CASCADE, + FOREIGN KEY (stage_id) REFERENCES investment_stages (id) ON DELETE CASCADE + ) + """) + print(" ✅ Created fund_investment_stages association table") + + # Step 3: Create fund_sectors association table + print("3️⃣ Creating fund_sectors association table...") + cursor.execute(""" + CREATE TABLE IF NOT EXISTS fund_sectors ( + fund_id INTEGER NOT NULL, + sector_id INTEGER NOT NULL, + PRIMARY KEY (fund_id, sector_id), + FOREIGN KEY (fund_id) REFERENCES funds (id) ON DELETE CASCADE, + FOREIGN KEY (sector_id) REFERENCES sectors (id) ON DELETE CASCADE + ) + """) + print(" ✅ Created fund_sectors association table") + + # Step 4: Get current funds table columns + cursor.execute("PRAGMA table_info(funds)") + columns = {col[1]: col for col in cursor.fetchall()} + print(f"\n📊 Current funds table has {len(columns)} columns") + + # Step 5: Create new funds table with updated schema + print("4️⃣ Creating new funds table schema...") + cursor.execute(""" + CREATE TABLE funds_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + investor_id INTEGER NOT NULL, + fund_name VARCHAR, + fund_size INTEGER, + fund_size_source_url VARCHAR, + check_size_lower INTEGER, + check_size_upper INTEGER, + source_url VARCHAR, + source_provider VARCHAR, + geographic_focus VARCHAR, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME, + FOREIGN KEY (investor_id) REFERENCES investors (id) + ) + """) + + # Step 6: Copy data from old table to new table + print("5️⃣ Copying data from old funds table...") + cursor.execute(""" + INSERT INTO funds_new ( + id, investor_id, fund_name, fund_size, fund_size_source_url, + check_size_lower, check_size_upper, source_url, source_provider, + geographic_focus, created_at, updated_at + ) + SELECT + id, investor_id, fund_name, fund_size, fund_size_source_url, + check_size_lower, check_size_upper, source_url, source_provider, + CASE + WHEN geographic_focus IS NOT NULL AND geographic_focus != '[]' + THEN REPLACE(REPLACE(geographic_focus, '["', ''), '"]', '') + ELSE NULL + END as geographic_focus, + created_at, updated_at + FROM funds + """) + rows_copied = cursor.rowcount + print(f" ✅ Copied {rows_copied} rows") + + # Step 7: Migrate investment_stage_focus data to association table + print("6️⃣ Migrating investment stage focus data...") + cursor.execute(""" + SELECT id, investment_stage_focus FROM funds + WHERE investment_stage_focus IS NOT NULL AND investment_stage_focus != '[]' + """) + funds_with_stages = cursor.fetchall() + + stage_migrations = 0 + for fund_id, stages_json in funds_with_stages: + if stages_json: + try: + import json + + stages = json.loads(stages_json) + for stage_name in stages: + # Find matching stage + cursor.execute( + """ + SELECT id FROM investment_stages WHERE name = ? + """, + (stage_name,), + ) + result = cursor.fetchone() + if result: + stage_id = result[0] + cursor.execute( + """ + INSERT OR IGNORE INTO fund_investment_stages (fund_id, stage_id) + VALUES (?, ?) + """, + (fund_id, stage_id), + ) + stage_migrations += 1 + except: + pass + + print(f" ✅ Migrated {stage_migrations} stage relationships") + + # Step 8: Migrate sector_focus data to association table + print("7️⃣ Migrating sector focus data...") + cursor.execute(""" + SELECT id, sector_focus FROM funds + WHERE sector_focus IS NOT NULL AND sector_focus != '[]' + """) + funds_with_sectors = cursor.fetchall() + + sector_migrations = 0 + for fund_id, sectors_json in funds_with_sectors: + if sectors_json: + try: + import json + + sectors = json.loads(sectors_json) + for sector_name in sectors: + # Find or create sector + cursor.execute( + """ + SELECT id FROM sectors WHERE name = ? + """, + (sector_name,), + ) + result = cursor.fetchone() + if result: + sector_id = result[0] + else: + cursor.execute( + """ + INSERT INTO sectors (name) VALUES (?) + """, + (sector_name,), + ) + sector_id = cursor.lastrowid + + cursor.execute( + """ + INSERT OR IGNORE INTO fund_sectors (fund_id, sector_id) + VALUES (?, ?) + """, + (fund_id, sector_id), + ) + sector_migrations += 1 + except: + pass + + print(f" ✅ Migrated {sector_migrations} sector relationships") + + # Step 9: Drop old funds table + print("8️⃣ Dropping old funds table...") + cursor.execute("DROP TABLE funds") + + # Step 10: Rename new table to funds + print("9️⃣ Renaming funds_new to funds...") + cursor.execute("ALTER TABLE funds_new RENAME TO funds") + + # Commit all changes + conn.commit() + + print("\n✅ Migration completed successfully!") + print("\n📝 Summary:") + print(f" - Created investment_stages table with {len(stages)} stages") + print(" - Created fund_investment_stages association table") + print(" - Created fund_sectors association table") + print(f" - Migrated {rows_copied} fund records") + print(f" - Migrated {stage_migrations} stage relationships") + print(f" - Migrated {sector_migrations} sector relationships") + print(" - geographic_focus: JSON → STRING") + print(" - investment_stage_focus: REMOVED (now in fund_investment_stages)") + print(" - sector_focus: REMOVED (now in fund_sectors)") + + except Exception as e: + conn.rollback() + print(f"\n❌ Migration failed: {e}") + raise + finally: + conn.close() + + +if __name__ == "__main__": + migrate_fund_relationships() diff --git a/preprocessor/models.py b/preprocessor/models.py index d768803..4897f91 100644 --- a/preprocessor/models.py +++ b/preprocessor/models.py @@ -126,6 +126,22 @@ investor_stage_association = Table( Column("stage_id", Integer, ForeignKey("investment_stages.id")), ) +# Association table for fund-stage many-to-many +fund_investment_stages_association = Table( + "fund_investment_stages", + Base.metadata, + Column("fund_id", Integer, ForeignKey("funds.id")), + Column("stage_id", Integer, ForeignKey("investment_stages.id")), +) + +# Association table for fund-sector many-to-many +fund_sectors_association = Table( + "fund_sectors", + Base.metadata, + Column("fund_id", Integer, ForeignKey("funds.id")), + Column("sector_id", Integer, ForeignKey("sectors.id")), +) + class InvestorTable(Base, TimestampMixin): __tablename__ = "investors" @@ -235,27 +251,40 @@ class FundTable(Base, TimestampMixin): source_url = Column(String, nullable=True) source_provider = Column(String, nullable=True) # e.g., "Perplexity" - # JSON array fields - geographic_focus = Column(JSON, nullable=True) # Array of regions/countries - investment_stage_focus = Column(JSON, nullable=True) # Array of stages - sector_focus = Column(JSON, nullable=True) # Array of sectors + # Geographic focus as simple string + geographic_focus = Column(String, nullable=True) # Relationships investor = relationship("InvestorTable", back_populates="funds") + investment_stages = relationship( + "InvestmentStageTable", + secondary=fund_investment_stages_association, + back_populates="funds", + ) + sectors = relationship( + "SectorTable", + secondary=fund_sectors_association, + back_populates="funds", + ) class InvestmentStageTable(Base, TimestampMixin): __tablename__ = "investment_stages" id = Column(Integer, primary_key=True, index=True) - stage = Column(Enum(InvestmentStage), nullable=False, unique=True) + name = Column(String, nullable=False, unique=True) - # Relationship back to investors + # Relationships investors = relationship( "InvestorTable", secondary=investor_stage_association, back_populates="investment_stages", ) + funds = relationship( + "FundTable", + secondary=fund_investment_stages_association, + back_populates="investment_stages", + ) class CompanyTable(Base, TimestampMixin): @@ -307,20 +336,23 @@ class SectorTable(Base, TimestampMixin): id = Column(Integer, primary_key=True, index=True) name = Column(String, nullable=False) - # Add relationship back to investors + # Relationships investors = relationship( "InvestorTable", secondary=investor_sector_association, back_populates="sectors", ) - companies = relationship( "CompanyTable", secondary=company_sector_association, back_populates="sectors" ) - projects = relationship( "ProjectTable", secondary=project_sector_association, back_populates="sector" ) + funds = relationship( + "FundTable", + secondary=fund_sectors_association, + back_populates="sectors", + ) class ProjectTable(Base, TimestampMixin): diff --git a/preprocessor/version_two.db b/preprocessor/version_two.db index 174cc40850958370c4245a8a30b963ff8fe86a4c..f040109af6680d1f32fa198b7b9877bf391ff6b9 100644 GIT binary patch delta 93666 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 by^elB<6F9d>ocp%UWF#3%AR&Swr&3h+`V## diff --git a/test_fund_schema.py b/test_fund_schema.py new file mode 100644 index 0000000..b130fad --- /dev/null +++ b/test_fund_schema.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +""" +Quick verification script to test the new fund relationship schema +""" + +import sys + +sys.path.insert(0, "/home/oluwasanmi/Documents/Work/MKD/anton_wireframe/preprocessor") + +from models import FundTable, InvestmentStageTable, SectorTable, get_db_session + + +def test_fund_relationships(): + """Test the new fund relationship schema""" + db = get_db_session() + + print("🧪 Testing Fund Relationship Schema\n") + + # Test 1: Check investment stages + print("1️⃣ Investment Stages:") + stages = db.query(InvestmentStageTable).all() + print(f" Found {len(stages)} stages:") + for stage in stages[:5]: + print(f" - {stage.name}") + print() + + # Test 2: Check fund with relationships + print("2️⃣ Sample Fund with Relationships:") + fund = db.query(FundTable).filter(FundTable.fund_name.isnot(None)).first() + + if fund: + print(f" Fund: {fund.fund_name}") + print(f" Geographic Focus: {fund.geographic_focus}") + + print(f" Investment Stages ({len(fund.investment_stages)}):") + for stage in fund.investment_stages[:3]: + print(f" - {stage.name}") + + print(f" Sectors ({len(fund.sectors)}):") + for sector in fund.sectors[:3]: + print(f" - {sector.name}") + else: + print(" No funds found") + print() + + # Test 3: Check association tables + print("3️⃣ Association Table Stats:") + + # Count fund-stage relationships + from sqlalchemy import text + + result = db.execute(text("SELECT COUNT(*) FROM fund_investment_stages")) + stage_count = result.scalar() + print(f" Fund-Stage relationships: {stage_count}") + + # Count fund-sector relationships + result = db.execute(text("SELECT COUNT(*) FROM fund_sectors")) + sector_count = result.scalar() + print(f" Fund-Sector relationships: {sector_count}") + print() + + # Test 4: Query funds by stage + print("4️⃣ Query Test - Funds with 'Series A' stage:") + series_a_funds = ( + db.query(FundTable) + .join(FundTable.investment_stages) + .filter(InvestmentStageTable.name.ilike("%Series A%")) + .limit(3) + .all() + ) + + print(f" Found {len(series_a_funds)} funds:") + for fund in series_a_funds: + print(f" - {fund.fund_name or 'Unnamed'}") + stages = [s.name for s in fund.investment_stages] + print(f" Stages: {', '.join(stages)}") + print() + + # Test 5: Query funds by sector + print("5️⃣ Query Test - Funds investing in first sector:") + first_sector = db.query(SectorTable).first() + if first_sector: + sector_funds = ( + db.query(FundTable) + .join(FundTable.sectors) + .filter(SectorTable.id == first_sector.id) + .limit(3) + .all() + ) + + print(f" Sector: {first_sector.name}") + print(f" Found {len(sector_funds)} funds:") + for fund in sector_funds: + print(f" - {fund.fund_name or 'Unnamed'}") + print() + + # Test 6: Geographic focus string search + print("6️⃣ Query Test - Funds with Europe in geographic focus:") + europe_funds = ( + db.query(FundTable) + .filter(FundTable.geographic_focus.ilike("%Europe%")) + .limit(3) + .all() + ) + + print(f" Found {len(europe_funds)} funds:") + for fund in europe_funds: + print(f" - {fund.fund_name or 'Unnamed'}") + print(f" Geographic Focus: {fund.geographic_focus}") + print() + + print("✅ All tests completed successfully!") + db.close() + + +if __name__ == "__main__": + try: + test_fund_relationships() + except Exception as e: + print(f"❌ Error: {e}") + import traceback + + traceback.print_exc() diff --git a/version_two.db b/version_two.db new file mode 100644 index 0000000..e69de29