11from pydantic import BaseModel , ValidationError
2- from typing import Optional , Union , List , TypedDict
3- from flask import Flask , jsonify , request , abort
2+ from typing import Optional , Union , List , TypedDict , cast
3+ from flask import Flask , jsonify , request , abort , Response
44from reddwarf .data_loader import Loader
55from reddwarf .implementations .polis import run_pipeline
66import logging
77import sys
88import traceback
99from datetime import datetime
1010from logging .handlers import RotatingFileHandler
11+ import pandas as pd
1112
1213from pprint import pprint
1314
@@ -54,7 +55,7 @@ def print_summary(loader: Loader):
5455
5556
5657@app .route ("/import" )
57- def importConversation ():
58+ def importConversation () -> Response :
5859 report_id = request .args .get ("report_id" )
5960 conversation_id = request .args .get ("conversation_id" )
6061 if report_id is None and conversation_id is None :
@@ -77,7 +78,7 @@ def importConversation():
7778 "votes_data" : loader .votes_data ,
7879 }
7980 )
80- if conversation_id is not None :
81+ else : # conversation_id is not None
8182 logger .info (
8283 f"Loading Polis conversation from conversation_id={ conversation_id } "
8384 )
@@ -116,19 +117,63 @@ class MathRequest(BaseModel):
116117 votes : List [VoteRecord ]
117118
118119
120+ class ConsensusStatement (TypedDict ):
121+ tid : int
122+ n_success : int
123+ n_trials : int
124+ p_success : float
125+ p_test : float
126+ cons_for : str
127+
128+
119129class Consensus (TypedDict ):
120- agree : List
121- disagree : List
130+ agree : List [ConsensusStatement ]
131+ disagree : List [ConsensusStatement ]
132+
133+
134+ class RepnessStatement (TypedDict ):
135+ tid : int
136+ n_success : int
137+ n_trials : int
138+ p_success : float
139+ p_test : float
140+ repness : float
141+ repness_test : float
142+ repful_for : str # Literal["agree", "disagree"] but simplified for compatibility
143+ best_agree : Union [bool , None ] # NotRequired in TypedDict
144+ n_agree : Union [int , None ] # NotRequired in TypedDict
145+
146+
147+ # PolisRepness is dict[str | GroupId, list[PolisRepnessStatement]]
148+ # GroupId is an int, so the key can be str or int
149+ Repness = dict [Union [str , int ], List [RepnessStatement ]]
122150
123151
124152class MathResult (TypedDict ):
125- statements_df : List
126- participants_df : List
127- repness : dict
153+ statements_df : List [ dict ]
154+ participants_df : List [ dict ]
155+ repness : Repness
128156 group_comment_stats : dict
129157 consensus : Consensus
130158
131159
160+ def convert_to_json_serializable (obj ):
161+ """
162+ Recursively converts pandas Series, DataFrames, and other non-JSON-serializable
163+ types to JSON-serializable formats.
164+ """
165+ if isinstance (obj , pd .Series ):
166+ return obj .to_dict ()
167+ elif isinstance (obj , pd .DataFrame ):
168+ return obj .to_dict (orient = "records" )
169+ elif isinstance (obj , dict ):
170+ return {key : convert_to_json_serializable (value ) for key , value in obj .items ()}
171+ elif isinstance (obj , (list , tuple )):
172+ return [convert_to_json_serializable (item ) for item in obj ]
173+ else :
174+ return obj
175+
176+
132177def calculate_distribution_imbalance (member_counts ):
133178 """
134179 Calculate how imbalanced the distribution is across groups.
@@ -373,9 +418,9 @@ def get_maths(
373418 "participants_df" : result .participants_df .reset_index ().to_dict (
374419 orient = "records"
375420 ),
376- "repness" : result .repness ,
421+ "repness" : cast ( Repness , convert_to_json_serializable ( result .repness )) ,
377422 "group_comment_stats" : group_comment_stats ,
378- "consensus" : result .consensus ,
423+ "consensus" : cast ( Consensus , convert_to_json_serializable ( result .consensus )) ,
379424 }
380425
381426
0 commit comments