diff --git a/reaction-master/#reaction.py# b/reaction-master/#reaction.py# deleted file mode 100755 index dacff98c7dc461a31ca032ab31d9f3a2828425e0..0000000000000000000000000000000000000000 --- a/reaction-master/#reaction.py# +++ /dev/null @@ -1,269 +0,0 @@ -#! /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()