Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
9369dff
with more page
daedaluschan Jul 15, 2024
2225687
path of css and js
daedaluschan Jul 15, 2024
b24ca82
correct the css file name
daedaluschan Jul 15, 2024
50086b7
missing .menu.open
daedaluschan Jul 15, 2024
8fa9ba4
using base.html to avoid repeating html codes
daedaluschan Jul 17, 2024
88093a7
comment out tariff.htlm for now
daedaluschan Jul 18, 2024
4081183
Adding the REST library
daedaluschan Aug 11, 2024
a5d2dc7
remove the tariff endpoint
daedaluschan Aug 11, 2024
a3ac49a
Hello world api endpoint
daedaluschan Aug 11, 2024
d3a06b4
switch to handl api call instead
daedaluschan Aug 18, 2024
1d719f3
try normal web again
daedaluschan Aug 24, 2024
1ea2cc0
useing restplus
daedaluschan Aug 24, 2024
913d14f
try update requirement.txt with no versioing
daedaluschan Aug 24, 2024
574d6bb
use simple web api code
daedaluschan Aug 24, 2024
f0ff9c6
with api key authenication
daedaluschan Aug 24, 2024
ba5c0f8
just changing the hello world msg
daedaluschan Aug 24, 2024
54b82ca
add api spec
daedaluschan Aug 26, 2024
98b583a
include send_from_directory
daedaluschan Aug 26, 2024
fea70de
add operationId in the spec yaml
daedaluschan Aug 26, 2024
aca72f7
api version to 3.1.0
daedaluschan Aug 26, 2024
6a171fb
appi api key part in the yaml
daedaluschan Aug 26, 2024
35fee13
Ignore about api key validation for now
daedaluschan Aug 26, 2024
5d1029c
new tariff end point
daedaluschan Aug 26, 2024
e4d19e1
updated api spec for new tariff end point
daedaluschan Aug 26, 2024
65afb1a
added the import for tariff_utils
daedaluschan Aug 26, 2024
80c756b
octopus tariff api spec
daedaluschan Aug 26, 2024
9614067
try to run request.get
daedaluschan Sep 21, 2024
8ff6201
try again request.get
daedaluschan Sep 21, 2024
cce95ed
change to simulate curl -u on the request.get()
daedaluschan Sep 21, 2024
536f23b
cast str on api_auth
daedaluschan Sep 21, 2024
34df9eb
place the secret retrieve in app.py
daedaluschan Sep 21, 2024
182f91d
correct the env var name
daedaluschan Sep 21, 2024
9a86232
put then auth parameter to carry the api key
daedaluschan Sep 21, 2024
ff967a4
continue to print the data payload
daedaluschan Sep 21, 2024
0cc1308
print out the parsed time value
daedaluschan Sep 21, 2024
d544613
correct the api end point
daedaluschan Sep 21, 2024
b1b30da
switch back
daedaluschan Sep 21, 2024
9a6b800
update requirement.txt
daedaluschan Sep 21, 2024
2446e81
not using dateutil
daedaluschan Sep 21, 2024
1b2fb5d
filter timeslot
daedaluschan Sep 21, 2024
6fdda12
not consider the current timeslot
daedaluschan Sep 21, 2024
5773c2a
calculating consecutive slot and avg tariff
daedaluschan Sep 22, 2024
97bf5d0
first draft completed
daedaluschan Sep 25, 2024
3a072e3
humm
daedaluschan Sep 26, 2024
955059b
hummm....
daedaluschan Sep 26, 2024
30b89d5
debug print
daedaluschan Sep 28, 2024
f7809ea
correct syntx on line 66
daedaluschan Sep 28, 2024
bb09914
parse the time as UTC
daedaluschan Sep 28, 2024
ccc2fd1
corrected time format
daedaluschan Sep 28, 2024
b1b94d5
converting to UK timezone with pytz
daedaluschan Sep 28, 2024
9b4a181
fixed bug
daedaluschan Sep 28, 2024
517c9c9
adding dummy endpoint for demo
daedaluschan Jan 8, 2025
6ce5a50
Merge pull request #1 from daedaluschan/add-dummy-demo-endpoint
daedaluschan Jan 8, 2025
75496b3
specify connection type when
daedaluschan Jan 8, 2025
d156ac5
Merge pull request #2 from daedaluschan/add-dummy-demo-endpoint
daedaluschan Jan 8, 2025
245ea6e
specify connection type if it is not ALL
daedaluschan Jan 8, 2025
82fbbfc
Merge pull request #3 from daedaluschan/add-dummy-demo-endpoint
daedaluschan Jan 8, 2025
15a38f9
add debug msg for the rest call status code
daedaluschan Jan 10, 2025
8cfe898
more trace
daedaluschan Jan 10, 2025
6c0a6e5
tracing different timestamp
daedaluschan Jan 10, 2025
c8faf45
New API endpoint
daedaluschan Feb 26, 2025
ea9b3eb
remove comment
daedaluschan Feb 26, 2025
047e308
upload FIX data dic xml
daedaluschan Aug 17, 2025
c339599
add xml file download route
daedaluschan Aug 17, 2025
e7ed6cd
added fixreft.html
daedaluschan Aug 17, 2025
e89d618
added route for html folder
daedaluschan Aug 17, 2025
5267598
resolve stack limit problem
daedaluschan Aug 17, 2025
d5bea94
Remove XML endpoint and static XML files
daedaluschan Apr 19, 2026
463c7a0
Merge branch 'main' into remove_xml
daedaluschan Apr 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 108 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,114 @@
from flask import Flask
from flask import Flask, jsonify, request, send_from_directory
from tariff_utils import calculate_start_time
import os
from datetime import datetime, timedelta
import random

