Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -7,10 +7,6 @@ from datetime import datetime, timedelta
|
|
7 |
from typing import Dict, Any, List, Optional
|
8 |
from pydantic import BaseModel, Field
|
9 |
import gradio as gr
|
10 |
-
from crewai import Agent, Crew, Process, Task, LLM
|
11 |
-
from crewai.agent import agent
|
12 |
-
from crewai.task import task
|
13 |
-
from crewai.crew import crew, CrewBase
|
14 |
|
15 |
# Setup logging
|
16 |
logging.basicConfig(
|
@@ -19,12 +15,6 @@ logging.basicConfig(
|
|
19 |
)
|
20 |
logger = logging.getLogger(__name__)
|
21 |
|
22 |
-
# Initialize LLM (will use environment variable)
|
23 |
-
llm = LLM(
|
24 |
-
model="gemini/gemini-2.0-flash-exp", # or any other model
|
25 |
-
api_key=os.getenv("GEMINI_API_KEY", "dummy-key-for-testing"),
|
26 |
-
)
|
27 |
-
|
28 |
# ========== DATA MODELS ==========
|
29 |
class UserVerification(BaseModel):
|
30 |
user_id: str = Field(..., description="User ID")
|
@@ -87,181 +77,174 @@ class MockDatabase:
|
|
87 |
# Initialize mock database
|
88 |
mock_db = MockDatabase()
|
89 |
|
90 |
-
# ==========
|
91 |
-
class
|
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 |
-
role="Dynamic Pricing Strategist",
|
124 |
-
goal="Calculate fair resale prices based on demand while preventing price gouging",
|
125 |
-
backstory="""You are an expert in market dynamics and pricing strategies. You analyze
|
126 |
-
demand levels, market conditions, and event popularity to recommend fair resale prices.
|
127 |
-
You balance market forces with consumer protection, ensuring prices reflect true demand
|
128 |
-
without enabling exploitative pricing.""",
|
129 |
-
llm=llm,
|
130 |
-
verbose=True
|
131 |
-
)
|
132 |
-
|
133 |
-
@agent
|
134 |
-
def resale_monitor_agent(self) -> Agent:
|
135 |
-
return Agent(
|
136 |
-
role="Resale Compliance Officer",
|
137 |
-
goal="Monitor and enforce resale policies to ensure fair ticket distribution",
|
138 |
-
backstory="""You are responsible for ensuring all ticket resales comply with platform
|
139 |
-
policies and local regulations. You monitor resale prices, frequency, and patterns to
|
140 |
-
prevent policy violations. Your work ensures the secondary ticket market remains fair
|
141 |
-
and accessible to genuine fans.""",
|
142 |
-
llm=llm,
|
143 |
-
verbose=True
|
144 |
-
)
|
145 |
-
|
146 |
-
@task
|
147 |
-
def verify_user_task(self) -> Task:
|
148 |
-
return Task(
|
149 |
-
description="""Verify the user with the following details:
|
150 |
-
- Name: {name}
|
151 |
-
- Email: {email}
|
152 |
-
- User ID: {user_id}
|
153 |
-
|
154 |
-
Perform KYC verification checks:
|
155 |
-
1. Validate email format and domain
|
156 |
-
2. Check if name appears legitimate
|
157 |
-
3. Assign verification level (basic/standard/premium)
|
158 |
-
4. Calculate risk score (0-1) based on user patterns
|
159 |
-
5. Determine if user is verified for ticket purchase
|
160 |
-
|
161 |
-
Consider factors like email domain reputation, name consistency, and any red flags.""",
|
162 |
-
agent=self.user_verification_agent(),
|
163 |
-
expected_output="UserVerification model with complete verification details"
|
164 |
-
)
|
165 |
-
|
166 |
-
@task
|
167 |
-
def detect_scalping_task(self) -> Task:
|
168 |
-
return Task(
|
169 |
-
description="""Analyze user behavior for scalping indicators:
|
170 |
-
- User ID: {user_id}
|
171 |
-
- Current purchase attempt for event: {event_name}
|
172 |
-
- Ticket quantity requested: {ticket_quantity}
|
173 |
-
|
174 |
-
Check for scalping patterns:
|
175 |
-
1. Purchase velocity (multiple purchases in short time)
|
176 |
-
2. IP address duplication (multiple accounts from same IP)
|
177 |
-
3. Unusual buying patterns
|
178 |
-
4. Bot-like behavior indicators
|
179 |
-
5. Historical resale frequency
|
180 |
-
|
181 |
-
Use the user's purchase history and behavioral data to determine scalping likelihood.""",
|
182 |
-
agent=self.scalping_detection_agent(),
|
183 |
-
expected_output="ScalpingDetection model with detection results and confidence score"
|
184 |
)
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
203 |
)
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
Check against policies:
|
215 |
-
1. Maximum 2x original price cap
|
216 |
-
2. No more than 4 resales per month
|
217 |
-
3. No bulk resales (>4 tickets at once)
|
218 |
-
4. Cooling period between purchases and resales
|
219 |
-
|
220 |
-
Determine if resale should be allowed and provide recommendations.""",
|
221 |
-
agent=self.resale_monitor_agent(),
|
222 |
-
context=[self.detect_scalping_task(), self.calculate_pricing_task()],
|
223 |
-
expected_output="ResaleCompliance model with compliance decision and violations"
|
224 |
)
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
)
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
259 |
)
|
260 |
|
261 |
# ========== MAIN APPLICATION ==========
|
262 |
class AntiScalpingSystem:
|
263 |
def __init__(self):
|
264 |
-
self.
|
|
|
|
|
|
|
265 |
|
266 |
def process_ticket_transaction(
|
267 |
self,
|
@@ -277,76 +260,67 @@ class AntiScalpingSystem:
|
|
277 |
) -> Dict[str, Any]:
|
278 |
"""Process a ticket transaction through the anti-scalping system"""
|
279 |
|
280 |
-
# Simulate some purchase history for the user
|
281 |
-
purchase_count = mock_db.get_user_purchases(user_id)
|
282 |
-
mock_db.user_purchase_history[user_id] = purchase_count + 1
|
283 |
-
|
284 |
-
inputs = {
|
285 |
-
"name": name,
|
286 |
-
"email": email,
|
287 |
-
"user_id": user_id,
|
288 |
-
"event_name": event_name,
|
289 |
-
"ticket_type": ticket_type,
|
290 |
-
"ticket_quantity": ticket_quantity,
|
291 |
-
"original_price": original_price,
|
292 |
-
"demand_level": demand_level,
|
293 |
-
"proposed_resale_price": proposed_resale_price
|
294 |
-
}
|
295 |
-
|
296 |
try:
|
297 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
|
299 |
-
#
|
300 |
-
|
301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
else:
|
303 |
-
|
304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
305 |
except Exception as e:
|
306 |
logger.error(f"Error processing transaction: {e}")
|
307 |
-
return self.
|
308 |
|
309 |
-
def
|
310 |
-
"""Create
|
311 |
-
is_scalper = random.random() > 0.7
|
312 |
-
price_ratio = inputs["proposed_resale_price"] / inputs["original_price"]
|
313 |
-
|
314 |
return {
|
315 |
-
"
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
"is_verified": not is_scalper,
|
320 |
-
"verification_level": "standard" if not is_scalper else "basic",
|
321 |
-
"risk_score": 0.3 if not is_scalper else 0.8
|
322 |
-
},
|
323 |
-
"scalping_detection": {
|
324 |
-
"is_scalper": is_scalper,
|
325 |
-
"confidence": 0.85 if is_scalper else 0.15,
|
326 |
-
"flags": ["rapid_purchases", "multiple_ips"] if is_scalper else [],
|
327 |
-
"purchase_velocity": random.randint(1, 10),
|
328 |
-
"ip_duplicates": random.randint(1, 5)
|
329 |
-
},
|
330 |
-
"pricing": {
|
331 |
-
"original_price": inputs["original_price"],
|
332 |
-
"recommended_resale_price": inputs["proposed_resale_price"],
|
333 |
-
"demand_level": inputs["demand_level"],
|
334 |
-
"price_adjustment_reason": f"Based on {inputs['demand_level']} demand",
|
335 |
-
"profit_margin": (price_ratio - 1) * 100 if price_ratio > 1 else None,
|
336 |
-
"loss_percentage": (1 - price_ratio) * 100 if price_ratio < 1 else None
|
337 |
-
},
|
338 |
-
"compliance": {
|
339 |
-
"is_compliant": price_ratio <= 2 and not is_scalper,
|
340 |
-
"violations": ["price_exceeds_2x"] if price_ratio > 2 else [],
|
341 |
-
"resale_allowed": price_ratio <= 2 and not is_scalper,
|
342 |
-
"max_allowed_price": inputs["original_price"] * 2,
|
343 |
-
"recommendation": "Proceed with resale" if price_ratio <= 2 and not is_scalper else "Block transaction"
|
344 |
-
},
|
345 |
-
"final_decision": "APPROVED" if price_ratio <= 2 and not is_scalper else "DENIED",
|
346 |
-
"action_items": [
|
347 |
-
"Process ticket resale" if price_ratio <= 2 and not is_scalper else "Block transaction",
|
348 |
-
"Monitor future activity" if is_scalper else "No additional action required"
|
349 |
-
]
|
350 |
}
|
351 |
|
352 |
# ========== GRADIO INTERFACE ==========
|
@@ -361,14 +335,18 @@ def create_interface():
|
|
361 |
|
362 |
# Validate inputs
|
363 |
if not all([name, email, user_id, event_name]):
|
364 |
-
return "Please fill in all required fields"
|
365 |
|
366 |
try:
|
367 |
-
original_price = float(original_price)
|
368 |
-
proposed_resale_price = float(proposed_resale_price)
|
369 |
-
ticket_quantity = int(ticket_quantity)
|
370 |
-
|
371 |
-
|
|
|
|
|
|
|
|
|
372 |
|
373 |
# Process through the system
|
374 |
result = system.process_ticket_transaction(
|
@@ -383,95 +361,159 @@ def create_interface():
|
|
383 |
proposed_resale_price=proposed_resale_price
|
384 |
)
|
385 |
|
|
|
|
|
|
|
|
|
386 |
# Format the output
|
|
|
|
|
387 |
output = f"""
|
388 |
-
# π« Anti-Scalping System Report
|
|
|
|
|
|
|
|
|
389 |
|
390 |
## π€ User Verification
|
391 |
-
- **User ID**: {result['verification']['user_id']}
|
|
|
|
|
392 |
- **Verified**: {'β
Yes' if result['verification']['is_verified'] else 'β No'}
|
393 |
-
- **Risk Score**: {result['verification']['risk_score']:.
|
394 |
-
- **Verification Level**: {result['verification']['verification_level']}
|
395 |
|
396 |
-
## π Scalping Detection
|
397 |
-
- **Scalper Detected**: {'π¨
|
398 |
-
- **Confidence**: {result['scalping_detection']['confidence']:.
|
399 |
- **Purchase Velocity**: {result['scalping_detection']['purchase_velocity']} purchases/hour
|
400 |
-
- **IP Duplicates**: {result['scalping_detection']['ip_duplicates']}
|
401 |
-
- **Flags**: {', '.join(result['scalping_detection']['flags']) if result['scalping_detection']['flags'] else 'None'}
|
402 |
|
403 |
-
## π° Pricing Analysis
|
404 |
- **Original Price**: ${result['pricing']['original_price']:.2f}
|
405 |
-
- **Proposed Resale**: ${
|
406 |
-
- **
|
|
|
407 |
- **Price Ratio**: {result['pricing']['recommended_resale_price']/result['pricing']['original_price']:.2f}x
|
408 |
"""
|
409 |
|
410 |
if result['pricing'].get('profit_margin'):
|
411 |
-
output += f"- **Profit Margin**: {result['pricing']['profit_margin']:.1f}
|
412 |
elif result['pricing'].get('loss_percentage'):
|
413 |
-
output += f"- **Loss**: {result['pricing']['loss_percentage']:.1f}
|
414 |
-
|
415 |
-
output += f"""
|
416 |
-
## β
Compliance Check
|
417 |
-
- **Compliant**: {'β
Yes' if result['compliance']['is_compliant'] else 'β No'}
|
418 |
-
- **Resale Allowed**: {'β
Yes' if result['compliance']['resale_allowed'] else 'β No'}
|
419 |
-
- **Max Allowed Price**: ${result['compliance']['max_allowed_price']:.2f}
|
420 |
-
- **Violations**: {', '.join(result['compliance']['violations']) if result['compliance']['violations'] else 'None'}
|
421 |
|
422 |
-
##
|
|
|
|
|
|
|
|
|
|
|
423 |
|
424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
425 |
"""
|
426 |
-
for action in result['action_items']:
|
427 |
-
output += f"- {action}\n"
|
428 |
|
429 |
return output
|
430 |
|
431 |
-
# Create Gradio interface
|
432 |
-
|
433 |
-
fn=process_transaction,
|
434 |
-
inputs=[
|
435 |
-
gr.Textbox(label="Full Name", placeholder="John Doe"),
|
436 |
-
gr.Textbox(label="Email", placeholder="john@example.com"),
|
437 |
-
gr.Textbox(label="User ID", placeholder="USER123"),
|
438 |
-
gr.Textbox(label="Event Name", placeholder="Taylor Swift - Eras Tour"),
|
439 |
-
gr.Dropdown(
|
440 |
-
label="Ticket Type",
|
441 |
-
choices=["General Admission", "VIP", "Premium", "Standard"],
|
442 |
-
value="Standard"
|
443 |
-
),
|
444 |
-
gr.Number(label="Ticket Quantity", value=1, minimum=1, maximum=10),
|
445 |
-
gr.Number(label="Original Ticket Price ($)", value=100),
|
446 |
-
gr.Radio(
|
447 |
-
label="Current Demand Level",
|
448 |
-
choices=["low", "medium", "high"],
|
449 |
-
value="medium"
|
450 |
-
),
|
451 |
-
gr.Number(label="Proposed Resale Price ($)", value=150)
|
452 |
-
],
|
453 |
-
outputs=gr.Markdown(),
|
454 |
title="π« Anti-Scalping Ticketing System",
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
471 |
|
472 |
return interface
|
473 |
|
474 |
# Main execution
|
475 |
if __name__ == "__main__":
|
476 |
interface = create_interface()
|
477 |
-
interface.launch(
|
|
|
|
|
|
|
|
|
|
|
|
7 |
from typing import Dict, Any, List, Optional
|
8 |
from pydantic import BaseModel, Field
|
9 |
import gradio as gr
|
|
|
|
|
|
|
|
|
10 |
|
11 |
# Setup logging
|
12 |
logging.basicConfig(
|
|
|
15 |
)
|
16 |
logger = logging.getLogger(__name__)
|
17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
# ========== DATA MODELS ==========
|
19 |
class UserVerification(BaseModel):
|
20 |
user_id: str = Field(..., description="User ID")
|
|
|
77 |
# Initialize mock database
|
78 |
mock_db = MockDatabase()
|
79 |
|
80 |
+
# ========== VERIFICATION LOGIC ==========
|
81 |
+
class UserVerificationEngine:
|
82 |
+
@staticmethod
|
83 |
+
def verify_user(name: str, email: str, user_id: str) -> UserVerification:
|
84 |
+
"""Verify user based on provided information"""
|
85 |
+
|
86 |
+
# Email validation
|
87 |
+
email_valid = "@" in email and "." in email.split("@")[-1]
|
88 |
+
|
89 |
+
# Name validation (basic check)
|
90 |
+
name_valid = len(name.strip()) >= 2 and not any(char.isdigit() for char in name)
|
91 |
+
|
92 |
+
# Risk assessment
|
93 |
+
risk_factors = []
|
94 |
+
if not email_valid:
|
95 |
+
risk_factors.append("invalid_email")
|
96 |
+
if not name_valid:
|
97 |
+
risk_factors.append("suspicious_name")
|
98 |
+
if email.endswith(('.temp', '.fake', '.test')):
|
99 |
+
risk_factors.append("temporary_email")
|
100 |
+
|
101 |
+
risk_score = min(0.9, len(risk_factors) * 0.3 + random.uniform(0.1, 0.3))
|
102 |
+
is_verified = risk_score < 0.5 and email_valid and name_valid
|
103 |
+
|
104 |
+
verification_level = "premium" if risk_score < 0.2 else "standard" if risk_score < 0.5 else "basic"
|
105 |
+
|
106 |
+
return UserVerification(
|
107 |
+
user_id=user_id,
|
108 |
+
name=name,
|
109 |
+
email=email,
|
110 |
+
is_verified=is_verified,
|
111 |
+
verification_level=verification_level,
|
112 |
+
risk_score=risk_score
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
)
|
114 |
+
|
115 |
+
class ScalpingDetectionEngine:
|
116 |
+
@staticmethod
|
117 |
+
def detect_scalping(user_id: str, ticket_quantity: int, event_name: str) -> ScalpingDetection:
|
118 |
+
"""Detect potential scalping behavior"""
|
119 |
+
|
120 |
+
flags = []
|
121 |
+
purchase_velocity = mock_db.get_user_purchases(user_id)
|
122 |
+
ip_duplicates = mock_db.get_ip_accounts(user_id)
|
123 |
+
resale_frequency = mock_db.get_resale_frequency(user_id)
|
124 |
+
|
125 |
+
# Check for scalping indicators
|
126 |
+
if purchase_velocity > 3:
|
127 |
+
flags.append("rapid_purchases")
|
128 |
+
if ip_duplicates > 2:
|
129 |
+
flags.append("multiple_ips")
|
130 |
+
if ticket_quantity > 4:
|
131 |
+
flags.append("bulk_purchase")
|
132 |
+
if resale_frequency > 5:
|
133 |
+
flags.append("frequent_reseller")
|
134 |
+
|
135 |
+
# Calculate scalping probability
|
136 |
+
scalping_score = (
|
137 |
+
(purchase_velocity / 10) * 0.3 +
|
138 |
+
(ip_duplicates / 5) * 0.3 +
|
139 |
+
(ticket_quantity / 10) * 0.2 +
|
140 |
+
(resale_frequency / 20) * 0.2
|
141 |
)
|
142 |
+
|
143 |
+
is_scalper = scalping_score > 0.5
|
144 |
+
confidence = min(0.95, scalping_score + random.uniform(0.1, 0.2))
|
145 |
+
|
146 |
+
return ScalpingDetection(
|
147 |
+
is_scalper=is_scalper,
|
148 |
+
confidence=confidence,
|
149 |
+
flags=flags,
|
150 |
+
purchase_velocity=purchase_velocity,
|
151 |
+
ip_duplicates=ip_duplicates
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
)
|
153 |
+
|
154 |
+
class DynamicPricingEngine:
|
155 |
+
@staticmethod
|
156 |
+
def calculate_pricing(original_price: float, demand_level: str, proposed_price: float) -> PricingRecommendation:
|
157 |
+
"""Calculate recommended pricing based on demand"""
|
158 |
+
|
159 |
+
demand_multipliers = {
|
160 |
+
"low": (0.8, 1.1), # Allow 20% below to 10% above
|
161 |
+
"medium": (0.9, 1.25), # Allow 10% below to 25% above
|
162 |
+
"high": (1.0, 1.5) # Allow original price to 50% above
|
163 |
+
}
|
164 |
+
|
165 |
+
min_mult, max_mult = demand_multipliers.get(demand_level, (0.9, 1.25))
|
166 |
+
|
167 |
+
min_price = original_price * min_mult
|
168 |
+
max_price = original_price * max_mult
|
169 |
+
|
170 |
+
# Recommend price within acceptable range
|
171 |
+
recommended_price = max(min_price, min(proposed_price, max_price))
|
172 |
+
|
173 |
+
price_ratio = recommended_price / original_price
|
174 |
+
|
175 |
+
profit_margin = None
|
176 |
+
loss_percentage = None
|
177 |
+
|
178 |
+
if price_ratio > 1.0:
|
179 |
+
profit_margin = (price_ratio - 1) * 100
|
180 |
+
elif price_ratio < 1.0:
|
181 |
+
loss_percentage = (1 - price_ratio) * 100
|
182 |
+
|
183 |
+
reason = f"Adjusted for {demand_level} demand market conditions"
|
184 |
+
if recommended_price != proposed_price:
|
185 |
+
reason += f" (modified from ${proposed_price:.2f})"
|
186 |
+
|
187 |
+
return PricingRecommendation(
|
188 |
+
original_price=original_price,
|
189 |
+
recommended_resale_price=recommended_price,
|
190 |
+
demand_level=demand_level,
|
191 |
+
price_adjustment_reason=reason,
|
192 |
+
profit_margin=profit_margin,
|
193 |
+
loss_percentage=loss_percentage
|
194 |
)
|
195 |
+
|
196 |
+
class ComplianceEngine:
|
197 |
+
@staticmethod
|
198 |
+
def check_compliance(
|
199 |
+
user_id: str,
|
200 |
+
proposed_price: float,
|
201 |
+
original_price: float,
|
202 |
+
scalping_detection: ScalpingDetection
|
203 |
+
) -> ResaleCompliance:
|
204 |
+
"""Check resale compliance against policies"""
|
205 |
+
|
206 |
+
violations = []
|
207 |
+
price_ratio = proposed_price / original_price
|
208 |
+
|
209 |
+
# Policy checks
|
210 |
+
if price_ratio > 2.0:
|
211 |
+
violations.append("price_exceeds_2x")
|
212 |
+
|
213 |
+
if scalping_detection.is_scalper:
|
214 |
+
violations.append("suspected_scalper")
|
215 |
+
|
216 |
+
if scalping_detection.purchase_velocity > 5:
|
217 |
+
violations.append("excessive_purchase_velocity")
|
218 |
+
|
219 |
+
resale_frequency = mock_db.get_resale_frequency(user_id)
|
220 |
+
if resale_frequency > 4:
|
221 |
+
violations.append("monthly_resale_limit_exceeded")
|
222 |
+
|
223 |
+
is_compliant = len(violations) == 0
|
224 |
+
resale_allowed = is_compliant and not scalping_detection.is_scalper
|
225 |
+
|
226 |
+
max_allowed_price = original_price * 2.0
|
227 |
+
|
228 |
+
if resale_allowed:
|
229 |
+
recommendation = "Transaction approved - complies with all policies"
|
230 |
+
else:
|
231 |
+
recommendation = f"Transaction blocked due to: {', '.join(violations)}"
|
232 |
+
|
233 |
+
return ResaleCompliance(
|
234 |
+
is_compliant=is_compliant,
|
235 |
+
violations=violations,
|
236 |
+
resale_allowed=resale_allowed,
|
237 |
+
max_allowed_price=max_allowed_price,
|
238 |
+
recommendation=recommendation
|
239 |
)
|
240 |
|
241 |
# ========== MAIN APPLICATION ==========
|
242 |
class AntiScalpingSystem:
|
243 |
def __init__(self):
|
244 |
+
self.verification_engine = UserVerificationEngine()
|
245 |
+
self.scalping_engine = ScalpingDetectionEngine()
|
246 |
+
self.pricing_engine = DynamicPricingEngine()
|
247 |
+
self.compliance_engine = ComplianceEngine()
|
248 |
|
249 |
def process_ticket_transaction(
|
250 |
self,
|
|
|
260 |
) -> Dict[str, Any]:
|
261 |
"""Process a ticket transaction through the anti-scalping system"""
|
262 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
try:
|
264 |
+
# Step 1: User Verification
|
265 |
+
verification = self.verification_engine.verify_user(name, email, user_id)
|
266 |
+
|
267 |
+
# Step 2: Scalping Detection
|
268 |
+
scalping_detection = self.scalping_engine.detect_scalping(
|
269 |
+
user_id, ticket_quantity, event_name
|
270 |
+
)
|
271 |
+
|
272 |
+
# Step 3: Dynamic Pricing
|
273 |
+
pricing = self.pricing_engine.calculate_pricing(
|
274 |
+
original_price, demand_level, proposed_resale_price
|
275 |
+
)
|
276 |
|
277 |
+
# Step 4: Compliance Check
|
278 |
+
compliance = self.compliance_engine.check_compliance(
|
279 |
+
user_id, proposed_resale_price, original_price, scalping_detection
|
280 |
+
)
|
281 |
+
|
282 |
+
# Step 5: Final Decision
|
283 |
+
final_decision = "APPROVED" if (
|
284 |
+
verification.is_verified and
|
285 |
+
compliance.resale_allowed and
|
286 |
+
not scalping_detection.is_scalper
|
287 |
+
) else "DENIED"
|
288 |
+
|
289 |
+
# Action items
|
290 |
+
action_items = []
|
291 |
+
if final_decision == "APPROVED":
|
292 |
+
action_items.append("β
Process ticket resale")
|
293 |
+
action_items.append("π Monitor user activity")
|
294 |
else:
|
295 |
+
action_items.append("β Block transaction")
|
296 |
+
if scalping_detection.is_scalper:
|
297 |
+
action_items.append("π¨ Flag user account for review")
|
298 |
+
if not verification.is_verified:
|
299 |
+
action_items.append("π Require additional verification")
|
300 |
+
|
301 |
+
# Create report
|
302 |
+
report = TicketingSystemReport(
|
303 |
+
verification=verification,
|
304 |
+
scalping_detection=scalping_detection,
|
305 |
+
pricing=pricing,
|
306 |
+
compliance=compliance,
|
307 |
+
final_decision=final_decision,
|
308 |
+
action_items=action_items
|
309 |
+
)
|
310 |
+
|
311 |
+
return report.dict()
|
312 |
+
|
313 |
except Exception as e:
|
314 |
logger.error(f"Error processing transaction: {e}")
|
315 |
+
return self._create_error_response(str(e))
|
316 |
|
317 |
+
def _create_error_response(self, error_msg: str) -> Dict[str, Any]:
|
318 |
+
"""Create error response"""
|
|
|
|
|
|
|
319 |
return {
|
320 |
+
"error": True,
|
321 |
+
"message": f"System error: {error_msg}",
|
322 |
+
"final_decision": "ERROR",
|
323 |
+
"action_items": ["π§ Contact system administrator"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
324 |
}
|
325 |
|
326 |
# ========== GRADIO INTERFACE ==========
|
|
|
335 |
|
336 |
# Validate inputs
|
337 |
if not all([name, email, user_id, event_name]):
|
338 |
+
return "β **Error**: Please fill in all required fields"
|
339 |
|
340 |
try:
|
341 |
+
original_price = float(original_price) if original_price else 0
|
342 |
+
proposed_resale_price = float(proposed_resale_price) if proposed_resale_price else 0
|
343 |
+
ticket_quantity = int(ticket_quantity) if ticket_quantity else 1
|
344 |
+
|
345 |
+
if original_price <= 0 or proposed_resale_price <= 0:
|
346 |
+
return "β **Error**: Prices must be greater than 0"
|
347 |
+
|
348 |
+
except (ValueError, TypeError):
|
349 |
+
return "β **Error**: Please enter valid numbers for prices and quantity"
|
350 |
|
351 |
# Process through the system
|
352 |
result = system.process_ticket_transaction(
|
|
|
361 |
proposed_resale_price=proposed_resale_price
|
362 |
)
|
363 |
|
364 |
+
# Handle errors
|
365 |
+
if result.get("error"):
|
366 |
+
return f"β **System Error**: {result.get('message', 'Unknown error occurred')}"
|
367 |
+
|
368 |
# Format the output
|
369 |
+
decision_emoji = "β
" if result['final_decision'] == "APPROVED" else "β"
|
370 |
+
|
371 |
output = f"""
|
372 |
+
# π« Anti-Scalping System Analysis Report
|
373 |
+
|
374 |
+
## {decision_emoji} Final Decision: **{result['final_decision']}**
|
375 |
+
|
376 |
+
---
|
377 |
|
378 |
## π€ User Verification
|
379 |
+
- **User ID**: `{result['verification']['user_id']}`
|
380 |
+
- **Name**: {result['verification']['name']}
|
381 |
+
- **Email**: {result['verification']['email']}
|
382 |
- **Verified**: {'β
Yes' if result['verification']['is_verified'] else 'β No'}
|
383 |
+
- **Risk Score**: {result['verification']['risk_score']:.1%} {'π’' if result['verification']['risk_score'] < 0.3 else 'π‘' if result['verification']['risk_score'] < 0.6 else 'π΄'}
|
384 |
+
- **Verification Level**: {result['verification']['verification_level'].title()}
|
385 |
|
386 |
+
## π Scalping Detection Analysis
|
387 |
+
- **Scalper Detected**: {'π¨ YES' if result['scalping_detection']['is_scalper'] else 'β
NO'}
|
388 |
+
- **Detection Confidence**: {result['scalping_detection']['confidence']:.1%}
|
389 |
- **Purchase Velocity**: {result['scalping_detection']['purchase_velocity']} purchases/hour
|
390 |
+
- **IP Address Duplicates**: {result['scalping_detection']['ip_duplicates']} accounts
|
391 |
+
- **Red Flags**: {', '.join(result['scalping_detection']['flags']) if result['scalping_detection']['flags'] else 'β
None detected'}
|
392 |
|
393 |
+
## π° Dynamic Pricing Analysis
|
394 |
- **Original Price**: ${result['pricing']['original_price']:.2f}
|
395 |
+
- **Proposed Resale**: ${proposed_resale_price:.2f}
|
396 |
+
- **Recommended Price**: ${result['pricing']['recommended_resale_price']:.2f}
|
397 |
+
- **Demand Level**: {result['pricing']['demand_level'].title()} π
|
398 |
- **Price Ratio**: {result['pricing']['recommended_resale_price']/result['pricing']['original_price']:.2f}x
|
399 |
"""
|
400 |
|
401 |
if result['pricing'].get('profit_margin'):
|
402 |
+
output += f"- **Profit Margin**: {result['pricing']['profit_margin']:.1f}% π\n"
|
403 |
elif result['pricing'].get('loss_percentage'):
|
404 |
+
output += f"- **Loss**: -{result['pricing']['loss_percentage']:.1f}% π\n"
|
405 |
+
|
406 |
+
output += f"""- **Reason**: {result['pricing']['price_adjustment_reason']}
|
|
|
|
|
|
|
|
|
|
|
407 |
|
408 |
+
## β
Compliance Evaluation
|
409 |
+
- **Policy Compliant**: {'β
Yes' if result['compliance']['is_compliant'] else 'β No'}
|
410 |
+
- **Resale Permitted**: {'β
Yes' if result['compliance']['resale_allowed'] else 'β No'}
|
411 |
+
- **Maximum Allowed**: ${result['compliance']['max_allowed_price']:.2f}
|
412 |
+
- **Policy Violations**: {', '.join(result['compliance']['violations']) if result['compliance']['violations'] else 'β
None'}
|
413 |
+
- **Recommendation**: {result['compliance']['recommendation']}
|
414 |
|
415 |
+
---
|
416 |
+
|
417 |
+
## π Required Actions
|
418 |
+
"""
|
419 |
+
for i, action in enumerate(result['action_items'], 1):
|
420 |
+
output += f"{i}. {action}\n"
|
421 |
+
|
422 |
+
# Add summary box
|
423 |
+
if result['final_decision'] == "APPROVED":
|
424 |
+
output += """
|
425 |
+
> β
**TRANSACTION APPROVED** - All checks passed. Resale can proceed as recommended.
|
426 |
+
"""
|
427 |
+
else:
|
428 |
+
output += """
|
429 |
+
> β **TRANSACTION BLOCKED** - Policy violations detected. Review required before proceeding.
|
430 |
"""
|
|
|
|
|
431 |
|
432 |
return output
|
433 |
|
434 |
+
# Create Gradio interface with better styling
|
435 |
+
with gr.Blocks(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
436 |
title="π« Anti-Scalping Ticketing System",
|
437 |
+
theme=gr.themes.Soft(
|
438 |
+
primary_hue="blue",
|
439 |
+
secondary_hue="slate",
|
440 |
+
)
|
441 |
+
) as interface:
|
442 |
+
|
443 |
+
gr.Markdown("""
|
444 |
+
# π« AI-Powered Anti-Scalping Ticketing System
|
445 |
+
|
446 |
+
This intelligent system prevents ticket scalping by analyzing user behavior, verifying identities,
|
447 |
+
and ensuring fair pricing compliance. Enter transaction details below for real-time analysis.
|
448 |
+
|
449 |
+
---
|
450 |
+
""")
|
451 |
+
|
452 |
+
with gr.Row():
|
453 |
+
with gr.Column(scale=1):
|
454 |
+
gr.Markdown("### π€ User Information")
|
455 |
+
name = gr.Textbox(label="Full Name", placeholder="John Doe", value="")
|
456 |
+
email = gr.Textbox(label="Email Address", placeholder="john@example.com", value="")
|
457 |
+
user_id = gr.Textbox(label="User ID", placeholder="USER123", value="")
|
458 |
+
|
459 |
+
gr.Markdown("### ποΈ Event Details")
|
460 |
+
event_name = gr.Textbox(label="Event Name", placeholder="Taylor Swift - Eras Tour", value="")
|
461 |
+
ticket_type = gr.Dropdown(
|
462 |
+
label="Ticket Type",
|
463 |
+
choices=["General Admission", "VIP", "Premium", "Standard", "Balcony", "Floor"],
|
464 |
+
value="Standard"
|
465 |
+
)
|
466 |
+
ticket_quantity = gr.Number(label="Number of Tickets", value=1, minimum=1, maximum=10, precision=0)
|
467 |
+
|
468 |
+
gr.Markdown("### π² Pricing Information")
|
469 |
+
original_price = gr.Number(label="Original Ticket Price ($)", value=100, minimum=1)
|
470 |
+
demand_level = gr.Radio(
|
471 |
+
label="Current Market Demand",
|
472 |
+
choices=["low", "medium", "high"],
|
473 |
+
value="medium"
|
474 |
+
)
|
475 |
+
proposed_resale_price = gr.Number(label="Proposed Resale Price ($)", value=150, minimum=1)
|
476 |
+
|
477 |
+
submit_btn = gr.Button("π Analyze Transaction", variant="primary", size="lg")
|
478 |
+
|
479 |
+
with gr.Column(scale=2):
|
480 |
+
output = gr.Markdown(value="π Fill in the form and click 'Analyze Transaction' to get started!")
|
481 |
+
|
482 |
+
# Event handlers
|
483 |
+
submit_btn.click(
|
484 |
+
fn=process_transaction,
|
485 |
+
inputs=[
|
486 |
+
name, email, user_id, event_name, ticket_type,
|
487 |
+
ticket_quantity, original_price, demand_level, proposed_resale_price
|
488 |
+
],
|
489 |
+
outputs=output
|
490 |
+
)
|
491 |
+
|
492 |
+
# Examples section
|
493 |
+
gr.Markdown("---")
|
494 |
+
gr.Markdown("### π Example Scenarios")
|
495 |
+
|
496 |
+
examples = gr.Examples(
|
497 |
+
examples=[
|
498 |
+
["John Smith", "john@gmail.com", "USER001", "Taylor Swift - Eras Tour", "VIP", 2, 500, "high", 750],
|
499 |
+
["Jane Doe", "jane@company.com", "USER002", "NBA Finals Game 7", "Premium", 4, 300, "high", 1200],
|
500 |
+
["Bob Wilson", "bob@email.com", "USER003", "Local Concert", "General Admission", 1, 50, "low", 40],
|
501 |
+
["Scalper Bot", "temp@fake.com", "BOT999", "Popular Event", "Standard", 8, 100, "high", 300],
|
502 |
+
],
|
503 |
+
inputs=[
|
504 |
+
name, email, user_id, event_name, ticket_type,
|
505 |
+
ticket_quantity, original_price, demand_level, proposed_resale_price
|
506 |
+
]
|
507 |
+
)
|
508 |
|
509 |
return interface
|
510 |
|
511 |
# Main execution
|
512 |
if __name__ == "__main__":
|
513 |
interface = create_interface()
|
514 |
+
interface.launch(
|
515 |
+
share=True,
|
516 |
+
server_name="0.0.0.0",
|
517 |
+
server_port=7860,
|
518 |
+
show_error=True
|
519 |
+
)
|