diff --git a/app.py b/app.py index d831bdf..a6a52fd 100644 --- a/app.py +++ b/app.py @@ -187,7 +187,8 @@ async def execute_http_function(user_id, function): request_data if log_request else {}, response_data['result'] if (log_response or response_data['status'] != 'SUCCESS') else {}, response_data['logs'], - version_number) + version_number, + response_data.get('execution_time')) if response_data['status'] != 'SUCCESS': return render_template("function_error.html", function_name=function_name ,error=response_data['result'], logs=response_data['logs']) diff --git a/db.py b/db.py index 2b1638a..69ecc33 100644 --- a/db.py +++ b/db.py @@ -95,13 +95,13 @@ class DataBase(): self.execute( 'DELETE FROM http_functions WHERE user_id=%s AND id=%s', [user_id, function_id], commit=True) - def add_http_function_invocation(self, http_function_id, status, request_data, response_data, logs, version_number): + def add_http_function_invocation(self, http_function_id, status, request_data, response_data, logs, version_number, execution_time): self.execute( - 'INSERT INTO http_function_invocations (http_function_id, status, request_data, response_data, logs, version_number) VALUES (%s, %s, %s, %s, %s, %s)', [http_function_id, status, json.dumps(request_data), json.dumps(response_data), json.dumps(logs), version_number], commit=True) + 'INSERT INTO http_function_invocations (http_function_id, status, request_data, response_data, logs, version_number, execution_time) VALUES (%s, %s, %s, %s, %s, %s, %s)', [http_function_id, status, json.dumps(request_data), json.dumps(response_data), json.dumps(logs), version_number, execution_time], commit=True) def get_http_function_invocations(self, http_function_id): http_function_invocations = self.execute( - """SELECT id, http_function_id, STATUS, invocation_time, request_data, response_data, LOGS, version_number + """SELECT id, http_function_id, STATUS, invocation_time, request_data, response_data, LOGS, version_number, execution_time FROM http_function_invocations WHERE http_function_id=%s ORDER BY invocation_time DESC""", [http_function_id]) diff --git a/routes/http.py b/routes/http.py index b8aaf27..487b421 100644 --- a/routes/http.py +++ b/routes/http.py @@ -190,11 +190,7 @@ def logs(function_id): if not http_function: return jsonify({'error': 'Function not found'}), 404 name = http_function['name'] - http_function_invocations = db.execute(""" - SELECT id, http_function_id, STATUS, invocation_time, request_data, response_data, LOGS, version_number - FROM http_function_invocations - WHERE http_function_id=%s - ORDER BY invocation_time DESC""", [function_id]) + http_function_invocations = db.get_http_function_invocations(function_id) if htmx: return render_block(environment, 'dashboard/http_functions/logs.html', 'page', user_id=user_id, function_id=function_id, name=name, http_function_invocations=http_function_invocations) return render_template("dashboard/http_functions/logs.html", user_id=user_id, name=name, function_id=function_id, http_function_invocations=http_function_invocations) diff --git a/routes/timer.py b/routes/timer.py index bb6c180..bdccec1 100644 --- a/routes/timer.py +++ b/routes/timer.py @@ -110,6 +110,7 @@ CREATE TABLE timer_function_invocations ( invocation_time TIMESTAMPTZ NOT NULL DEFAULT NOW(), logs JSONB, version_number INT NOT NULL, + execution_time FLOAT, CONSTRAINT fk_timer_function_invocations FOREIGN KEY (timer_function_id) @@ -402,9 +403,9 @@ def logs(function_id): # Fetch the invocation logs timer_function_invocations = db.execute(""" - SELECT id, timer_function_id, status, invocation_time, - logs, version_number - FROM timer_function_invocations + SELECT id, timer_function_id, status, invocation_time, + logs, version_number, execution_time + FROM timer_function_invocations WHERE timer_function_id = %s ORDER BY invocation_time DESC LIMIT 100 diff --git a/templates/dashboard/analytics.html b/templates/dashboard/analytics.html new file mode 100644 index 0000000..37efecc --- /dev/null +++ b/templates/dashboard/analytics.html @@ -0,0 +1,97 @@ +
+ +
+ +
+ + +
+
+

Total Invocations

+

{{ invocations | length }}

+
+
+

Success Rate

+ {% set successful_invocations = invocations | selectattr('status', 'equalto', 'SUCCESS') | list %} +

+ {% if invocations %} + {{ "%.2f"|format((successful_invocations|length / invocations|length) * 100) }}% + {% else %} + N/A + {% endif %} +

+
+
+

Avg. Execution Time

+ {% set total_execution_time = invocations | sum(attribute='execution_time') %} +

+ {% if invocations %} + {{ "%.2f"|format(total_execution_time / invocations|length) }}ms + {% else %} + N/A + {% endif %} +

+
+
+ + +
+

Invocation History

+
+ + + + + + + {% if invocations %} + + {% set max_execution_time = [1] %} + {% for inv in invocations %} + {% if inv.execution_time > max_execution_time[0] %}{% set _ = max_execution_time.pop() %}{{ + max_execution_time.append(inv.execution_time) }}{% endif %} + {% endfor %} + + {% set path = namespace(d='M') %} + {% for inv in invocations | reverse %} + {% set x = 40 + (loop.index0 * (340 / (invocations|length - 1 if invocations|length > 1 else 1))) %} + {% set y = 180 - ((inv.execution_time / max_execution_time[0]) * 160) %} + {% if loop.first %} + {% set path.d = path.d ~ x ~ "," ~ y %} + {% else %} + {% set path.d = path.d ~ " L" ~ x ~ "," ~ y %} + {% endif %} + {% endfor %} + + + + + {% for inv in invocations | reverse %} + {% set x = 40 + (loop.index0 * (340 / (invocations|length - 1 if invocations|length > 1 else 1))) %} + {% set y = 180 - ((inv.execution_time / max_execution_time[0]) * 160) %} + + {{ inv.invocation_time.strftime('%Y-%m-%d %H:%M:%S') }}: {{ "%.2f"|format(inv.execution_time) + }}ms + + {% endfor %} + {% endif %} + +
+
+
+ + \ No newline at end of file diff --git a/templates/dashboard/http_functions/logs.html b/templates/dashboard/http_functions/logs.html index 2ca6685..b63e259 100644 --- a/templates/dashboard/http_functions/logs.html +++ b/templates/dashboard/http_functions/logs.html @@ -14,6 +14,7 @@ logs_url=url_for('http.logs', function_id=function_id), history_url=url_for('http.history', function_id=function_id)) }}
+ {{ render_partial('dashboard/analytics.html', invocations=http_function_invocations) }}
@@ -26,7 +27,7 @@ history_url=url_for('http.history', function_id=function_id)) }} {% for invocation in http_function_invocations %}
+ class="log-row grid md:grid-cols-4 gap-4 p-4 hover:bg-gray-50 transition-colors duration-150 border-b border-gray-200">
+ {{ render_partial('dashboard/analytics.html', invocations=timer_function_invocations) }}
@@ -24,7 +25,7 @@ history_url=url_for('timer.history', function_id=function_id)) }} {% for invocation in timer_function_invocations %}
+ class="log-row grid md:grid-cols-3 gap-4 p-4 hover:bg-gray-50 transition-colors duration-150 border-b border-gray-200">
diff --git a/worker.py b/worker.py index 7370dbb..943d0c8 100644 --- a/worker.py +++ b/worker.py @@ -55,14 +55,15 @@ async def execute_timer_function_async(timer_function): # Record the invocation db.execute(""" - INSERT INTO timer_function_invocations - (timer_function_id, status, logs, version_number) - VALUES (%s, %s, %s, %s) + INSERT INTO timer_function_invocations + (timer_function_id, status, logs, version_number, execution_time) + VALUES (%s, %s, %s, %s, %s) """, [ timer_function['id'], response_data['status'], json.dumps(response_data['logs']), - version_number + version_number, + response_data.get('execution_time') ], commit=True) except Exception as e: