Skip to content

Commit 31c6ba8

Browse files
authored
Merge pull request #18 from mircealungu/development
Moved setup.py to the root of the repo.
2 parents 6105b92 + 01aeb84 commit 31c6ba8

File tree

116 files changed

+38043
-741
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+38043
-741
lines changed

MANIFEST.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
recursive-include dashboard/static *
2+
recursive-include dashboard/templates *

README.md

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,31 @@ Adding the extension to your flask app is simple:
2020
from flask import Flask
2121
import dashboard
2222

23-
app = Flask(__name__)
24-
dashboard.bind(app)
23+
user_app = Flask(__name__)
24+
dashboard.config.from_file('/<path to your config file>/config.cfg')
25+
26+
def get_session_id():
27+
# Implement your own function for obtaining the user variable here.
28+
return "12345"
29+
30+
dashboard.config.get_group_by = get_session_id
31+
dashboard.bind(app=user_app)
2532

2633
Usage
2734
=====
2835
Once the setup is done, a config file ('config.cfg') should be set next to the python file that contains the entry point of the app.
29-
The following things should be set for best performance:
36+
The following things can be configured:
3037

3138
[dashboard]
32-
DATABASE=sqlite:////<path to your project>/flask-dashboard.db
33-
GIT=/<path to your project>/flask-dashboard/.git/
34-
TEST_DIR=/<path to your project>/flask-dashboard/tests/
39+
APP_VERSION=1.0
40+
CUSTOM_LINK=dashboard
41+
USERNAME=admin
42+
PASSWORD=admin
43+
DATABASE=sqlite:////<path to your project>/dashboard.db
44+
GIT=/<path to your project>/dashboard/.git/
45+
TEST_DIR=/<path to your project>/dashboard/tests/
46+
47+
For more information: [see this file](dashboard/config.py)
3548

3649
When running your app, the dashboard van be viewed by default in the route:
3750

flask-dashboard/dashboard/__init__.py renamed to dashboard/__init__.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323

2424
# get current location of the project
2525
def loc():
26-
s = os.path.abspath(os.path.dirname(__file__))
27-
return str(s.rsplit('/', 1)[0]) + '/'
26+
return os.path.abspath(os.path.dirname(__file__)) + '/'
27+
2828

2929
# define the blueprint
3030
blueprint = Blueprint('dashboard', __name__, template_folder=loc() + 'templates')
@@ -50,15 +50,16 @@ def bind(app):
5050
# register the blueprint to the app
5151
app.register_blueprint(blueprint, url_prefix='/' + config.link)
5252

53-
# search for tests
54-
from dashboard.database.tests import add_test, get_tests
55-
suites = TestLoader().discover(config.test_dir, pattern="*test*.py")
56-
existing_tests = get_tests()
57-
tests = []
58-
for t in existing_tests:
59-
tests.append(t.name)
60-
for suite in suites:
61-
for case in suite:
62-
for test in case:
63-
if str(test) not in tests:
64-
add_test(str(test))
53+
# search for tests if test dir specified
54+
if config.test_dir:
55+
from dashboard.database.tests import add_test, get_tests
56+
suites = TestLoader().discover(config.test_dir, pattern="*test*.py")
57+
existing_tests = get_tests()
58+
tests = []
59+
for t in existing_tests:
60+
tests.append(t.name)
61+
for suite in suites:
62+
for case in suite:
63+
for test in case:
64+
if str(test) not in tests:
65+
add_test(str(test))

flask-dashboard/dashboard/config.py renamed to dashboard/config.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ def __init__(self):
1515
self.version = '1.0'
1616
self.link = 'dashboard'
1717
self.database_name = 'sqlite:///flask-dashboard.db'
18-
self.test_dir = './'
18+
self.test_dir = None
19+
self.username = 'admin'
20+
self.password = 'admin'
1921

