Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
Communications Challenge
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
edugain
Communications Challenge
Commits
8533dd4d
Commit
8533dd4d
authored
11 months ago
by
Tobias Dussa
Browse files
Options
Downloads
Patches
Plain Diff
Cleanup.
parent
c51c41d4
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
reaction-master/#reaction.py#
+0
-269
0 additions, 269 deletions
reaction-master/#reaction.py#
with
0 additions
and
269 deletions
reaction-master/#reaction.py#
deleted
100755 → 0
+
0
−
269
View file @
c51c41d4
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# reaction
# Copyright (C) 2021 EGI-IRTF
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
<https:
//
www.gnu.org
/
licenses
/>
.
from datetime import datetime, timedelta
try:
from jinja2 import Template
jinja2 = True
except:
jinja2 = False
import glob
import io
import os
try:
import matplotlib.pyplot as plot
matplotlib = True
except:
matplotlib = False
BASEDIR = 'stats/'
if jinja2:
LATEXTEMPLATE = Template(r'''
<pre>
\documentclass{article}
\usepackage{tikz}
\usepackage{pgfplots}
\pgfplotsset{width=\textwidth,compat=1.16}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
title={Communication Challenge -- {{title}}},
xlabel={Time [hours]},
ylabel={Reactions [number of participants]},
xmin=0, xmax={{xmax}},
ymin=0, ymax={{ymax}},
legend pos=south east,
ymajorgrids=true,
grid style=dashed,
]
\addplot[color=blue, mark=square]
coordinates { {{coordinates}} };
\addlegendentry{ {{responses}} participants ({{percentageResponses}}\,\%) have reacted}
\addplot[color=red]
coordinates { (0, {{sitesNumber}})({{xmax}}, {{sitesNumber}}) };
\addlegendentry{ {{sitesNumber}} participants have been challenged}
\addplot[color=green]
coordinates { (24, 0)(24, {{ymax}}) };
\addlegendentry{ {{quickSitesNumber}} participants ({{percentageQuick}}\,\%) have reacted within 24 h}
\end{axis}
\end{tikzpicture}
\end{document}
</pre>
''')
def floatPart(string):
return(float(string.split('/')[-1].split('-', 1)[0]))
def hashtagPart(string):
return(string.split('/')[-1].split('-', 1)[-1])
class ReactionApp(object):
def __init__(self, environ, start_response):
self.environ = environ
self.start = start_response
def format(self, code, msg):
self.start(code, [('Content-type', 'text/html; charset=UTF-8')])
return '
<html><body><p>
{msg}
</p></body></html>
'.format(msg=msg).encode('UTF-8')
def image(self, code, type, image):
self.start(code, [('Content-type', 'image/{}'.format(type))])
return image
def updatefile(self, folder, hashtag):
with open(folder + hashtag, 'a') as tagfile:
tagfile.write(str((datetime.now()-datetime(1970, 1, 1)).total_seconds()) + '\n')
def checkresults(self, folder, hashtag):
if os.path.isfile(folder + '.LOCKED'):
return self.format('200 OK', 'This campaign has been closed!')
with open(folder + hashtag, 'r') as tagfile:
lines = tagfile.read().splitlines()
if len(lines)
<
2
:
return
self.format
('500
Internal
Server
Error
',
'
Unfortunately
,
this
campaign
is
broken
!')
elapsedtime =
datetime.fromtimestamp(float(lines[1]))
-
datetime.fromtimestamp
(
float
(
lines
[0]))
if
not
os.path.isfile
(
folder
+
str
(
elapsedtime.total_seconds
())
+
'
-
'
+
hashtag
)
:
open
(
folder
+
str
(
elapsedtime.total_seconds
())
+
'
-
'
+
hashtag
,
'
a
').
close
()
responses =
glob.glob(folder
+
'
*-*
')
responses.sort(key=
floatPart)
responses =
list(map(hashtagPart,
responses
))
message =
'<p>Reaction time: '
+
str
(
elapsedtime
)
+
'</
p
>
'
return self.format('200 OK', message)
def gatherdata(self, folder):
if not os.path.isdir(folder):
return False
responseFiles = glob.glob(folder + '*-*')
data = {}
data['title'] = folder.split('/')[-2]
data['sites'] = set([hashtagPart(x) for x in glob.glob(folder + '*')])
data['sitesNumber'] = len(data['sites'])
data['responses'] = len(responseFiles)
data['responseTimes'] = {hashtagPart(x): floatPart(x) for x in responseFiles}
data['responsePoints'] = {hashtagPart(x): os.path.getctime(x) for x in responseFiles}
data['missingSites'] = list(filter(lambda x: x not in data['responseTimes'], data['sites']))
data['missingSitesNumber'] = len(data['missingSites'])
data['quickSites'] = list(filter(lambda x: x
<
(24
*3600
),
data
['
responseTimes
'].
values
()))
data
['
quickSitesNumber
'
] =
len(data['quickSites'])
data
['
percentageResponses
'
] =
round(100*data['responses']/len(data['sites']))
data
['
percentageQuick
'
] =
round(100*len(data['quickSites'])/len(data['sites']))
return
data
def
getresults
(
self
,
folder
)
:
ReactionApp.getresults.rank =
0
def
steprank
()
:
ReactionApp.getresults.rank
+=
1
return
(
ReactionApp.getresults.rank
)
if
not
os.path.isdir
(
folder
)
:
return
self.format
('404
Not
Found
',
'
This
campaign
does
not
exist
!')
data =
self.gatherdata(folder)
message =
'<h1>Communication Challenge — {title}'
.
format_map
(
data
)
message
+=
'
—
{
status
}
Results
</
h1
>
'
message += '{responses} participants ({percentageResponses} %) have reacted
<br>
'.format_map(data)
message += '{sitesNumber} participants have been challenged
<br>
'.format_map(data)
message += '{quickSitesNumber} participants ({percentageQuick} %) have reacted within 24 h
<br>
'.format_map(data)
message += '
<h2>
Reaction Times
</h2>
'
message += '
<table
cellspacing=
"2"
cellpadding=
"5"
bgcolor=
"#000000"
><tr
bgcolor=
"#ffffff"
><th
align=
"left"
>
Sequence #
</th><th
align=
"left"
>
Timestamp
</th><th
align=
"left"
>
Participant
</th><th
align=
"left"
>
Reaction Time
</th></tr>
'
message += '
<tr
bgcolor=
"#ffffff"
>
' + '
</tr><tr
bgcolor=
"#ffffff"
>
'.join(map(lambda x: '
<td
align=
"right"
>
'+str(steprank())+'
</td><td>
'+datetime.fromtimestamp(data['responsePoints'][x], tz=datetime.now().astimezone().tzinfo).isoformat()+'
</td><td><code>
'+x+'
</code></td><td
align=
"right"
>
'+str(timedelta(seconds=data['responseTimes'][x]))+'
</td>
', sorted(data['responseTimes'].keys(), key=lambda x: data['responseTimes'][x]))) + '
</tr></table>
'
if (data['missingSitesNumber'] > 0):
message += '
<h2>
Missing Reactions
</h2>
'
message += '
<ul><li><code>
' + '...
</code></li><li><code>
'.join(map(lambda x: x[0:48], sorted(data['missingSites']))) + '...
</code></li></ul>
'
status = 'Preliminary'
else:
status = 'Final'
if os.path.isfile(folder + '.LOCKED'):
status = 'Final'
return self.format('200 OK', message.format(status=status))
usa def getlatex(self, folder):
if not os.path.isdir(folder):
return self.format('404 Not Found', 'This campaign does not exist!')
data = self.gatherdata(folder)
responseTimes = [round(x/3600, 3) for x in data['responseTimes'].values()]
responseTimes.sort()
data['coordinates'] = '(0, 0)'
data['coordinates'] += ''.join(['('+str(responseTimes[x])+', '+str(x+1)+')' for x in range(data['responses'])])
data['xmax'] = round(max(responseTimes) * 1.2, -1)
data['ymax'] = round(data['sitesNumber'] * 1.2, -1)
return self.format('200 OK', LATEXTEMPLATE.render(data))
def getgraph(self, folder, format):
if not os.path.isdir(folder):
return self.format('404 Not Found', 'This campaign does not exist!')
data = self.gatherdata(folder)
responseTimes = [round(x/3600, 3) for x in data['responseTimes'].values()]
responseTimes.append(0)
responseTimes.sort()
xmax = round(max(responseTimes) * 1.2, -1)
ymax = round(data['sitesNumber'] * 1.2, -1)
plot.figure()
plot.plot(responseTimes, range(data['responses']+1), marker='.', color='blue', label='{responses} participants ({percentageResponses} %) have reacted'.format_map(data), zorder=3)
plot.axhline(y=data['sitesNumber'], color='red', label='{sitesNumber} participants have been challenged'.format_map(data), zorder=2)
plot.axvline(x=24, color='lightgreen', label='{quickSitesNumber} participants ({percentageQuick} %) have reacted within 24 h'.format_map(data), zorder=1)
plot.title('Communication Challenge — {title}'.format_map(data))
plot.xlabel('Time [hours]')
plot.ylabel('Reactions [number of participants]')
plot.legend(loc='lower right')
plot.plot
with io.BytesIO() as buffer:
plot.savefig(buffer, format=format)
buffer.seek(0)
return self.image('200 OK', format, buffer.read())
def process(self):
path = self.environ['PATH_INFO'].split('/')[1:]
try:
prefix, hashtag = path[0].rsplit('-', 1)
except ValueError:
prefix = ''
hashtag = path[0]
try:
int(hashtag, 16)
except (KeyError, ValueError):
return self.format('400 Bad Request', 'This is not a valid link!')
if prefix:
folder = os.path.abspath(BASEDIR + prefix) + '/'
if not folder.startswith(BASEDIR):
self.format('400 Bad Request', 'Good try!')
else:
folder = BASEDIR
if not os.path.isfile(folder + hashtag):
if len(path) == 2 and path[1] == 'create':
if not os.path.isdir(folder):
os.makedirs(folder)
self.updatefile(folder, hashtag)
return self.format('200 OK', 'File {tag} created'.format(tag=hashtag))
elif len(path) != 1:
return self.format('400 Bad Request', 'What are you trying to do?')
elif not os.path.isdir(folder):
return self.format('404 Not Found', 'This campaign does not exist!')
else:
return self.format('404 Not Found', 'This id does not exist!')
if 'HTTP_REFERER' in self.environ:
referer = self.environ['HTTP_REFERER']
else:
referer = ''
if 'HTTPS' in self.environ and self.environ['HTTPS']:
url = 'https://'
else:
url = 'http://'
url += '{host}{URI}'.format(host=self.environ['HTTP_HOST'],
URI=self.environ['REQUEST_URI'])
if referer == url:
self.updatefile(folder, hashtag)
return self.checkresults(folder, hashtag)
elif len(path) == 2:
if path[1] == 'results':
return self.getresults(folder)
elif path[1] == 'graph' and matplotlib:
return self.getgraph(folder, 'png')
elif path[1] == 'latex' and jinja2:
return self.getlatex(folder)
else:
return self.format('400 Bad Request', 'Nice try!')
elif len(path) == 3 and path[1] == 'graph' and matplotlib:
if path[2] == 'jpg':
return self.getgraph(folder, 'jpg')
elif path[2] == 'pdf':
return self.getgraph(folder, 'pdf')
elif path[2] == 'png':
return self.getgraph(folder, 'png')
elif path[2] == 'svg':
return self.getgraph(folder, 'svg')
else:
return self.format('400 Bad Request', 'Nice try!')
elif os.path.isfile(folder + '.LOCKED'):
return self.format('200 OK', 'This campaign has been closed!')
else:
return self.format('200 OK', 'Please click
<a
href=
"{URL}"
>
here
</a>
to confirm your submission!'.format(URL=url))
def __iter__(self):
yield self.process()
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment