YuukouNum / app.py
OzoneAsai's picture
Update app.py
b1b82b2 verified
from flask import Flask, request, render_template_string
from decimal import Decimal, ROUND_HALF_UP, InvalidOperation
import re
from datetime import datetime
app = Flask(__name__)
# 入れ子構造を持つ単位変換用の辞書
CONVERSION_TABLE = {
'length': {
'm': 1,
'cm': 0.01,
'mm': 0.001,
'km': 1000,
},
'time': {
's': 1,
'min': 60,
'h': 3600,
},
'mass': {
'g': 1,
'kg': 1000,
'mg': 0.001,
'µg': 1e-6,
},
'volume': {
'L': 1,
'mL': 0.001,
}
}
def log(message):
"""タイムスタンプ付きでメッセージを出力"""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
print(f"[{timestamp}] {message}")
def parse_value_unit(value_unit_str):
"""数値と単位をパースして、共通単位に変換"""
log(f"Parsing: {value_unit_str}")
match = re.match(r"([\d.eE+-]+)\s*\[([a-zA-Zµ^2]+)\]", value_unit_str)
if match:
value, unit = match.groups()
log(f"Parsed value: {value}, unit: {unit}")
try:
decimal_value = Decimal(value)
except InvalidOperation:
raise ValueError(f"Invalid numeric value: {value}")
sig_figs = len(value.split('.')[1]) if '.' in value else 0
return decimal_value, unit, sig_figs
else:
raise ValueError("Invalid format. The input should be in the form 'value[unit]'.")
def find_unit_family(unit):
"""指定された単位のファミリー(カテゴリ)を見つける"""
for family, units in CONVERSION_TABLE.items():
if unit in units:
return family
raise ValueError(f"Unit {unit} not found in any known unit families.")
def convert_to_common_unit(value, from_unit, to_unit):
"""異なる単位を同じ単位に変換する"""
from_family = find_unit_family(from_unit)
to_family = find_unit_family(to_unit)
if from_family != to_family:
raise ValueError(f"Cannot convert between different unit families: {from_unit} to {to_unit}")
conversion_factor = CONVERSION_TABLE[from_family][from_unit] / CONVERSION_TABLE[to_family][to_unit]
converted_value = value * Decimal(conversion_factor)
log(f"Converting {value} {from_unit} to {converted_value} {to_unit}")
return converted_value
def calculate_expression(expression):
"""複数の数値を処理する計算(加算、減算、乗算、除算に対応)"""
log(f"Calculating expression: {expression}")
pattern = r"([\d.eE+-]+\s*\[[^\]]+\])"
terms = re.findall(pattern, expression)
log(f"Found terms: {terms}")
if not terms:
raise ValueError("Invalid expression format")
# 最初の項目を取得
value1, unit1, sig_figs1 = parse_value_unit(terms[0])
total_value = value1
total_unit = unit1
sig_figs_list = [sig_figs1]
for i in range(1, len(terms)):
value2, unit2, sig_figs2 = parse_value_unit(terms[i])
sig_figs_list.append(sig_figs2)
if unit1 != unit2 and ('*' not in expression and '/' not in expression):
value2 = convert_to_common_unit(value2, unit2, unit1)
if '*' in expression:
total_value *= value2
total_unit = f"{total_unit}*{unit2}"
elif '/' in expression:
total_value /= value2
total_unit = f"{total_unit}/{unit2}"
else:
total_value += value2
log(f"Current total: {total_value}, current significant figures list: {sig_figs_list}")
# 最も少ない有効数字に基づいて丸める
min_sig_figs = min(sig_figs_list)
rounded_result = total_value.quantize(Decimal('1e{0}'.format(-(min_sig_figs - 1))), rounding=ROUND_HALF_UP)
# 小数点以下のゼロを省略せずに表示
rounded_result_str = format(rounded_result, 'f')
log(f"Final result: {rounded_result_str} [{total_unit}]")
return f"{rounded_result_str} [{total_unit}]"
# HTMLテンプレート(バイト列でエンコード)
template = b"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Unit Calculator</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
h1 {
color: #333;
}
form {
margin-bottom: 20px;
}
input[type="text"] {
padding: 8px;
width: 300px;
font-size: 16px;
}
button {
padding: 8px 12px;
font-size: 16px;
background-color: #007BFF;
color: #fff;
border: none;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
.result {
font-size: 20px;
font-weight: bold;
color: #007BFF;
}
</style>
</head>
<body>
<h1>Unit Calculator</h1>
<form id="calcForm">
<label for="expression">Enter expression:</label>
<input type="text" id="expression" name="expression" required>
<button type="submit">Calculate</button>
</form>
<div id="result" class="result"></div>
<script>
document.getElementById('calcForm').addEventListener('submit', function(event) {
event.preventDefault();
var expression = document.getElementById('expression').value;
var xhr = new XMLHttpRequest();
xhr.open('POST', '/calculate', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
document.getElementById('result').innerText = 'Result: ' + xhr.responseText;
}
};
xhr.send('expression=' + encodeURIComponent(expression));
});
</script>
</body>
</html>
"""
@app.route('/', methods=['GET'])
def index():
return render_template_string(template.decode('utf-8'))
@app.route('/calculate', methods=['POST'])
def calculate():
expression = request.form['expression']
try:
result = calculate_expression(expression)
return result
except Exception as e:
log(f"Error: {str(e)}")
return f"Error: {str(e)}", 400
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", port=7860)