Add incidents to the page
parent
6b29e2a373
commit
7a826cc4bc
|
@ -1,2 +1,3 @@
|
||||||
__pycache__
|
__pycache__
|
||||||
*.py[co]
|
*.py[co]
|
||||||
|
*.sqlite
|
||||||
|
|
|
@ -10,7 +10,7 @@ from statusforce.models import Service, Incident
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = b'GSADFGST#$%^$%&^2345234534576476'
|
app.secret_key = b'GSADFGST#$%^$%&^2345234534576476'
|
||||||
app.config['FLASK_ADMIN_SWATCH'] = 'materia'
|
app.config['FLASK_ADMIN_SWATCH'] = 'materia'
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///statusforce.sqlite'
|
||||||
|
|
||||||
# Set up database
|
# Set up database
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
@ -29,7 +29,7 @@ def index():
|
||||||
Show the status page
|
Show the status page
|
||||||
"""
|
"""
|
||||||
services = Service.query.all()
|
services = Service.query.all()
|
||||||
incidents = Incident.query.limit(10).all()
|
incidents = Incident.query.order_by(Incident.created.desc()).limit(10).all()
|
||||||
service_statuses = [service.status for service in services]
|
service_statuses = [service.status for service in services]
|
||||||
overall_status = 'operational' if all(status == 'operational' for status in service_statuses) else \
|
overall_status = 'operational' if all(status == 'operational' for status in service_statuses) else \
|
||||||
'offline' if all(status == 'offline' for status in service_statuses) else 'unclear'
|
'offline' if all(status == 'offline' for status in service_statuses) else 'unclear'
|
||||||
|
|
|
@ -12,5 +12,6 @@ DateTime = db.DateTime
|
||||||
Enum = db.Enum
|
Enum = db.Enum
|
||||||
Integer = db.Integer
|
Integer = db.Integer
|
||||||
String = db.String
|
String = db.String
|
||||||
|
Text = db.Text
|
||||||
relationship = db.relationship
|
relationship = db.relationship
|
||||||
backref = db.backref
|
backref = db.backref
|
||||||
|
|
|
@ -2,20 +2,28 @@ from datetime import datetime
|
||||||
|
|
||||||
from sqlalchemy.sql.expression import func
|
from sqlalchemy.sql.expression import func
|
||||||
|
|
||||||
from statusforce.db import Model, Column, ForeignKey, Boolean, DateTime, Enum, Integer, String, relationship, backref
|
from statusforce.db import Model, Column, ForeignKey, Boolean, DateTime, Enum, Integer, String, Text, \
|
||||||
|
relationship, backref
|
||||||
|
|
||||||
|
|
||||||
class Service(Model):
|
class Service(Model):
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
name = Column(String, nullable=False)
|
name = Column(String, nullable=False)
|
||||||
status = Column(Enum('operational', 'unclear', 'offline'), default='operational')
|
status = Column(Enum('operational', 'unclear', 'offline'), default='operational')
|
||||||
incidents = relationship('Incident', backref=backref('services', lazy=True))
|
incidents = relationship('Incident', backref=backref('service', lazy=True))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class Incident(Model):
|
class Incident(Model):
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
service_id = Column(Integer, ForeignKey('service.id'))
|
service_id = Column(Integer, ForeignKey('service.id'))
|
||||||
title = Column(String, nullable=False)
|
title = Column(String, nullable=False)
|
||||||
|
description = Column(Text)
|
||||||
is_resolved = Column(Boolean, default=False)
|
is_resolved = Column(Boolean, default=False)
|
||||||
created = Column(DateTime, nullable=False, default=datetime.utcnow)
|
created = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||||
updated = Column(DateTime, onupdate=func.current_timestamp())
|
updated = Column(DateTime, onupdate=func.current_timestamp())
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '{} ({})'.format(self.title, self.service.name)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>System Status</title>
|
<title>System Status</title>
|
||||||
<link href="{{url_for('static', filename='css/bootstrap.min.css')}}" rel="stylesheet">
|
<link href="{{url_for('static', filename='css/bootstrap.min.css')}}" rel="stylesheet">
|
||||||
|
<link href="{{url_for('static', filename='css/bootstrap-icons.css')}}" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
.bd-placeholder-img {
|
.bd-placeholder-img {
|
||||||
font-size: 1.125rem;
|
font-size: 1.125rem;
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-light">
|
<body class="bg-light">
|
||||||
<div class="container">
|
<div class="container px-5">
|
||||||
<main>
|
<main>
|
||||||
<div class="py-5 text-center">
|
<div class="py-5 text-center">
|
||||||
<img class="d-block mx-auto mb-4" src="{{url_for('static', filename='img/ltf-logo.svg')}}" alt="" width="72" height="57">
|
<img class="d-block mx-auto mb-4" src="{{url_for('static', filename='img/ltf-logo.svg')}}" alt="" width="72" height="57">
|
||||||
|
@ -30,42 +31,87 @@
|
||||||
</div>
|
</div>
|
||||||
{% if overall_status == 'operational' %}
|
{% if overall_status == 'operational' %}
|
||||||
<div class="p-3 mb-5 bg-success text-white" style="border-radius: 3px;">
|
<div class="p-3 mb-5 bg-success text-white" style="border-radius: 3px;">
|
||||||
All systems are operational
|
<i class="bi-check-circle-fill" style="margin-right: 0.5rem;"></i> All systems are operational
|
||||||
</div>
|
</div>
|
||||||
{% elif overall_status == 'unclear' %}
|
{% elif overall_status == 'unclear' %}
|
||||||
<div class="p-3 mb-5 bg-warning text-white" style="border-radius: 3px;">
|
<div class="p-3 mb-5 bg-warning" style="border-radius: 3px;">
|
||||||
Some systems are experiencing problems
|
<i class="bi-question-circle-fill" style="margin-right: 0.5rem;"></i> Some systems are experiencing problems
|
||||||
</div>
|
</div>
|
||||||
{% elif overall_status == 'offline' %}
|
{% elif overall_status == 'offline' %}
|
||||||
<div class="p-3 mb-5 bg-danger text-white" style="border-radius: 3px;">
|
<div class="p-3 mb-5 bg-danger text-white" style="border-radius: 3px;">
|
||||||
<strong>There is a major disruption of services</strong>
|
<i class="bi-exclamation-circle-fill" style="margin-right: 0.5rem;"></i> <strong>There is a major disruption of services</strong>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if services | length > 0 %}
|
||||||
|
<h2 class="mt-5 mb-3">Services</h2>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{% for service in services %}
|
{% for service in services %}
|
||||||
{% if service.status == 'operational' %}
|
{% if service.status == 'operational' %}
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center p-3">
|
<li class="list-group-item d-flex justify-content-between align-items-center p-3">
|
||||||
{% elif service.status == 'unclear' %}
|
{% elif service.status == 'unclear' %}
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center p-3 bg-warning" style="--bg-opacity: 0.2">
|
<li class="list-group-item d-flex justify-content-between align-items-center p-3 bg-warning" style="--bs-bg-opacity: 0.2">
|
||||||
{% elif service.status == 'offline' %}
|
{% elif service.status == 'offline' %}
|
||||||
<li class="list-group-item d-flex justify-content-between align-items-center p-3 bg-danger text-white" style="--bg-opacity: 0.2">
|
<li class="list-group-item d-flex justify-content-between align-items-center p-3 bg-danger" style="--bs-bg-opacity: 0.2">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{service.name}}
|
{{service.name}}
|
||||||
{% if service.status == 'operational' %}
|
{% if service.status == 'operational' %}
|
||||||
<span class="p-2 bg-success border border-light rounded-circle">
|
<span class="p-2 bg-success rounded-circle">
|
||||||
{% elif service.status == 'unclear' %}
|
{% elif service.status == 'unclear' %}
|
||||||
<span class="p-2 bg-warning border border-light rounded-circle">
|
<span class="p-2 bg-warning rounded-circle">
|
||||||
{% elif service.status == 'offline' %}
|
{% elif service.status == 'offline' %}
|
||||||
<span class="p-2 bg-danger border border-light rounded-circle">
|
<span class="p-2 bg-danger rounded-circle">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="visually-hidden">{{service.status}}</span>
|
<span class="visually-hidden">{{service.status}}</span>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% if incidents | length > 0 %}
|
||||||
|
<h2 class="mt-5 mb-3">Incidents</h2>
|
||||||
|
{% endif %}
|
||||||
|
{% for incident in incidents %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-auto text-center flex-column d-none d-sm-flex">
|
||||||
|
<div class="row h-50">
|
||||||
|
{% if loop.first %}
|
||||||
|
<div class="col"> </div>
|
||||||
|
<div class="col"> </div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col border-end border-2"> </div>
|
||||||
|
<div class="col border-start border-2"> </div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<!-- h5 class="m-2"><span class="rounded-circle bg-white border px-3 py-1"></span></h5 -->
|
||||||
|
{% if incident.is_resolved %}
|
||||||
|
<h5 class="m-2"><span class="bi-check-circle-fill text-success" style="font-size: 200%"></span></h5>
|
||||||
|
{% else %}
|
||||||
|
<h5 class="m-2"><span class="bi-exclamation-circle-fill text-danger" style="font-size: 200%"></span></h5>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row h-50">
|
||||||
|
{% if loop.last %}
|
||||||
|
<div class="col"> </div>
|
||||||
|
<div class="col"> </div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col border-end border-2"> </div>
|
||||||
|
<div class="col border-start border-2"> </div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col py-2">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="float-end">{{incident.created.strftime('%a %D %B, %Y')}}</div>
|
||||||
|
<h4 class="card-title text-muted">{{incident.title}} - {{incident.service.name}}</h4>
|
||||||
|
<p class="card-text text-muted">{{incident.description}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</main>
|
</main>
|
||||||
<footer class="my-5 pt-5 text-muted text-center text-small">
|
<footer class="my-5 pt-5 text-muted text-center text-small">
|
||||||
<p class="mb-1">© 2021 Liberty Tech Force</p>
|
<p class="mb-1">© 2021 <a href="https://libertytechforce.com">Liberty Tech Force</a> | Powered by <a href="https://git.libertytechforce.com/libertytechforce/statusforce">StatusForce</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<!-- script src="js/bootstrap.bundle.min.js"></script -->
|
<!-- script src="js/bootstrap.bundle.min.js"></script -->
|
||||||
|
|
Loading…
Reference in New Issue