api_key = os.getenv("OCTOPUS_KEY")

app = Flask(__name__)

# Read API Key from environment variable
# VALID_API_KEYS = {os.getenv('API_KEY')} # Assuming there's only one key for simplicity

# def require_api_key(f):
# def decorated(*args, **kwargs):
# api_key = request.headers.get('API-Key')
# if api_key not in VALID_API_KEYS:
# abort(401) # Unauthorized access if the API key is not valid
# return f(*args, **kwargs)
# return decorated

@app.route('/')
# @require_api_key
def hello_world():
return 'Hello from Koyeb'
return jsonify(message="Hello, Happy Flasking!")

@app.route('/api/spec')
def api_spec():
return send_from_directory('static', 'api_spec.yaml')

# --- New route: serve XML files from /static/xml ---
@app.route('/xml/<path:filename>')
def serve_xml(filename: str):
"""Serve XML files from the /static/xml directory via /xml/<filename>.xml.

If the requested file does not exist, return a JSON 404 with a clear message.
Only .xml files are allowed (anything else 404s).
"""
if not filename.lower().endswith('.xml'):
abort(404)

xml_dir = os.path.join(app.root_path, 'static', 'xml')
file_path = os.path.join(xml_dir, filename)

# Ensure the file exists; if not, return a simple JSON 404 response
if not os.path.isfile(file_path):
return jsonify(error="File not found"), 404

# send_from_directory safely serves files from a specific folder
return send_from_directory(xml_dir, filename, mimetype='application/xml')

# send_from_directory safely serves files from a specific folder
# \1# --- New route: serve HTML files from /static/html ---
@app.route('/html/<path:filename>')
def serve_html(filename: str):
"""Serve HTML files from the /static/html directory via /html/<filename>.html.

If the requested file does not exist, return a JSON 404 with a clear message.
Only .html files are allowed (anything else 404s).
"""
if not filename.lower().endswith('.html'):
abort(404)

html_dir = os.path.join(app.root_path, 'static', 'html')
file_path = os.path.join(html_dir, filename)

# Ensure the file exists; if not, return a simple JSON 404 response
if not os.path.isfile(file_path):
return jsonify(error="File not found"), 404

# send_from_directory safely serves files from a specific folder
return send_from_directory(html_dir, filename, mimetype='text/html')

@app.route('/tariff')
def tariff():
# Retrieve the numHours parameter from the request's query string
num_hours_str = request.args.get('numHours', default=None)

if num_hours_str is None:
return jsonify(error="numHours parameter is required"), 400

try:
num_hours = int(num_hours_str)
except ValueError:
return jsonify(error="numHours must be an integer"), 400

# Use the external module to calculate the start time
start_time_str = calculate_start_time(num_hours, api_key)
return jsonify(startTime=start_time_str)

@app.route('/demo_status')
def demo_status():
connection_type = request.args.get('type', default=None)

if connection_type not in ["FIX", "MQ", "SFTP", "ALL"]:
return jsonify(error="Invalid connection type. Allowed values are FIX, MQ, SFTP, ALL."), 400

return jsonify(message=f'All your {"" if connection_type == "ALL" else connection_type} connections are up and running')

@app.route('/demo_details')
def demo_details():
connection_id = request.args.get('id', default=None)

