]> code.communitydata.science - coldcallbot-discord.git/commitdiff
Initial version of coldcalling app
authorJeremy Foote <jdfoote1@gmail.com>
Thu, 2 Mar 2023 20:16:39 +0000 (15:16 -0500)
committerJeremy Foote <jdfoote1@gmail.com>
Thu, 2 Mar 2023 20:16:39 +0000 (15:16 -0500)
.gitignore [new file with mode: 0644]
app.py [new file with mode: 0644]
static/main.css [new file with mode: 0644]
static/process_button.js [new file with mode: 0644]
templates/cold_caller.html [new file with mode: 0644]
templates/group_maker.html [new file with mode: 0644]
templates/shuffler.html [new file with mode: 0644]
test.csv [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..b04e720
--- /dev/null
@@ -0,0 +1,3 @@
+config.py
+__pycache__/
+*.pyc
diff --git a/app.py b/app.py
new file mode 100644 (file)
index 0000000..c113e45
--- /dev/null
+++ b/app.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+
+import pandas as pd
+from random import choices, shuffle
+from datetime import datetime
+import csv
+import os
+from flask import Flask, render_template, request, abort
+
+app = Flask(__name__)
+
+@app.route("/")
+def hello_world():
+    return "<p>Hello, Test!</p>"
+
+
+@app.route('/response_quality', methods=['POST'])
+def response_quality():
+    student_name = request.form['studentName']
+    button_value = request.form['buttonValue']
+    course = request.form['course']
+
+    fn = f'../assessments/{course}/{course}.csv'
+
+    if button_value == 'absent':
+        answered = 'F'
+        button_value = ''
+    else:
+        answered = 'T'
+
+    write_to_file(student_name, fn,
+            answered=answered,
+            assessment=button_value)
+
+    return 'Received feedback from ' + student_name + ': ' + button_value
+
+@app.route("/coldcaller/<course>", methods=['POST','GET'])
+def coldcaller(course):
+    if course not in ["com_304","com_411","com_674"]:
+        abort(404)
+    weight = 2
+    students = pd.read_csv(f'../assessments/{course}/{course}_students.csv').Name
+    student = ''
+    out_fn = f'../assessments/{course}/{course}.csv'
+    caller = Caller(out_fn, students, weight)
+    if request.method == "POST":
+        student = caller.get_random_student()
+    return render_template('cold_caller.html', student=student)
+
+@app.route("/shuffler", methods=['POST','GET']) 
+def shuffler():
+    course = request.args.get('course')
+    try:
+        student_list = pd.read_csv(f'../assessments/{course}/{course}_students.csv').Name
+    except FileNotFoundError:
+        abort(404)
+    shuffle(student_list)
+    print(student_list)
+    return render_template('shuffler.html', result=student_list)
+
+@app.route("/make_groups", methods=['POST','GET'])
+def make_groups():
+    course = request.args.get('course')
+    group_size = int(request.args.get('group_size'))
+    print('running')
+    try:
+        student_list = pd.read_csv(f'../assessments/{course}/{course}_students.csv').Name
+    except FileNotFoundError:
+        abort(404)
+    shuffle(student_list)
+    print(student_list)
+    print(range(0,len(student_list)//group_size + 1, group_size))
+    result = []
+    j = 1
+    for i in range(0,len(student_list), group_size):
+        result.append((j, student_list[i:i+group_size]))
+        j += 1
+    return render_template('group_maker.html', result=result)
+
+
+
+class Caller:
+
+    def __init__(self, out_fn, students, weight = 2):
+        self.weight = weight
+        self.fn = out_fn
+        self.students = students
+        self.last_chosen = None
+        self.today = datetime.now().date()
+        self.weights_dict = self.get_weights()
+
+    def get_weights(self):
+        times_called = self.get_times_called()
+        weights_dict = {}
+        for student in self.students:
+            try:
+                curr_tc = times_called[student]
+            except KeyError:
+                curr_tc = 0
+            student_weight = (1/self.weight) ** curr_tc
+            weights_dict[student] = student_weight
+        return weights_dict
+
+
+    def get_times_called(self):
+        try:
+            df = pd.read_csv(self.fn)
+            if len(df) > 0:
+                self.last_chosen = df.name.iloc[-1]
+            df.date = pd.to_datetime(df.date).dt.date
+            times_called = df[(df.answered.isin(['T','TRUE']))|(df.date==self.today)].groupby('name').size()
+            self.absent_today = df.loc[(df.date==self.today) & (df.answered.isin(['F', 'FALSE'])), 'name']
+        except FileNotFoundError or IndexError:
+            times_called = pd.DataFrame()
+        return times_called
+
+    def update_weight(self, student):
+        self.weights_dict[student] /= self.weight
+
+    def get_random_student(self, can_repeat=False):
+        if not can_repeat:
+            curr_weights = {k:v for k,v in self.weights_dict.items() if k != self.last_chosen}
+        else:
+            curr_weights = self.weights_dict
+        for student in set(self.absent_today):
+            print(curr_weights.keys())
+            print(student in curr_weights)
+            if student != self.last_chosen:
+                del curr_weights[student]
+        rand_student = choices(list(curr_weights.keys()), weights=list(curr_weights.values()), k=1)[0]
+        print(f"Weight of {rand_student}: {curr_weights[rand_student]}")
+        self.update_weight(rand_student)
+        return(rand_student)
+
+def write_to_file(student, fn, answered, assessment):
+    if not os.path.exists(fn):
+        with open(fn, 'w') as f:
+            f.write(','.join(['name', 'date', 'answered', 'assessment']))
+            f.write('\n')
+    with open(fn, 'a') as f:
+        out_csv = csv.writer(f)
+        out_csv.writerow([student,datetime.now().date(),answered,assessment])
+
+
diff --git a/static/main.css b/static/main.css
new file mode 100644 (file)
index 0000000..e06524c
--- /dev/null
@@ -0,0 +1,25 @@
+body {
+  background: Linen;
+  margin-top: 50px;
+  margin-left: 100px;
+       margin-right: 100px;
+       font-family: Georgia, serif;
+  color: DarkSlateGray;
+       font-size: 1.3em;
+  }
+
+p {
+  font-family: Georgia, serif;
+  font-size: 1em;
+  color: DarkSlateGray;
+  }
+
+h1 {
+  font-family: Verdana, Geneva, sans-serif;
+  font-size: 2.5em;
+  color: FireBrick;
+  }
+
+.rand-button {
+       font-size: .8em;
+}
diff --git a/static/process_button.js b/static/process_button.js
new file mode 100644 (file)
index 0000000..6bea8a2
--- /dev/null
@@ -0,0 +1,23 @@
+$(document).ready(function() {
+   $('#goodButton, #badButton, #neutralButton, #absentButton').on('click', function() {
+       var studentName = $('#studentName').text();
+       console.log(studentName);
+       var buttonValue = $(this).val();
+       var courseCode = window.location.pathname.split('/').pop();
+       $.ajax({
+           url: '/response_quality',
+           type: 'POST',
+           data: {
+              studentName: studentName,
+              buttonValue: buttonValue,
+              course: courseCode
+           },
+           success: function(response) {
+              console.log(response);
+           },
+           error: function(error) {
+              console.log(error);
+           }
+       });
+   });
+});
diff --git a/templates/cold_caller.html b/templates/cold_caller.html
new file mode 100644 (file)
index 0000000..0a149fd
--- /dev/null
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Random Student Picker</title>
+    <link rel="stylesheet" href='/static/main.css' />
+    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
+  <script src="{{ url_for('static', filename='process_button.js') }}"></script>
+</head>
+<body>
+<h3>
+The next student is:
+</h3>
+
+<h2 id='studentName' name='studentName'>{{student}}</h2>
+
+<form method="post" id="todo-form">
+        <button class='rand-button' type="submit">Get random student</button>
+
+</form>
+
+
+<button class='assessment' id="goodButton" value="G">Good</button>
+<button class='assessment' id="badButton" value="B">Bad</button>
+<button class='assessment' id="neutralButton" value="M">Neutral</button>
+<button class='assessment' id="absentButton" value="absent">Absent</button>
+
+
+</body>
+</html>
diff --git a/templates/group_maker.html b/templates/group_maker.html
new file mode 100644 (file)
index 0000000..b065016
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Random Student Picker</title>
+    <link rel="stylesheet" href='/static/main.css' />
+</head>
+<body>
+<h3>
+  Groups:
+</h3>
+
+{% for group in result %}
+<h2>Group {{group[0]}}</h2>
+
+<ul>
+  {% for member in group[1] %}
+  <li> {{member}} </li>
+  {% endfor %}
+</ul>
+
+{% endfor %}
+
+
+</body>
+</html>
diff --git a/templates/shuffler.html b/templates/shuffler.html
new file mode 100644 (file)
index 0000000..6f8b67d
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Shuffled List</title>
+    <link rel="stylesheet" href='/static/main.css' />
+</head>
+<body>
+<h3>
+  Shuffled List:
+</h3>
+
+<ul>
+  {% for member in result %}
+  <li> {{member}} </li>
+  {% endfor %}
+</ul>
+
+</body>
+</html>
diff --git a/test.csv b/test.csv
new file mode 100644 (file)
index 0000000..dfe743c
--- /dev/null
+++ b/test.csv
@@ -0,0 +1,71 @@
+name,date,answered,assessment
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r
+owen,2022-01-01,,\r
+dad,2022-01-01,,\r
+dad,2022-01-01,,\r

Community Data Science Collective || Want to submit a patch?