File size: 5,647 Bytes
bfcc872
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
"""
Citation and sourcing utilities to reduce hallucinations.

When presenting grant information, we cite the source data:
- Grant ID and title
- Specific fields (funding_max, deadline, etc.)
- Field source (from grant record or extracted)

This helps users verify information and trust the system.
"""

from typing import Dict, Any, List, Optional
from datetime import datetime


def cite_grant_fact(grant: Dict[str, Any], field: str, value: Any) -> str:
    """
    Create a citation for a fact about a grant.

    Args:
        grant: The grant record dict
        field: Field name (e.g., "funding_max", "close_date")
        value: The value to cite

    Returns:
        Formatted citation like "£50,000 [Source: Grant ID #2315]"
    """
    grant_id = grant.get("id") or grant.get("competition_id") or "unknown"

    # Special formatting for currency
    if field in ("funding_max", "funding_min", "total_pot") and isinstance(value, (int, float)):
        return f"£{value:,.0f} [Source: Grant ID #{grant_id}]"

    # Special formatting for dates
    if field in ("close_date", "open_date", "deadline") and value:
        return f"{value} [Source: Grant ID #{grant_id}]"

    # Default formatting
    if value is None:
        return "Not specified"

    return f"{value} [Source: Grant ID #{grant_id}]"


def format_grant_with_citations(grant: Dict[str, Any]) -> Dict[str, Any]:
    """
    Format a grant record with citations on key fields.

    Args:
        grant: The grant record

    Returns:
        Dict with cited versions of key fields
    """
    grant_id = grant.get("id") or grant.get("competition_id") or "unknown"
    title = grant.get("title", "(untitled)")

    cited = {
        "id": grant_id,
        "title": title,
        "url": grant.get("url", ""),
    }

    # Cite monetary fields
    if grant.get("funding_max"):
        cited["funding_max"] = cite_grant_fact(grant, "funding_max", grant["funding_max"])
    if grant.get("funding_min"):
        cited["funding_min"] = cite_grant_fact(grant, "funding_min", grant["funding_min"])
    if grant.get("total_pot"):
        cited["total_pot"] = cite_grant_fact(grant, "total_pot", grant["total_pot"])

    # Cite dates
    if grant.get("close_date"):
        cited["close_date"] = cite_grant_fact(grant, "close_date", grant["close_date"])
    if grant.get("open_date"):
        cited["open_date"] = cite_grant_fact(grant, "open_date", grant["open_date"])

    return cited


def build_citation_summary(grant: Dict[str, Any]) -> str:
    """
    Build a markdown summary of grant with citations.

    Args:
        grant: The grant record

    Returns:
        Markdown formatted summary with citations
    """
    grant_id = grant.get("id") or grant.get("competition_id") or "unknown"
    title = grant.get("title", "(untitled)")
    url = grant.get("url", "")

    lines = [
        f"## {title}",
        f"**Grant ID:** #{grant_id}",
        "",
    ]

    if url:
        lines.append(f"**Official Link:** {url}")
        lines.append("")

    # Funding details with citations
    if grant.get("funding_max") or grant.get("funding_min"):
        min_fund = grant.get("funding_min")
        max_fund = grant.get("funding_max")

        if min_fund and max_fund:
            funding_str = f"£{min_fund:,.0f} – £{max_fund:,.0f}"
        elif max_fund:
            funding_str = f"up to £{max_fund:,.0f}"
        elif min_fund:
            funding_str = f"from £{min_fund:,.0f}"
        else:
            funding_str = "See official page"

        lines.append(f"**Funding per project:** {funding_str} [Source: Grant ID #{grant_id}]")

    if grant.get("total_pot"):
        lines.append(f"**Total available:** £{grant['total_pot']:,.0f} [Source: Grant ID #{grant_id}]")

    lines.append("")

    # Dates with citations
    if grant.get("open_date") or grant.get("close_date"):
        close = grant.get("close_date") or grant.get("deadline")
        if close:
            lines.append(f"**Deadline:** {close} [Source: Grant ID #{grant_id}]")
        if grant.get("open_date"):
            lines.append(f"**Opens:** {grant['open_date']} [Source: Grant ID #{grant_id}]")

    lines.append("")

    # Duration with citation
    if grant.get("duration_min") or grant.get("duration_max"):
        min_dur = grant.get("duration_min")
        max_dur = grant.get("duration_max")

        if min_dur and max_dur:
            duration_str = f"{min_dur}{max_dur} months"
        elif max_dur:
            duration_str = f"up to {max_dur} months"
        elif min_dur:
            duration_str = f"from {min_dur} months"
        else:
            duration_str = "Variable"

        lines.append(f"**Project duration:** {duration_str} [Source: Grant ID #{grant_id}]")

    return "\n".join(lines)


def validate_fact_availability(grant: Dict[str, Any], fact_type: str) -> bool:
    """
    Check if a fact exists in the grant data before citing it.

    Args:
        grant: The grant record
        fact_type: Type of fact ("funding", "deadline", "duration", "scope", "eligibility")

    Returns:
        True if the fact is available to cite

    Raises assertion/warning if fact is missing
    """
    fact_fields = {
        "funding": ["funding_max", "funding_min", "total_pot"],
        "deadline": ["close_date", "deadline"],
        "duration": ["duration_min", "duration_max"],
        "scope": ["scope", "scope_raw", "sections"],
        "eligibility": ["eligibility", "eligibility_raw"],
    }

    required_fields = fact_fields.get(fact_type, [])

    for field in required_fields:
        if grant.get(field):
            return True

    return False