2022
# define a custom function to retrieve the session_id or username
2123
self.get_group_by = None
@@ -35,7 +37,10 @@ def from_file(self, config_file):
3537
version automatically retrieved by reading the commit-id (hashed value):
3638
GIT = If you're using git, then it is easier to set the location to the .git-folder,
3739
The location is relative to the config-file.
38-
40+
41+
USERNAME: for logging into the dashboard, a username and password is required. The
42+
username can be set using this variable.
43+
PASSWORD: same as for the username, but this is the password variable.
3944
4045
:param config_file: a string pointing to the location of the config-file
4146
"""
@@ -51,6 +56,7 @@ def from_file(self, config_file):
5156
self.database_name = parser.get('dashboard', 'DATABASE')
5257
if parser.has_option('dashboard', 'TEST_DIR'):
5358
self.test_dir = parser.get('dashboard', 'TEST_DIR')
59+
5460
# When the option git is selected, it overrides the given version
5561
if parser.has_option('dashboard', 'GIT'):
5662
git = parser.get('dashboard', 'GIT')
@@ -65,5 +71,11 @@ def from_file(self, config_file):
6571
except IOError:
6672
print("Error reading one of the files to retrieve the current git-version.")
6773
raise
74+
75+
# provide username and/or password
76+
if parser.has_option('dashboard', 'USERNAME'):
77+
self.username = parser.get('dashboard', 'USERNAME')
78+
if parser.has_option('dashboard', 'PASSWORD'):
79+
self.password = parser.get('dashboard', 'PASSWORD')
6880
except configparser.Error:
6981
raise

flask-dashboard/dashboard/database/__init__.py renamed to dashboard/database/__init__.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,6 @@ class FunctionCall(Base):
6969
ip = Column(String(25), nullable=False)
7070

7171

72-
class Setting(Base):
73-
""" Table for storing the values of certain variables """
74-
__tablename__ = 'settings'
75-
# the name of the variable must be unique
76-
variable = Column(String(100), primary_key=True)
77-
# the corresponding value
78-
value = Column(String(100))
79-
8072
# define the database
8173
engine = create_engine(config.database_name)
8274

flask-dashboard/dashboard/database/endpoint.py renamed to dashboard/database/endpoint.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""
22
Contains all functions that access a single endpoint
33
"""
4-
from sqlalchemy import func, text
4+
from sqlalchemy import func, text, asc
55
from sqlalchemy.orm.exc import NoResultFound
66
import datetime
77

@@ -24,12 +24,38 @@ def get_line_results(endpoint):
2424
return data
2525

2626

27+
def get_num_requests(endpoint):
28+
""" Returns a list with all dates on which an endpoint is accessed.
29+
:param endpoint: if None, the result is the sum of all endpoints
30+
"""
31+
with session_scope() as db_session:
32+
query = text("""select
33+
datetime(CAST(strftime('%s', time)/3600 AS INT)*3600, 'unixepoch') AS newTime,
34+
count(execution_time) as count
35+
from functioncalls
36+
where (endpoint=:val OR :val='None') group by newTime""")
37+
result = db_session.execute(query, {'val': str(endpoint)})
38+
data = result.fetchall()
39+
return data
40+
41+
2742
def get_endpoint_column(endpoint, column):
43+
""" Returns a list of entries from column in which the endpoint is involved. """
44+
with session_scope() as db_session:
45+
result = db_session.query(column,
46+
func.min(FunctionCall.time).label('startedUsingOn')). \
47+
filter(FunctionCall.endpoint == endpoint). \
48+
group_by(column).order_by(asc('startedUsingOn')).all()
49+
db_session.expunge_all()
50+
return result
51+
52+
53+
def get_endpoint_column_user_sorted(endpoint, column):
2854
""" Returns a list of entries from column in which the endpoint is involved. """
2955
with session_scope() as db_session:
3056
result = db_session.query(column). \
3157
filter(FunctionCall.endpoint == endpoint). \
32-
group_by(column).all()
58+
group_by(column).order_by(asc(column)).all()
3359
db_session.expunge_all()
3460
return result
3561

@@ -74,13 +100,22 @@ def update_monitor_rule(endpoint, value):
74100

75101

