1- from fastapi import APIRouter , Depends , Request , Response
1+ from fastapi import APIRouter , Depends , HTTPException , Request , Response
22from typing import List
33import json
44
55from app .core .limiter import limiter
6- from app .core .settings import settings
6+ from app .core .config import settings
77from app .services .clustering import summarize_clusters
8+ from app .services .credits import CreditService
89from ..dependencies .auth import verify_token
910
1011from ....services .analyzer import centroid_analysis
1920async def rank_ideas (
2021 request : Request ,
2122 ideaRequest : IdeaRequest ,
22- token : str = Depends (verify_token ), # No effect for now; verification is disabled
23+ user_info : dict = Depends (verify_token ),
2324) -> AnalysisResponse :
24- print ('Ranking ideas' )
2525 """
2626 Analyze and rank ideas based on semantic similarity.
2727
@@ -31,96 +31,167 @@ async def rank_ideas(
3131 - Generate relationship graphs (optional)
3232 - Create pairwise similarity matrix (optional)
3333
34+ Credits required:
35+ - Basic analysis: 1 credit per 100 ideas
36+ - Relationship graph: 3 credits
37+ - Cluster names: 5 credits
38+
3439 Returns:
3540 AnalysisResponse containing ranked ideas and optional advanced analysis
3641
3742 Raises:
3843 HTTPException(400): If input data is invalid
44+ HTTPException(402): If insufficient credits
3945 HTTPException(429): If rate limit is exceeded
4046 """
41-
42- # Extract raw ideas for analysis
47+ print ( 'Ranking ideas' )
48+
4349 ideas = [item .idea for item in ideaRequest .ideas ]
44- id_mapping = {item .idea : {'id' : item .id or 1 , 'author_id' : item .author_id } for item in ideaRequest .ideas }
45- if len (ideas ) < 4 :
46- return Response (status_code = 400 , content = 'Please provide at least 4 items to analyze' )
50+ num_ideas = len (ideas )
51+ total_bytes = sum (len (item .idea .encode ('utf-8' )) for item in ideaRequest .ideas )
52+
53+ if num_ideas < 4 :
54+ return Response (status_code = 400 , content = 'Please provide at least 4 items to analyze' )
55+
56+ # Check credits for basic analysis
57+ user_id = user_info ["user_id" ]
58+
59+ operations = ["basic_analysis" ]
60+
61+ if ideaRequest .advanced_features :
62+ if ideaRequest .advanced_features .relationship_graph :
63+ operations .append ("relationship_graph" )
64+ if ideaRequest .advanced_features .cluster_names :
65+ operations .append ("cluster_names" )
66+
67+ if not await CreditService .has_sufficient_credits (
68+ user_id , operations , num_ideas , total_bytes
69+ ):
70+ raise HTTPException (
71+ status_code = 402 ,
72+ detail = f"Insufficient credits for analysis. Available credits: { await CreditService .get_credits (user_id )} "
73+ )
4774
4875 # Perform core analysis
4976 results , plot_data = centroid_analysis (ideas )
77+ await CreditService .deduct_credits (user_id , "basic_analysis" , num_ideas , total_bytes )
5078
51- # Create ranked ideas response
79+ response = await build_base_response (ideas , results , plot_data )
80+
81+ if ideaRequest .advanced_features :
82+ response = await process_advanced_features (
83+ ideaRequest , response , user_id , ideas , plot_data , num_ideas , total_bytes
84+ )
85+
86+ print ('Results calculated successfully!\n ' , response )
87+ return AnalysisResponse (** response )
88+
89+ def _generate_edges (ranked_ideas : List [RankedIdea ], similarity_matrix : List [List [float ]]) -> List [dict ]:
90+ """
91+ Generate graph edges showing relationships between ideas and to centroid.
92+
93+ Creates two types of edges:
94+ 1. Between ideas based on pairwise similarity
95+ 2. From each idea to the centroid based on similarity scores
96+ """
97+ edges = []
98+
99+ # Create edges between ideas
100+ for i , idea_from in enumerate (ranked_ideas ):
101+ if i + 1 > len (similarity_matrix ):
102+ break
103+ for j , idea_to in enumerate (ranked_ideas [i + 1 :], i + 1 ):
104+ edges .append ({
105+ "from_id" : idea_from .id ,
106+ "to_id" : idea_to .id ,
107+ "similarity" : similarity_matrix [i ][j ]
108+ })
109+
110+ # Create edges to centroid
111+ for idea in ranked_ideas :
112+ edges .append ({
113+ "from_id" : idea .id ,
114+ "to_id" : "Centroid" ,
115+ "similarity" : idea .similarity_score
116+ })
117+
118+ return edges
119+
120+ async def build_base_response (ideas : List [str ], results : Results , plot_data : PlotData ) -> dict :
121+ """Build base response with ranked ideas and similarity scores"""
52122 ranked_ideas = [
53123 RankedIdea (
54- id = id_mapping [idea ]['id' ],
55- author_id = id_mapping [idea ]['author_id' ],
124+ id = str (idx ),
56125 idea = idea ,
57- similarity_score = results ["similarity" ][index ],
58- cluster_id = plot_data ["kmeans_data" ]["cluster" ][index ],
126+ similarity_score = results ["similarity" ][idx ],
127+ cluster_id = plot_data ["kmeans_data" ]["cluster" ][idx ],
59128 )
60- for index , idea in enumerate (results ["ideas" ])
129+ for idx , idea in enumerate (results ["ideas" ])
61130 ]
62131
63- # Sort by similarity score
64132 ranked_ideas .sort (key = lambda x : x .similarity_score , reverse = True )
65133
66- # Build response with optional advanced features
67- response = {
134+ return {
68135 "ranked_ideas" : ranked_ideas ,
69136 "relationship_graph" : None ,
70137 "pairwise_similarity_matrix" : None ,
71138 "cluster_names" : None
72139 }
73-
74140
75- if ideaRequest .advanced_features :
76- if ideaRequest .advanced_features .relationship_graph :
77- coords = plot_data .get ("scatter_points" , [])
78- nodes = [
79- {
80- "id" : idea .id ,
81- "coordinates" : {
82- "x" : coords [i ][0 ],
83- "y" : coords [i ][1 ]
84- }
85- }
86- for i , idea in enumerate (ranked_ideas )]
87- # Add the centroid:
88- nodes .append ({"id" : "Centroid" , "coordinates" : {"x" : coords [- 1 ][0 ], "y" : coords [- 1 ][0 ]}})
89- response ["relationship_graph" ] = RelationshipGraph (
90- nodes = nodes ,
91- edges = _generate_edges (ranked_ideas , plot_data .get ("pairwise_similarity" , []))
92- )
93-
94- if ideaRequest .advanced_features .pairwise_similarity_matrix :
95- response ["pairwise_similarity_matrix" ] = plot_data .get ("pairwise_similarity" )
96-
97- if ideaRequest .advanced_features .cluster_names :
98- response ["cluster_names" ] = await summarize_clusters (ranked_ideas )
99- print ('Results calculated successfully!\n ' , response )
100- return AnalysisResponse (** response )
141+ async def process_advanced_features (
142+ request : IdeaRequest ,
143+ response : dict ,
144+ user_id : str ,
145+ ideas : List [str ],
146+ plot_data : PlotData ,
147+ num_ideas : int ,
148+ total_bytes : int
149+ ) -> dict :
150+ """Process and add advanced features if credits are available"""
151+ if request .advanced_features .relationship_graph :
152+ response ["relationship_graph" ] = build_relationship_graph (
153+ response ["ranked_ideas" ], plot_data
154+ )
155+ CreditService .deduct_credits (user_id , "relationship_graph" , num_ideas , total_bytes )
156+
157+ if request .advanced_features .cluster_names :
158+ response ["cluster_names" ] = await summarize_clusters (response ["ranked_ideas" ])
159+ CreditService .deduct_credits (user_id , "cluster_names" , num_ideas , total_bytes )
160+
161+ if request .advanced_features .pairwise_similarity_matrix :
162+ response ["pairwise_similarity_matrix" ] = plot_data .pairwise_similarity
163+
164+ return response
101165
102- def _generate_edges (ranked_ideas : List [RankedIdea ], similarity_matrix : List ) -> List [dict ]:
103- """Generate graph edges based on similarity scores"""
104- edges = []
166+ def build_relationship_graph (ranked_ideas : List [RankedIdea ], plot_data : PlotData ) -> RelationshipGraph :
167+ """
168+ Builds a graph representation of idea relationships including:
169+ - Nodes with coordinates from MDS analysis
170+ - Edges showing similarity between ideas
171+ - Centroid connections
172+ """
173+ coords = plot_data .scatter_points
105174
106- for i , idea_from in enumerate ( ranked_ideas ):
107- if i + 1 > len ( similarity_matrix ): break
108- for j , idea_to in enumerate ( ranked_ideas [ i + 1 :], i + 1 ):
109- edge = {
110- "from_id " : idea_from . id ,
111- "to_id " : idea_to . id ,
112- "similarity " : similarity_matrix [i ][j ]
113- }
114- edges . append ( edge )
115-
116- # Add edges to centroid
117- for i , idea in enumerate ( ranked_ideas ):
118- edge = {
119- "from_id " : idea . id ,
120- "to_id " : "Centroid" , # centroid id
121- "similarity " : idea . similarity_score
175+ # Create nodes including centroid
176+ nodes = [
177+ {
178+ "id" : idea . id ,
179+ "coordinates " : {
180+ "x " : coords [ i ][ 0 ] ,
181+ "y " : coords [i ][1 ]
182+ }
183+ }
184+ for i , idea in enumerate ( ranked_ideas )
185+ ]
186+ nodes . append ({
187+ "id" : "Centroid" ,
188+ "coordinates " : {
189+ "x " : coords [ - 1 ][ 0 ],
190+ "y " : coords [ - 1 ][ 1 ]
122191 }
123- edges . append ( edge )
192+ } )
124193
125-
126- return edges
194+ # Generate edges between ideas and to centroid
195+ edges = _generate_edges (ranked_ideas , plot_data .pairwise_similarity )
196+
197+ return RelationshipGraph (nodes = nodes , edges = edges )
0 commit comments