0Learn commited on
Commit
bde41ab
·
verified ·
1 Parent(s): eea022e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -0
app.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import math
3
+ import re
4
+ from fastapi import FastAPI, HTTPException
5
+ from pydantic import BaseModel, field_validator
6
+
7
+ app = FastAPI()
8
+
9
+ ALLOWED_FUNCTIONS = {'sin', 'cos', 'tan', 'log', 'ln', 'sqrt', 'abs', 'pow'}
10
+ ALLOWED_CONSTANTS = {'pi', 'e'}
11
+
12
+ class Expression(BaseModel):
13
+ expr: str
14
+
15
+ @field_validator('expr')
16
+ @classmethod
17
+ def validate_expression(cls, v: str) -> str:
18
+ if not v:
19
+ raise ValueError("Expression cannot be empty")
20
+
21
+ # Check for invalid characters
22
+ invalid_chars = re.findall(r'[^\d\s\+\-\*/\(\)\^\.\w]', v)
23
+ if invalid_chars:
24
+ raise ValueError(f"Invalid characters found: {', '.join(invalid_chars)}")
25
+
26
+ # Check for invalid function names or constants
27
+ tokens = re.findall(r'\b[a-zA-Z_]\w*\b', v)
28
+ invalid_tokens = [token for token in tokens if token not in ALLOWED_FUNCTIONS and token not in ALLOWED_CONSTANTS]
29
+ if invalid_tokens:
30
+ raise ValueError(f"Invalid function or constant names: {', '.join(invalid_tokens)}")
31
+
32
+ return v
33
+
34
+ @app.post("/calculate")
35
+ async def calculate_api(expression: Expression):
36
+ try:
37
+ result = evaluate_expression(expression.expr)
38
+ return {"result": result}
39
+ except ValueError as e:
40
+ raise HTTPException(status_code=400, detail=str(e))
41
+ except Exception as e:
42
+ raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}")
43
+
44
+ def evaluate_expression(expr: str) -> float:
45
+ safe_dict = {
46
+ 'sin': math.sin, 'cos': math.cos, 'tan': math.tan,
47
+ 'log': math.log10, 'ln': math.log, 'sqrt': math.sqrt,
48
+ 'pi': math.pi, 'e': math.e,
49
+ 'abs': abs, 'pow': pow
50
+ }
51
+ try:
52
+ # Replace '^' with '**' for exponentiation
53
+ expr = expr.replace('^', '**')
54
+ return eval(expr, {"__builtins__": None}, safe_dict)
55
+ except ZeroDivisionError:
56
+ raise ValueError("Division by zero is not allowed")
57
+ except ValueError as e:
58
+ raise ValueError(f"Math domain error: {str(e)}")
59
+ except Exception as e:
60
+ raise ValueError(f"Invalid expression: {str(e)}")
61
+
62
+ # Global variable to store calculation history
63
+ history = []
64
+
65
+ def calculate(expression):
66
+ try:
67
+ result = evaluate_expression(expression)
68
+ result_str = f"{result:.8f}".rstrip('0').rstrip('.')
69
+ global history
70
+ history.append(f"{expression} = {result_str}")
71
+ history = history[-5:]
72
+ return "0", result_str, "\n".join(reversed(history))
73
+ except Exception as e:
74
+ return expression, f"Error: {str(e)}", "\n".join(reversed(history))
75
+
76
+ def update_expression(expression, value):
77
+ if expression == "0" and value not in ['.', '(', ')']:
78
+ return value
79
+ if value in ['sin', 'cos', 'tan', 'log', 'ln', 'sqrt']:
80
+ return expression + value + '('
81
+ return expression + value
82
+
83
+ def backspace(expression):
84
+ return expression[:-1] if len(expression) > 1 else "0"
85
+
86
+ def clear(expression):
87
+ return "0"
88
+
89
+ css = """
90
+ .calculator-display { font-family: 'Digital-7', 'Courier New', monospace; font-size: 24px; }
91
+ .calculator-result { font-family: 'Digital-7', 'Courier New', monospace; font-size: 72px; }
92
+ .calculator-button { font-family: 'Arial', sans-serif; font-size: 18px; }
93
+ .number-button { background-color: #e0e0e0; }
94
+ .basic-op-button { background-color: #f0f0f0; }
95
+ .advanced-op-button { background-color: #ffa500; }
96
+ .calculator-box { border: 1px solid #ccc; padding: 10px; margin: 5px; border-radius: 5px; }
97
+ """
98
+
99
+ with gr.Blocks(css=css) as iface:
100
+ gr.Markdown("# Scientific Calculator")
101
+
102
+ with gr.Row():
103
+ with gr.Column(scale=2):
104
+ display = gr.Textbox(value="0", label="Display", elem_classes=["calculator-display"])
105
+ with gr.Column(scale=1):
106
+ result_display = gr.Textbox(value="", label="Result", elem_classes=["calculator-result"])
107
+
108
+ with gr.Row():
109
+ history_display = gr.Textbox(label="History (Last 5 Calculations)", lines=5)
110
+
111
+ with gr.Row():
112
+ with gr.Column(scale=2):
113
+ with gr.Column(elem_classes=["calculator-box"]):
114
+ # Number pad (3x4 grid)
115
+ for i in range(3):
116
+ with gr.Row():
117
+ for j in range(3):
118
+ num = str(7 - 3*i + j)
119
+ gr.Button(num, elem_classes=["calculator-button", "number-button"]).click(
120
+ lambda x, v=num: update_expression(x, v), inputs=display, outputs=display)
121
+ with gr.Row():
122
+ gr.Button("0", elem_classes=["calculator-button", "number-button"]).click(
123
+ lambda x: update_expression(x, "0"), inputs=display, outputs=display)
124
+ gr.Button(".", elem_classes=["calculator-button", "number-button"]).click(
125
+ lambda x: update_expression(x, "."), inputs=display, outputs=display)
126
+ gr.Button("=", elem_classes=["calculator-button", "basic-op-button"]).click(
127
+ calculate, inputs=display, outputs=[display, result_display, history_display])
128
+
129
+ with gr.Column(scale=1):
130
+ with gr.Column(elem_classes=["calculator-box"]):
131
+ # Basic operations
132
+ for op, symbol in [("add", "+"), ("sub", "-"), ("mul", "*"), ("div", "/")]:
133
+ gr.Button(symbol, elem_classes=["calculator-button", "basic-op-button"]).click(
134
+ lambda x, v=symbol: update_expression(x, v), inputs=display, outputs=display)
135
+ gr.Button("(", elem_classes=["calculator-button", "basic-op-button"]).click(
136
+ lambda x: update_expression(x, "("), inputs=display, outputs=display)
137
+ gr.Button(")", elem_classes=["calculator-button", "basic-op-button"]).click(
138
+ lambda x: update_expression(x, ")"), inputs=display, outputs=display)
139
+ gr.Button("C", elem_classes=["calculator-button", "basic-op-button"]).click(
140
+ clear, inputs=display, outputs=display)
141
+ gr.Button("⌫", elem_classes=["calculator-button", "basic-op-button"]).click(
142
+ backspace, inputs=display, outputs=display)
143
+
144
+ with gr.Column(scale=1):
145
+ with gr.Column(elem_classes=["calculator-box"]):
146
+ # Advanced operations
147
+ for func in ["sin", "cos", "tan", "log", "ln", "sqrt"]:
148
+ gr.Button(func, elem_classes=["calculator-button", "advanced-op-button"]).click(
149
+ lambda x, v=func: update_expression(x, v), inputs=display, outputs=display)
150
+ gr.Button("^", elem_classes=["calculator-button", "advanced-op-button"]).click(
151
+ lambda x: update_expression(x, "^"), inputs=display, outputs=display)
152
+ gr.Button("π", elem_classes=["calculator-button", "advanced-op-button"]).click(
153
+ lambda x: update_expression(x, "pi"), inputs=display, outputs=display)
154
+ gr.Button("e", elem_classes=["calculator-button", "advanced-op-button"]).click(
155
+ lambda x: update_expression(x, "e"), inputs=display, outputs=display)
156
+
157
+ iface.launch()