76102
def get_all_measurement(endpoint):
77-
"""Return all entries with measurements from a given endpoint. Uses for creating a histogram. """
103+
"""Return all entries with measurements from a given endpoint. Used for creating a histogram. """
78104
with session_scope() as db_session:
79105
result = db_session.query(FunctionCall).filter(FunctionCall.endpoint == endpoint).all()
80106
db_session.expunge_all()
81107
return result
82108

83109

110+
def get_all_measurement_per_column(endpoint, column, value):
111+
"""Return all entries with measurements from a given endpoint for which the column has a specific value.
112+
Used for creating a box plot. """
113+
with session_scope() as db_session:
114+
result = db_session.query(FunctionCall).filter(FunctionCall.endpoint == endpoint, column == value).all()
115+
db_session.expunge_all()
116+
return result
117+
118+
84119
def get_last_accessed_times():
85120
""" Returns a list of all endpoints and their last accessed time. """
86121
with session_scope() as db_session:

dashboard/database/function_calls.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
"""
2+
Contains all functions that access any functionCall-object
3+
"""
4+
5+
from flask import request
6+
from sqlalchemy import func, desc, text, asc
7+
from dashboard import config
8+
import datetime
9+
from dashboard.database import session_scope, FunctionCall
10+
11+
12+
def get_reqs_endpoint_day():
13+
""" Retrieves the number of requests per endpoint per day. """
14+
with session_scope() as db_session:
15+
query = text("""select strftime('%Y-%m-%d', time) AS newTime,
16+
count(endpoint) AS cnt,
17+
endpoint
18+
from functioncalls
19+
group by newTime, endpoint""")
20+
result = db_session.execute(query)
21+
data = result.fetchall()
22+
return data
23+
24+
25+
def add_function_call(time, endpoint):
26+
""" Add a measurement to the database. """
27+
with session_scope() as db_session:
28+
group_by = None
29+
if config.get_group_by:
30+
group_by = config.get_group_by()
31+
32+
ip = request.environ['REMOTE_ADDR']
33+
call = FunctionCall(endpoint=endpoint, execution_time=time, version=config.version,
34+
time=datetime.datetime.now(), group_by=str(group_by), ip=ip)
35+
db_session.add(call)
36+
37+
38+
def get_times():
39+
""" Return all entries of measurements with the average and total number of execution times. The results are
40+
grouped by their endpoint. """
41+
with session_scope() as db_session:
42+
result = db_session.query(FunctionCall.endpoint,
43+
func.count(FunctionCall.execution_time).label('count'),
44+
func.avg(FunctionCall.execution_time).label('average')
45+
).group_by(FunctionCall.endpoint).order_by(desc('count')).all()
46+
db_session.expunge_all()
47+
return result
48+
49+
50+
def get_data():
51+
""" Returns all data in the FunctionCall table, for the export data option. """
52+
with session_scope() as db_session:
53+
result = db_session.query(FunctionCall.endpoint,
54+
FunctionCall.execution_time,
55+
FunctionCall.time,
56+
FunctionCall.version,
57+
FunctionCall.group_by,
58+
FunctionCall.ip).all()
59+
db_session.expunge_all()
60+
return result
61+
62+
63+
def get_data_per_version(version):
64+
""" Returns all data in the FuctionCall table, grouped by their version. """
65+
with session_scope() as db_session:
66+
result = db_session.query(FunctionCall.execution_time, FunctionCall.version). \
67+
filter(FunctionCall.version == version).all()
68+
db_session.expunge_all()
69+
return result
70+
71+
72+
def get_versions(end=None):
73+
with session_scope() as db_session:
74+
result = db_session.query(FunctionCall.version,
75+
func.min(FunctionCall.time).label('startedUsingOn')). \
76+
filter((FunctionCall.endpoint == end) | (end is None)).group_by(FunctionCall.version).order_by(
77+
asc('startedUsingOn')).all()
78+
db_session.expunge_all()
79+
return result
80+
81+
82+
def get_data_per_endpoint(end):
83+
with session_scope() as db_session:
84+
result = db_session.query(FunctionCall.execution_time, FunctionCall.endpoint). \
85+
filter(FunctionCall.endpoint == end).all()
86+
db_session.expunge_all()
87+
return result
88+
89+
90+
def get_endpoints():
91+
with session_scope() as db_session:
92+
result = db_session.query(FunctionCall.endpoint,
93+
func.count(FunctionCall.endpoint).label('cnt')). \
94+
group_by(FunctionCall.endpoint).order_by(asc('cnt')).all()
95+
db_session.expunge_all()
96+
return result

