From daa1115964839f7638b0f2d37cbbbb5d221e68e7 Mon Sep 17 00:00:00 2001 From: rnsrk Date: Tue, 12 Nov 2024 03:37:02 +0100 Subject: [PATCH] first commit --- .gitignore | 2 + app.py | 120 ++++++++++++++++++ charts/__init__.py | 1 + charts/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 141 bytes .../bar_chart_from_stacked.cpython-312.pyc | Bin 0 -> 2086 bytes .../chart_generator.cpython-312.pyc | Bin 0 -> 1169 bytes charts/__pycache__/charts.cpython-312.pyc | Bin 0 -> 6473 bytes charts/charts.py | 104 +++++++++++++++ static/styles.css | 23 ++++ templates/chart.html | 34 +++++ 10 files changed, 284 insertions(+) create mode 100644 .gitignore create mode 100644 app.py create mode 100644 charts/__init__.py create mode 100644 charts/__pycache__/__init__.cpython-312.pyc create mode 100644 charts/__pycache__/bar_chart_from_stacked.cpython-312.pyc create mode 100644 charts/__pycache__/chart_generator.cpython-312.pyc create mode 100644 charts/__pycache__/charts.cpython-312.pyc create mode 100644 charts/charts.py create mode 100644 static/styles.css create mode 100644 templates/chart.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d743995 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv +data diff --git a/app.py b/app.py new file mode 100644 index 0000000..ea0e5e1 --- /dev/null +++ b/app.py @@ -0,0 +1,120 @@ +from flask import Flask, render_template, redirect, url_for +import pandas as pd +from charts.charts import bar_chart, bar_chart_with_special_legend, point_chart, pie_chart + +app = Flask(__name__) + +# Read the CSV file + +def translate_csv(file, translation_dict): + with open(file, 'r') as f: + text = f.read() + for key, value in translation_dict.items(): + text = text.replace(key, value) + with open('data/umfrage2survey.csv', 'w') as f: + f.write(text) + +translation_dict = { + 'Wie sind Sie auf WissKI aufmerksam geworden?': 'How did you hear about WissKI?', + 'In welchem Bereich verwenden Sie WissKI?': 'In which area do you use WissKI?', + 'Wie lange arbeiten Sie mit WissKI?': 'How long do you work with WissKI?', + 'Bedeutung von Softwarefeatures und Eigenschaften': 'Importance of software features and properties', + 'In welcher Rolle benutzen Sie WissKI?': 'In which role do you use WissKI?', + 'Haben Sie Erfahrung mit anderen Forschungsdaten-Management-Systemen?': 'Do you have experience with other research data management systems?', + 'Haben Sie Zugriff auf kompetenten IT- und Systemadministrationssupport innerhalb Ihres Projektes bzw. Institution?': 'Do you have access to competent IT and system administration support within your project or institution?', + 'Arbeitet Ihre Institution/ Unternehmen das erste Mal mit WissKI?': 'Is this the first time your institution/company has worked with WissKI?', + 'Wie viele Stunden stehen Ihnen für die Arbeit mit WissKI wöchentlich zur Verfügung?': 'How many hours a week do you have available for working with WissKI?', + 'Welchen Community-Angeboten haben Sie mindestens einmal wahrgenommen?': 'Which community offers have you used at least once?', + 'Im Verhältnis zu anderen Forschungsdaten-Management-Systemen verorte ich die Qualität von WissKI als...': 'In relation to other research data management systems, I classify the quality of WissKI as...', + 'Wie wahrscheinlich ist es, dass Sie WissKI als Forschungsdaten-Management-System weiterempfehlen?':'How likely is it that you would recommend WissKI as a research data management system?' +} + +translate_csv('data/Community-Umfrage.csv', translation_dict) + +df = pd.read_csv('data/umfrage2survey.csv', encoding='utf8') +#df = pd.read_csv('data/Community-Umfrage.csv', encoding='utf8') + +plots = {} + +# Normal bar charts +# 1 Wie sind Sie auf WissKI aufmerksam geworden? +# 2 In welchem Bereich verwenden Sie WissKI? +# 25 Haben Sie Zugriff auf kompetenten IT- und Systemadministrationssupport innerhalb Ihres Projektes bzw. Institution? +# 26 Arbeitet Ihre Institution/ Unternehmen das erste Mal mit WissKI? + +bar_charts = [1,2,25,26] +for chart in bar_charts: + question_data = df.iloc[:, chart] + question_plot = bar_chart(question_data, question_data.name) + plots[chart] = (question_data.name, question_plot) + +# Cleaned pie charts +# 23 In welcher Rolle benutzen Sie WissKI? +question_data = df.iloc[:, 23] +for index, value in question_data.items(): + new_value = value.split(' (')[0] + question_data[index] = new_value + +question_plot = pie_chart(question_data, question_data.name) +plots[3] = (question_data.name, question_plot) + +# Normal Pie charts +# 3 Wie lange arbeiten Sie mit WissKI? +# 24 Haben Sie Erfahrung mit anderen Forschungsdaten-Management-Systemen? +# 27 Wie viele Stunden stehen Ihnen für die Arbeit mit WissKI wöchentlich zur Verfügung? +# 28 Welchen Community-Angeboten haben Sie mindestens einmal wahrgenommen? +pie_charts = [3,24,27,28] +for chart in pie_charts: + question_data = df.iloc[:, chart] + question_plot = pie_chart(question_data, question_data.name) + plots[chart] = (question_data.name, question_plot) + +# Point charts +# 9-22 Software Features +median_values = df.iloc[:, 9:23].median() +median_values_cleaned = pd.Series(name='Software Features') +for index, value in median_values.items(): + new_index = index.split(' [')[1].split(']')[0] + median_values_cleaned[new_index] = value + +point_plot = point_chart(median_values_cleaned, 'Software Features') +plots[9] = ('Software Features', point_plot) + + +# Bar charts with median +# 29 Im Verhältnis zu anderen Forschungsdaten-Management-Systemen verorte ich die Qualität von WissKI als... +bar_charts_with_median = [29] +for chart in bar_charts_with_median: + question_data = df.iloc[:, chart] + question_plot = bar_chart_with_special_legend(question_data, question_data.name, 'quality') + plots[chart] = (question_data.name, question_plot) + +bar_charts_with_median = [31] +for chart in bar_charts_with_median: + question_data = df.iloc[:, chart] + question_plot = bar_chart_with_special_legend(question_data, question_data.name, 'recommendation') + plots[chart] = (question_data.name, question_plot) + +plots = dict(sorted(plots.items(), key=lambda x: x[0])) + +# Generate list of charts +charts = [{'id': i+1, 'title': plots[col][0], 'data': plots[col][1]} for i, col in enumerate(plots.keys())] + +@app.route('/') +def index(): + return redirect(url_for('show_chart', chart_id=1)) + +@app.route('/chart/') +def show_chart(chart_id): + chart = next((c for c in charts if c['id'] == chart_id), None) + if not chart: + return redirect(url_for('index')) + + plot_url = chart['data'] + prev_id = chart_id - 1 if chart_id > 1 else len(charts) + next_id = chart_id + 1 if chart_id < len(charts) else 1 + + return render_template('chart.html', plot_url=plot_url, prev_id=prev_id, next_id=next_id, title=chart['title']) + +if __name__ == '__main__': + app.run(debug=True) diff --git a/charts/__init__.py b/charts/__init__.py new file mode 100644 index 0000000..dd48f72 --- /dev/null +++ b/charts/__init__.py @@ -0,0 +1 @@ +# This file can be empty but is required to make the charts directory a package diff --git a/charts/__pycache__/__init__.cpython-312.pyc b/charts/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cba5902e08c642c7964b063b8095bed5086f7483 GIT binary patch literal 141 zcmX@j%ge<81i{Tl=>b6cF^Gc>KC=KtrZZGBXfpb(WGG?+@;-yq{L<0S$j?pHFUl(} z%GOWMEYUAdEOE|AEGjA1PlizO@tJv7fK6rT0|#_QM#gdmDSD5QWRz$$_e^(Uw#Z9w6tRS%USDr@bT*t=QpT03J% zkRv$~5=5<(^aRJC9%z*uIMP$ENbRAPjT}v=RH?n>7B#n=IPf1|9;ah8 z=cTl~#XH~1i!r!ou!qe7c0Bfe464B4?7rr3#l8lmHK|QG>b?+j*E*D=zdg19NSBB@ z;fjUVwlKCXi#hJFoEKsXGU*l?Cn->5p+b6vR|TT)T4%ir%AVc_gf6md?R}9a5M%oV zj%-zai&}x|y0)gZl@h4R&XK5>K-GV17b=blKC)03*@udOC?XIv-cSq*KC)sDCcxL1 zSoPGZ>bx681%h3B#!;PR*EN!CSl1hw0c>f+@H7kx5HbpRo(rzK^Il zD$UcFHAm%Q1yQ|hxrh)%H5V0-2dVanhwq_)s^fmeBU`*+R1NGxkp#RrR4#i3`z-rb zsY*CoU?gjQTuCcboAHB^MW_tRHZ^lOe=ci#`69wJUbbKd2mJe?N`=gfrU$5u37ZR- zU7JuH&6QmrBAU2sxfPVn^9(SX(IwQtl^hFl7kij?=Y7`?vLboeMIP02R=_1xwLOUD zs1}ZbX%|K$OKjpI8s~7%wQ|U9ss@Eh0vJoQ~l~ZLHPP@s$=UeVtYXqjL~NbI&5TrGl5^qVq)cI5jr~UsQ z3dYz3{3^y**+Hm*4KuYox-_~xwlwzSWWzkxGEddbQw{S>%e+)KFEz}|wTais-eq^m zT^(sAhZiSn zP=1d86n{DS+a2qblG{+Uhc^~)tQ=}8hdI8vcyr}UQ#mT|>8A3|wg!3+wt7y~drmZZ zhW-Y!dhC(1VI*3{k-Bl@$%&>h5PCy(V`%lOrg3gh?3FRFt+Ni>aiAJ4rLV5^HI@Fq z;)&(qrQvVR*2XtFIm%ByUBxf-m*cgmrt&#wuPt7C^u>dz*QrCT)L=a|xGFzOJWYI` zY@{-+)JQ!w^89WiHCDUES9yK$`h%$r^I+}v4l5;K8?9qx<@~@4jx(S3vQLR`scYxh zUW6xGYun%$iwFFd9q_9s+>ReXaE0TK*=yJMsN}25Si(qz8-8HquhOXyphFXsU`B#nTh7Rx7|I}BYJf~&6DH)JqG&1S)d~;sPv&3yl6Tkx zl*{fa_)%_b-_;OQp$?1p03d@^PzbnTpVFlRdEe!Nz?$TE^k0178h+p(tU*7@H*3(> z+BK`N*Pkk0zR)>Q&+BS$S6h;gF?t;%ysD&!4#NBHx$rZP3hLh|UGeqp_wt1S&+2Jj zKiu;?%bUhyDctZfJ*`K)?3V1C`DPE{SsojoR(=i=eM>JRD`o6Z!?q?KFgdG{1Z1da zSh_DU&5&tWn8_Q45LTXJf@?M@X_ScFGgPK)j}kGSGbzs)`7*%_ZJ0J?0cubaGs&=7 z=*cF<&cZSaIa&pC6hjWiMHb9z4o=N5C7YVTR?gNj3+FMdXl5C+ppH9{K+d!soInie z8r4W#w3Urgo`pr5)iN<1@pLEn`u#AuZkKS9SPm&B^9D`6(CFN{MyQj_`P6D2TbO9n zCigcsS={yTY6n9`IGcFL&POh4-~xqSFV+_u&n`pJX7ngD(h5g+l^vy7-d0Y-qb()g zoH|m*yl6#4$1Z!Alkn&jr&y>jY~vF&bQOv2PVP*;nX1hR=5#&Xl;1u*MR!`hPI5eyEPWlRrh@M?b`m)#RC)I#g3%D#z+5`tY#`Z$7!H%bPD5L^5V$f&Syaq&C+xMb^P6VH;{+!ep8BY?kT2;p}y Qeh!p#5No4;IMgZn1C-71!2kdN literal 0 HcmV?d00001 diff --git a/charts/__pycache__/charts.cpython-312.pyc b/charts/__pycache__/charts.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..926980c4ad854964368b0fe0c720d214411063d4 GIT binary patch literal 6473 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 IZLX((15eniy8r+H literal 0 HcmV?d00001 diff --git a/charts/charts.py b/charts/charts.py new file mode 100644 index 0000000..ef2aa3c --- /dev/null +++ b/charts/charts.py @@ -0,0 +1,104 @@ +import seaborn as sns +import matplotlib.pyplot as plt +import io +import base64 +import pandas as pd + +def bar_chart(data, title): + data = data.str.split(';').explode().value_counts() + fig, ax = plt.subplots(figsize=(12,6)) + palette = sns.color_palette("husl", len(data)) + sns.barplot(x=data.index, y=data.values, ax=ax, hue=data.index, palette=palette, legend=False) + ax.set_xlabel('Answer') + ax.set_ylabel('Count') + + # Rotate the x-axis labels and set their horizontal alignment + for tick in ax.get_xticklabels(): + tick.set_rotation(45) + tick.set_horizontalalignment('right') + tick.set_fontsize(12) + + fig.tight_layout() + + img = io.BytesIO() + fig.savefig(img, format='png', dpi=300, bbox_inches='tight', pad_inches=0.5) + img.seek(0) + plot_url = base64.b64encode(img.getvalue()).decode('utf8') + plt.close() + return plot_url + +def bar_chart_with_special_legend(data, title, scale='recommendation'): + if scale == 'recommendation': + data = data.map({1:'not likely', 2:'less likely', 3:'not sure', 4:'more likely', 5:'definitely'}) + + # create a value counts series + data_counts = data.value_counts() + + fig, ax = plt.subplots(figsize=(12,6)) + palette = sns.color_palette("husl", 5) + sns.barplot(x=data_counts.index, y=data_counts.values, ax=ax, hue=data_counts.index, palette=palette) + ax.set_xlabel('Answer') + ax.set_ylabel('Count') + + # set y-axis to only have integer values + ax.yaxis.set_major_locator(plt.MultipleLocator(1.0)) + + # Rotate the x-axis labels and set their horizontal alignment + for tick in ax.get_xticklabels(): + tick.set_rotation(45) + tick.set_horizontalalignment('right') + tick.set_fontsize(12) + + fig.tight_layout() + + img = io.BytesIO() + fig.savefig(img, format='png', dpi=300, bbox_inches='tight', pad_inches=0.5) + img.seek(0) + plot_url = base64.b64encode(img.getvalue()).decode('utf8') + plt.close() + return plot_url + +def pie_chart(data, title): + data = data.str.split(';').explode().value_counts() + fig, ax = plt.subplots(figsize=(12,6)) + ax.pie(data.values, labels=data.index, autopct='%1.1f%%') + fig.tight_layout() + img = io.BytesIO() + fig.savefig(img, format='png', dpi=300, bbox_inches='tight', pad_inches=0.5) + img.seek(0) + plot_url = base64.b64encode(img.getvalue()).decode('utf8') + plt.close() + return plot_url + +def point_chart(data, title): + # Count the occurrences of each value + + # Map the x-axis values to the appropriate string + data = data.map({1:'not important', 2:'less important', 3:'important', 4:'more important', 5:'mandatory'}) + + # Create a figure and axis + fig, ax = plt.subplots() + + # Plot your data + for answer, value in data.items(): + ax.plot([value], [answer], marker='o') + + # Set the x-axis ticks and labels + ax.set_xticks(list(set(data.values))) + ax.set_xticklabels(list(set(data.values)), rotation=45) + + # Set the y-axis limits to remove the axis + ax.set_yticks(list(data.keys())) + + # Set the font size of the y-axis + for tick in ax.get_yticklabels(): + tick.set_fontsize(9) + + fig.tight_layout() + img = io.BytesIO() + fig.savefig(img, format='png', dpi=300, bbox_inches='tight', pad_inches=0.5) + img.seek(0) + plot_url = base64.b64encode(img.getvalue()).decode('utf8') + plt.close() + return plot_url + diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..0c13d88 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,23 @@ +body { + font-family: Arial, sans-serif; + text-align: center; +} + +img { + width: 80%; + height: auto; +} + +div { + margin-top: 20px; +} + +a { + text-decoration: none; + font-size: 20px; +} + +.navigation { + display: flex; + justify-content: space-around; +} diff --git a/templates/chart.html b/templates/chart.html new file mode 100644 index 0000000..9e33141 --- /dev/null +++ b/templates/chart.html @@ -0,0 +1,34 @@ + + + + + + {{ title }} + + + + + + + + + +