if not connection_id:
return jsonify(error="ID parameter is required"), 400

current_time = datetime.utcnow()
random_minutes = random.randint(1, 20)
last_connection_time = current_time - timedelta(minutes=random_minutes)

return jsonify(message=f"Connection {connection_id} is up", lastConnectionTime=last_connection_time.isoformat() + "Z")

if __name__ == "__main__":
app.run()
if __name__ == '__main__':
app.run(debug=True)
25 changes: 25 additions & 0 deletions app_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from flask import Flask
from flask_restful import Api, Resource
from flasgger import Swagger

app = Flask(__name__)
api = Api(app)
swagger = Swagger(app)

class Hello(Resource):
def get(self):
"""
A hello world endpoint
---
responses:
200:
description: Returns a greeting
examples:
application/json: {"hello": "world"}
"""
return {'hello': 'world'}

api.add_resource(Hello, '/hello')

if __name__ == '__main__':
app.run(debug=True)
16 changes: 16 additions & 0 deletions app_rest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from flask import Flask
from flask_restplus import Api, Resource

app = Flask(__name__)
api = Api(app, version='1.0', title='My API', description='A simple API')

ns = api.namespace('my_namespace', description='Namespace operations')

@ns.route('/hello')
class HelloWorld(Resource):
def get(self):
'''Returns a greeting'''
return {'hello': 'world'}

if __name__ == '__main__':
app.run(debug=True)
10 changes: 10 additions & 0 deletions app_simple_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def hello_world():
return jsonify(message="Hello, World!")

if __name__ == '__main__':
app.run(debug=True)
42 changes: 42 additions & 0 deletions app_web.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from flask import Flask, render_template
from flask_restplus import Api, Resource
app = Flask(__name__)

@app.route('/')
def home():
return render_template('index.html')

@app.route('/about')
def about():
return render_template('about.html')

@app.route('/portfolio')
def portfolio():
return render_template('portfolio.html')

@app.route('/services')
def services():
return render_template('services.html')

@app.route('/contact')
def contact():
return render_template('contact.html')

@app.route('/blog')
def blog():
return render_template('blog.html')


api = Api(app, version='1.0', title='My Octopus API', description='A simple API to retrieve my Octopus tariff data')

ns = api.namespace('my_tariff_namespace', description='Tariff Namespace operations')

@ns.route('/tariff')
class HelloWorld(Resource):
def get(self):
'''Returns a greeting'''
return {'hello': 'world'}


if __name__ == "__main__":
app.run(debug=True)
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
Werkzeug==2.2.2
Flask-RESTX
requests==2.26.0
pytz
128 changes: 128 additions & 0 deletions static/api_spec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
openapi: 3.1.0
info:
title: Hello World API
description: A simple API to return a Hello World message
version: "1.0"
servers:
- url: https://sharp-moria-avon18-b017b82a.koyeb.app/
paths:
/:
get:
summary: Returns a Hello World message
operationId: helloWorld
responses:
200:
description: Successful response
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "Hello, World!"
401:
description: Unauthorized access
/tariff:
get:
summary: Calculate and return a future start time when the appliance should start in order to minimize energy cost
operationId: cheapestStartTime
parameters:
- in: query
name: numHours
schema:
type: integer
required: true
description: Number of hours of the appliance going to run for
responses:
200:
description: Successful response with the future start time
content:
application/json:
schema:
type: object
properties:
startTime:
type: string
example: "2024-08-10 17:00:00"
400:
description: Bad request response when input validation fails
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "numHours parameter is required"
401:
description: Unauthorized access
/demo_status:
get:
summary: Returns the status of connections
operationId: demoStatus
parameters:
- in: query
name: type
schema:
type: string
enum: [FIX, MQ, SFTP, ALL]
required: true
description: The type of connection to check
responses:
200:
description: Successful response confirming all connections are up
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "All your connections are up and running"
400:
description: Bad request response when input validation fails
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "Invalid connection type. Allowed values are FIX, MQ, SFTP, ALL."
/demo_details:
get:
summary: Returns the details of a specific connection
operationId: demoDetails
parameters:
- in: query
name: id
schema:
type: string
required: true
description: The ID of the connection to check
responses:
200:
description: Successful response with connection details
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "Connection 123 is up"
lastConnectionTime:
type: string
format: date-time
example: "2025-01-08T12:00:00Z"
400:
description: Bad request response when input validation fails
content:
application/json:
schema:
type: object
properties:
error:
type: string
example: "ID parameter is required"
Loading