flask-dashboard/dashboard/forms.py renamed to dashboard/forms.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from flask_wtf import FlaskForm
2-
from wtforms import validators
3-
from wtforms import SubmitField, PasswordField, StringField
2+
from wtforms import validators, SubmitField, PasswordField, StringField, SelectMultipleField
43

54

65
class MonitorDashboard(FlaskForm):
@@ -10,7 +9,7 @@ class MonitorDashboard(FlaskForm):
109

1110

1211
class Login(FlaskForm):
13-
""" Used for serving a login form on /{{ link }}/login. """
12+
""" Used for serving a login form. """
1413
name = StringField('Username', [validators.required()])
1514
password = PasswordField('Password', [validators.required()])
1615
submit = SubmitField('Login')
@@ -26,4 +25,4 @@ class ChangeSetting(FlaskForm):
2625

2726
class RunTests(FlaskForm):
2827
""" Used for serving a login form on /{{ link }}/testmonitor. """
29-
submit = SubmitField('Run selected tests')
28+
submit = SubmitField('Run selected tests')

flask-dashboard/main.py renamed to dashboard/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
def get_session_id():
1111
# implement here your own custom function
12-
return "12345"
12+
return "1234"
1313

1414
dashboard.config.get_group_by = get_session_id
1515
dashboard.bind(app=user_app)
File renamed without changes.

flask-dashboard/dashboard/routings/__init__.py renamed to dashboard/routings/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import dashboard.routings.login
1111
import dashboard.routings.setup
1212
import dashboard.routings.result
13+
import dashboard.routings.export_data
14+
import dashboard.routings.measurements
1315

1416
# Provide a secret-key for using WTF-forms
1517
if user_app.secret_key is None:
@@ -25,4 +27,4 @@ def static(filename):
2527
# All rules below are for viewing the dashboard-pages
2628
@blueprint.route('/')
2729
def index():
28-
return redirect(url_for('dashboard.measurements'))
30+
return redirect(url_for('dashboard.measurements', index=0))

dashboard/routings/export_data.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from flask import make_response, render_template, session
2+
from dashboard.security import secure
3+
from dashboard.database.function_calls import get_data
4+
from dashboard import blueprint, config
5+
6+
import datetime
7+
8+
9+
@blueprint.route('/download-csv')
10+
@secure
11+
def download_csv():
12+
csv = "\"ENDPOINT\",\"EXECUTION_TIME\",\"TIME_OF_EXECUTION\",\"VERSION\",\"GROUP_BY\",\"IP_ADDRESS\"\n"
13+
data = get_data()
14+
for entry in data:
15+
csv += "\"{0}\",{1},\"{2}\",\"{3}\",\"{4}\",\"{5}\"\n".format(entry.endpoint, entry.execution_time, entry.time,
16+
entry.version, entry.group_by, entry.ip)
17+
18+
response = make_response(csv)
19+
response.headers["Content-Disposition"] = "attachment; filename=measurements_{0}.csv".format(
20+
str(datetime.datetime.now()).replace(" ", "_").replace(":", "-")[:19])
21+
return response
22+
23+
24+
@blueprint.route('/export-data')
25+
@secure
26+
def export_data():
27+
csv = ["\"ENDPOINT\",\"EXECUTION_TIME\",\"TIME_OF_EXECUTION\",\"VERSION\",\"GROUP_BY\",\"IP_ADDRESS\""]
28+
data = get_data()
29+
for entry in data:
30+
csv.append("\"{0}\",{1},\"{2}\",\"{3}\",\"{4}\",\"{5}\"".format(entry.endpoint, entry.execution_time,
31+
entry.time, entry.version, entry.group_by,
32+
entry.ip))
33+
34+
return render_template('export-data.html', link=config.link, session=session, data=csv)

0 commit comments

Comments
 (0)