diff --git a/backend/app/api/v1/campaigns.py b/backend/app/api/v1/campaigns.py index c572aa4..6c2691e 100644 --- a/backend/app/api/v1/campaigns.py +++ b/backend/app/api/v1/campaigns.py @@ -1,5 +1,4 @@ import base64 -import uuid from datetime import datetime, timezone from uuid import UUID diff --git a/backend/app/api/v1/certificates.py b/backend/app/api/v1/certificates.py index 9ce4a0e..a828afd 100644 --- a/backend/app/api/v1/certificates.py +++ b/backend/app/api/v1/certificates.py @@ -226,7 +226,7 @@ async def list_donor_certificates( if not user or (user.id != donor_id and user.role.value != "admin"): query = select(DonationCertificate).where( DonationCertificate.donation.has(donor_id=donor_id), - DonationCertificate.is_public == True, + DonationCertificate.is_public, ) else: query = select(DonationCertificate).where( diff --git a/backend/app/api/v1/fund_release.py b/backend/app/api/v1/fund_release.py index 9349898..61ab3da 100644 --- a/backend/app/api/v1/fund_release.py +++ b/backend/app/api/v1/fund_release.py @@ -7,7 +7,7 @@ # --- Constants & Thresholds --- TIER_1_THRESHOLD_PHP = 15000.00 -EMERGENCY_RELEASE_CAP_PERCENT = 0.50 +EMERGENCY_RELEASE_CAP_PERCENT = 0.50 CREDIBILITY_PENALTY = 15.0 CRITICAL_CREDIBILITY_SCORE = 50.0 @@ -70,7 +70,7 @@ async def process_fund_release(request: FundReleaseRequest): organizer = evaluate_account_standing(organizer) if organizer.is_frozen: raise HTTPException( - status_code=status.HTTP_403_FORBIDDEN, + status_code=status.HTTP_403_FORBIDDEN, detail="Account frozen: Pending or overdue receipts detected." ) @@ -78,20 +78,20 @@ async def process_fund_release(request: FundReleaseRequest): # Tier 1 Logic: Emergency Quick-Release if not organizer.registered_off_ramp_partner: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, + status_code=status.HTTP_400_BAD_REQUEST, detail="Unregistered off-ramp partner. Cannot route emergency funds." ) - + capped_amount = min(request.requested_amount, request.total_pool_balance * EMERGENCY_RELEASE_CAP_PERCENT) organizer.receipt_state = ReceiptState.PENDING - + return FundReleaseResponse( status="APPROVED", tier=1, released_amount=capped_amount, message=f"Emergency funds routed to {organizer.registered_off_ramp_partner}. Post-expenditure receipt required.", account_updates={ - "receipt_state": organizer.receipt_state.value, + "receipt_state": organizer.receipt_state.value, "credibility_score": organizer.credibility_score } ) @@ -99,22 +99,22 @@ async def process_fund_release(request: FundReleaseRequest): # Tier 2 Logic: Milestone-Based Capital if not request.current_milestone_id: raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, + status_code=status.HTTP_400_BAD_REQUEST, detail="Milestone ID required for Tier 2 capital release." ) - + milestone = milestones_db.get(request.current_milestone_id) if not milestone: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Milestone tracking not found.") - + if milestone.state != MilestoneState.VERIFIED: raise HTTPException( - status_code=status.HTTP_423_LOCKED, + status_code=status.HTTP_423_LOCKED, detail="Programmatic lock enforced: Preceding milestone proof of completion not explicitly verified." ) - + allowed_release = min(request.requested_amount, milestone.amount, request.total_pool_balance) - + return FundReleaseResponse( status="APPROVED", tier=2, @@ -127,8 +127,8 @@ async def milestone_validation_hook(milestone_id: str, proof_url: str): milestone = milestones_db.get(milestone_id) if not milestone: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Milestone not found.") - + milestone.proof_of_completion = proof_url milestone.state = MilestoneState.VERIFIED - + return {"status": "success", "message": f"Milestone {milestone_id} explicitly verified. Subsequent payouts unlocked."} diff --git a/backend/app/api/v1/onchain_certificates.py b/backend/app/api/v1/onchain_certificates.py index 32026f5..737d494 100644 --- a/backend/app/api/v1/onchain_certificates.py +++ b/backend/app/api/v1/onchain_certificates.py @@ -2,7 +2,6 @@ from __future__ import annotations import uuid -from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession diff --git a/backend/app/api/v1/volunteer.py b/backend/app/api/v1/volunteer.py index 2c58fb4..5ac38ad 100644 --- a/backend/app/api/v1/volunteer.py +++ b/backend/app/api/v1/volunteer.py @@ -14,7 +14,6 @@ VolunteerSignup, VolunteerCategory, OpportunityStatus, - SignupStatus, ) router = APIRouter(prefix="/volunteer", tags=["volunteer"]) diff --git a/backend/app/certificates/__init__.py b/backend/app/certificates/__init__.py index 6765cf2..a255f9e 100644 --- a/backend/app/certificates/__init__.py +++ b/backend/app/certificates/__init__.py @@ -48,7 +48,6 @@ def generate_certificate_pdf( emerald = HexColor("#10B891") navy = HexColor("#0F172A") - gold = HexColor("#F5B923") text_color = HexColor("#4B5563") title_style = ParagraphStyle( diff --git a/backend/app/certificates/onchain_service.py b/backend/app/certificates/onchain_service.py index 8b82bb8..d0172de 100644 --- a/backend/app/certificates/onchain_service.py +++ b/backend/app/certificates/onchain_service.py @@ -7,11 +7,6 @@ generate_svg_certificate, get_certificate_hash, ) -from app.storage.svg_s3 import ( - upload_svg_certificate, - upload_certificate_html_wrapper, - StoredSVGCertificate, -) class OnChainCertificateService: @@ -86,11 +81,9 @@ async def generate_and_store_certificate( svg_hash = get_certificate_hash(svg_content) - # Step 3: Generate HTML wrapper with embedded SVG for social previews - # Step 3 and 4: Upload to public S3 bucket (async) - # We no longer need the PNG fallback wrapper since the new generator handles HTML - svg_stored = await upload_svg_certificate(svg_content, donation_id) - html_s3_url = await upload_certificate_html_wrapper(svg_content, donation_id) + # Step 3: Persist only metadata in DB; no external object storage required. + svg_public_url = "" + html_public_url = "" # Step 5: Compute certificate integrity hash (for future merkle proofs) cert_integrity_hash = hashlib.sha256( @@ -98,8 +91,8 @@ async def generate_and_store_certificate( ).hexdigest() return { - "svg_s3_url": svg_stored.s3_url, - "html_s3_url": html_s3_url, + "svg_s3_url": svg_public_url, + "html_s3_url": html_public_url, "svg_hash": svg_hash, "verified": True, "tx_verified": tx_verified, diff --git a/backend/app/certificates/png_hydrator.py b/backend/app/certificates/png_hydrator.py index 041fe44..7492002 100644 --- a/backend/app/certificates/png_hydrator.py +++ b/backend/app/certificates/png_hydrator.py @@ -6,7 +6,6 @@ from __future__ import annotations import logging -import textwrap import time from io import BytesIO from datetime import datetime diff --git a/backend/app/certificates/service.py b/backend/app/certificates/service.py index b5a7d11..55fdb5e 100644 --- a/backend/app/certificates/service.py +++ b/backend/app/certificates/service.py @@ -1,8 +1,8 @@ """Certificate creation service - orchestrates certificate generation and storage.""" from __future__ import annotations +import hashlib import logging -import uuid from typing import Optional from sqlalchemy.ext.asyncio import AsyncSession @@ -15,8 +15,6 @@ from app.models.user import User from app.models.provenance import ProvenanceRecord from app.models.campaign_drive import CampaignDrive -from app.certificates.generator import generate_certificate_pdf -from app.storage.s3 import upload_certificate_pdf logger = logging.getLogger(__name__) diff --git a/backend/app/certificates/svg_generator.py b/backend/app/certificates/svg_generator.py index c4d08ca..4178365 100644 --- a/backend/app/certificates/svg_generator.py +++ b/backend/app/certificates/svg_generator.py @@ -771,8 +771,6 @@ def generate_svg_certificate( merkle_proof=merkle_proof, onchain_hash=onchain_hash, ) - html_escaped = html.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) - svg = f"""