|
import gradio as gr |
|
from textblob import TextBlob |
|
from pydantic import BaseModel, Field, validator |
|
from typing import Union |
|
|
|
import gradio as gr |
|
from textblob import TextBlob |
|
import requests |
|
from bs4 import BeautifulSoup |
|
nation_states_metrics = { |
|
0: "civil rights", |
|
1: "economy", |
|
2: "political freedoms", |
|
3: "population", |
|
4: "wealth gaps", |
|
5: "death rate (née unexpected death rate)", |
|
6: "compassion", |
|
7: "eco-friendliness", |
|
8: "social conservatism", |
|
9: "nudity", |
|
10: "industry: automobile manufacturing", |
|
11: "industry: cheese exports", |
|
12: "industry: basket weaving", |
|
13: "industry: information technology", |
|
14: "industry: pizza delivery", |
|
15: "industry: trout fishing", |
|
16: "industry: arms manufacturing", |
|
17: "sector: agriculture", |
|
18: "industry: beverage sales", |
|
19: "industry: timber woodchipping", |
|
20: "industry: mining", |
|
21: "industry: insurance", |
|
22: "industry: furniture restoration", |
|
23: "industry: retail", |
|
24: "industry: book publishing", |
|
25: "industry: gambling", |
|
26: "sector: manufacturing", |
|
27: "government size", |
|
28: "welfare", |
|
29: "public healthcare", |
|
30: "law enforcement", |
|
31: "business subsidization", |
|
32: "religiousness", |
|
33: "income equality", |
|
34: "niceness", |
|
35: "rudeness", |
|
36: "intelligence", |
|
37: "ignorance (née stupidity)", |
|
38: "political apathy", |
|
39: "health", |
|
40: "cheerfulness (née happiness)", |
|
41: "weather", |
|
42: "compliance (née safety from crime)", |
|
43: "safety", |
|
44: "lifespan", |
|
45: "ideological radicality", |
|
46: "defense forces", |
|
47: "pacifism", |
|
48: "economic freedom (née most pro-market)", |
|
49: "taxation", |
|
50: "freedom from taxation", |
|
51: "corruption", |
|
52: "integrity (née freedom from corruption)", |
|
53: "authoritarianism", |
|
54: "youth rebelliousness", |
|
55: "culture", |
|
56: "employment", |
|
57: "public transport", |
|
58: "tourism", |
|
59: "weaponization", |
|
60: "recreational drug use", |
|
61: "obesity", |
|
62: "secularism (née godlessness)", |
|
63: "environmental beauty", |
|
64: "charmlessness (née toxicity)", |
|
65: "influence", |
|
66: "world assembly endorsements", |
|
67: "averageness", |
|
68: "human development index", |
|
69: "primitiveness", |
|
70: "scientific advancement", |
|
71: "inclusiveness", |
|
72: "average income", |
|
73: "average income of poor", |
|
74: "average income of rich", |
|
75: "public education", |
|
76: "economic output", |
|
77: "crime", |
|
78: "foreign aid", |
|
79: "black market", |
|
80: "residency", |
|
81: "survivors", |
|
82: "zombies", |
|
83: "dead", |
|
84: "percentage zombies", |
|
85: "average disposable income", |
|
86: "international artwork", |
|
87: "patriotism", |
|
88: "food quality" |
|
} |
|
|
|
class GetNationIssuesRequest(BaseModel): |
|
"""Pydantic model for get_nation_issues request parameters.""" |
|
nation: str = Field(..., description="The nation name") |
|
password: str = Field(..., description="The API password") |
|
|
|
class AddressNationIssuesRequest(BaseModel): |
|
"""Pydantic model for address_nation_issues request parameters.""" |
|
nation: str = Field(..., description="The nation name") |
|
password: str = Field(..., description="The API password") |
|
issue_id: Union[str, int] = Field(..., description="The issue ID to address") |
|
option_id: Union[str, int] = Field(..., description="The chosen option ID for the issue") |
|
|
|
@validator('issue_id', 'option_id', pre=True) |
|
def convert_to_string(cls, v): |
|
"""Convert issue_id and option_id to strings for consistent processing.""" |
|
return str(v) |
|
|
|
def get_nation_issues(nation: str, password: str): |
|
""" |
|
Fetch nation issues using validated parameters. |
|
|
|
Args: |
|
nation (str): The nation name |
|
password (str): The API password |
|
|
|
Returns: |
|
dict: Formatted nation issues data |
|
""" |
|
|
|
request_data = GetNationIssuesRequest(nation=nation, password=password) |
|
|
|
url = f"https://www.nationstates.net/cgi-bin/api.cgi" |
|
headers = { |
|
"X-Password": request_data.password, |
|
"User-Agent": "Nationstate LLM" |
|
} |
|
params = { |
|
"nation": request_data.nation, |
|
"q": "issues" |
|
} |
|
response = requests.get(url, headers=headers, params=params) |
|
|
|
def get_nation_issues_JSON(sml_data): |
|
""" |
|
Fetches and reformats issues from the NationStates API. |
|
|
|
Args: |
|
nation (str): The nation name. |
|
password (str): The API password. |
|
|
|
Returns: |
|
dict: A dictionary containing the nation ID and a list of issue details. |
|
""" |
|
|
|
|
|
soup = BeautifulSoup(sml_data, "xml") |
|
|
|
|
|
nation_elem = soup.find("NATION") |
|
nation_id = nation_elem.get("id") if nation_elem else "Unknown" |
|
|
|
|
|
issues_list = [] |
|
|
|
|
|
if nation_elem: |
|
for issue in nation_elem.find_all("ISSUE"): |
|
issue_data = { |
|
"issue_id": issue.get("id", "Unknown"), |
|
"title": issue.TITLE.get_text() if issue.TITLE else "No title available", |
|
"text": issue.TEXT.get_text() if issue.TEXT else "No text available", |
|
"author": issue.AUTHOR.get_text() if issue.AUTHOR else "Unknown author", |
|
"editor": issue.EDITOR.get_text() if issue.EDITOR else "Unknown editor", |
|
"pic1": issue.PIC1.get_text() if issue.PIC1 else "No image 1", |
|
"pic2": issue.PIC2.get_text() if issue.PIC2 else "No image 2", |
|
"options": [] |
|
} |
|
|
|
|
|
for option in issue.find_all("OPTION"): |
|
option_data = { |
|
"option_id": option.get("id", "Unknown"), |
|
"text": option.get_text() if option else "No option text" |
|
} |
|
issue_data["options"].append(option_data) |
|
|
|
|
|
issues_list.append(issue_data) |
|
|
|
return {"nation_id": nation_id, "issues": issues_list} |
|
|
|
return get_nation_issues_JSON(response.text) |
|
|
|
|
|
|
|
|
|
def address_nation_issues(nation: str, password: str, issue_id: Union[str, int], option_id: Union[str, int]): |
|
""" |
|
Address a specific issue for a nation in NationStates API. |
|
|
|
Args: |
|
nation (str): The nation name. |
|
password (str): The API password. |
|
issue_id (Union[str, int]): The issue ID to address. |
|
option_id (Union[str, int]): The chosen option ID for the issue. |
|
|
|
Returns: |
|
dict: Formatted nation data response. |
|
""" |
|
|
|
request_data = AddressNationIssuesRequest( |
|
nation=nation, |
|
password=password, |
|
issue_id=issue_id, |
|
option_id=option_id |
|
) |
|
|
|
url = "https://www.nationstates.net/cgi-bin/api.cgi" |
|
|
|
headers = { |
|
"X-Password": request_data.password, |
|
"User-Agent": "Nationstate LLM" |
|
} |
|
|
|
data = { |
|
"nation": request_data.nation, |
|
"c": "issue", |
|
"issue": request_data.issue_id, |
|
"option": request_data.option_id |
|
} |
|
|
|
response = requests.post(url, headers=headers, data=data) |
|
|
|
def reformat_nation_data(xml_data): |
|
""" |
|
Parses and reformats NationStates XML data into a structured dictionary. |
|
|
|
Args: |
|
xml_data (str): Raw XML data. |
|
|
|
Returns: |
|
dict: A dictionary containing nation details, issue info, rankings, reclassifications, and headlines. |
|
""" |
|
soup = BeautifulSoup(xml_data, "xml") |
|
|
|
nation_elem = soup.find("NATION") |
|
nation_id = nation_elem.get("id") if nation_elem else "Unknown" |
|
|
|
issue_elem = nation_elem.find("ISSUE") if nation_elem else None |
|
issue_data = { |
|
"issue_id": issue_elem.get("id") if issue_elem else "Unknown", |
|
"choice": issue_elem.get("choice") if issue_elem else "Unknown", |
|
"ok": issue_elem.OK.get_text() if issue_elem and issue_elem.OK else "Unknown", |
|
"description": issue_elem.DESC.get_text() if issue_elem and issue_elem.DESC else "No description available", |
|
"rankings": [], |
|
"reclassifications": [], |
|
"headlines": [] |
|
} |
|
|
|
|
|
rankings_elem = issue_elem.find("RANKINGS") if issue_elem else None |
|
if rankings_elem: |
|
for rank in rankings_elem.find_all("RANK"): |
|
census_id = rank.get("census_id", "Unknown") |
|
census_id_key = int(census_id) if census_id.isdigit() else -1 |
|
assert census_id_key.isdigit(), f"Invalid census_id: {census_id}" |
|
issue_data["rankings"].append({ |
|
"census_id": rank.get("id", "Unknown"), |
|
"census": nation_states_metrics.get(census_id_key, "Unknown Metric"), |
|
"score": rank.SCORE.get_text() if rank.SCORE else "Unknown", |
|
"change": rank.CHANGE.get_text() if rank.CHANGE else "Unknown", |
|
"pchange": rank.PCHANGE.get_text() if rank.PCHANGE else "Unknown" |
|
}) |
|
|
|
|
|
reclassifications_elem = issue_elem.find("RECLASSIFICATIONS") if issue_elem else None |
|
if reclassifications_elem: |
|
for reclassify in reclassifications_elem.find_all("RECLASSIFY"): |
|
issue_data["reclassifications"].append({ |
|
"type": reclassify.get("type", "Unknown"), |
|
"from": reclassify.FROM.get_text() if reclassify.FROM else "Unknown", |
|
"to": reclassify.TO.get_text() if reclassify.TO else "Unknown" |
|
}) |
|
|
|
|
|
headlines_elem = issue_elem.find("HEADLINES") if issue_elem else None |
|
if headlines_elem: |
|
issue_data["headlines"] = [headline.get_text() for headline in headlines_elem.find_all("HEADLINE")] |
|
|
|
return {"nation_id": nation_id, "issue": issue_data} |
|
|
|
return reformat_nation_data(response.text) |
|
|
|
def get_id_to_name(rank_id): |
|
""" |
|
Converts an ID (rank_id) to a human-readable name based on the NationStates metrics. |
|
|
|
Args: |
|
rank_id (int): The ID to convert. |
|
|
|
Returns: |
|
str: The human-readable name corresponding to the rank_id. |
|
""" |
|
return nation_states_metrics.get(rank_id, "Unknown Metric") |
|
|
|
get_nation_issues_interface = gr.Interface( |
|
fn=get_nation_issues, |
|
inputs=[ |
|
gr.Textbox(label="Nation Name", placeholder="Enter the nation name"), |
|
gr.Textbox(label="API Password", placeholder="Enter your API password"), |
|
], |
|
outputs=gr.JSON(), |
|
title="Get Nation Issues as JSON", |
|
description="Fetch and reformat issues from the NationStates API", |
|
api_name="get_nation_issues_JSON", |
|
) |
|
address_nation_issues_interface = gr.Interface( |
|
fn=address_nation_issues, |
|
inputs=[ |
|
gr.Textbox(label="Nation Name", placeholder="Enter the nation name"), |
|
gr.Textbox(label="API Password", placeholder="Enter your API password"), |
|
gr.Textbox(label="Issue ID", placeholder="Enter the issue ID to address"), |
|
gr.Textbox(label="Option ID", placeholder="Enter the option ID to choose") |
|
], |
|
outputs=gr.JSON(), |
|
title="Address Nation Issues", |
|
description="Address a specific issue for a nation in NationStates API", |
|
api_name="address_nation_issues", |
|
) |
|
|
|
''' |
|
id_to_name_interface = gr.Interface( |
|
fn=get_id_to_name, |
|
inputs=gr.Number(label="rank_id", value=0, precision=0), |
|
outputs=gr.Textbox(label="Human-Readable Name"), |
|
title="ID to Name Converter", |
|
description="Convert an ID(rank_id) to a human-readable name based on NationStates metrics", |
|
api_name="get_id_to_name" |
|
) |
|
''' |
|
demo = gr.TabbedInterface( |
|
interface_list=[get_nation_issues_interface, address_nation_issues_interface], |
|
tab_names=["Get Nation Issues JSON", "Address Nation Issues"], |
|
title="NationStates API Tools", |
|
) |
|
|
|
if __name__ == "__main__": |
|
demo.launch(mcp_server=True) |