1 # Mattermost Bot module.
2 # Copyright (c) 2016-2022 by Someone <someone@somenet.org> (aka. Jan Vales <jan@jvales.net>)
3 # published under MIT-License
5 from inspect import cleandoc
11 # pylint: disable=wrong-import-position
12 from AbstractCommand import AbstractCommand
13 class DialogManagedCourseFeedback(AbstractCommand):
14 TRIGGER = "course-feedback"
15 CONFIG = {"display_name": "somebot-command", "auto_complete": True,
16 "auto_complete_hint": "['--help']",
18 CONFIG["auto_complete_desc"] = CONFIG["description"] = AbstractCommand.ICON_PRIVATE+"Run in course-channel. Opens dialog to edit/delete course-feedback."
21 def __init__(self, team_id, datadir):
22 super().__init__(team_id)
24 self.datadir = datadir
27 def on_POST(self, request, data):
28 if re.match(r"^(-|—|–)+help$", data["text"]):
29 request.respond_cmd_temp(cleandoc("""
30 ## :pencil:``Course feedback!``:rocket:
31 We want your feedback in order to find out which courses need to be improved asap to achieve the excellence we are accustomed to, or better.
32 The feedback will be anonymized during aggregation, then analyzed and discussed with the appropriate people like the course admins, the deans of study affairs and the vice rector for teaching matters and/or people who can give legal advice about course rules.
35 + Was there an online option and how was it?
36 + How much time did you spent on this course?
37 + This course's modalities - describe them in a few words. (Also consider updating the info in VoWi - its a work resource for us and we use it to represent you)
38 + How did you like the modalities of this course? (Would you want it to be applied to others?)
39 + Overall feedback for this course.
40 + Feedback about this survey for :fsinf:. (This will not be shared outside of FSInf)
42 :information_source: *We are your legally appointed representatives - we will never forward any personal info about you without your explicit permission.* :)
46 c = self.bot.api.get_channel(data["channel_id"])
48 if c["type"] != 'O' or c["name"] in ["town-square", "off-topic"] or c["display_name"][0] == "=":
49 request.respond_cmd_err("``/"+self.TRIGGER+"`` must be run in a course-channel.")
52 self._open_dialog(request, data)
53 request.respond_cmd_temp("## :white_check_mark: Success! :)\n#### Opened dialog.")
56 def on_POST_interactive(self, request, data):
58 # print("on_POST_interactive")
61 self._open_dialog(request, data)
65 def on_POST_dialog(self, request, data):
67 # print("on_POST_dialog")
70 if data["submission"]["feedback_hours"] and int(data["submission"]["feedback_hours"]) < 0:
71 request.respond(200, {"errors": {"feedback_hours": "Hours must be positive."}})
74 if data["submission"]["feedback_hours"] and int(data["submission"]["feedback_hours"]) > 4464:
75 request.respond(200, {"errors": {"feedback_hours": "Hours must be smaller than 4465. Did you really spend EVERY HOUR of the semester for this course? Did you not sleep at all?"}})
79 c = self.bot.api.get_channel(data["channel_id"])
81 self._response_store(self.datadir+data["callback_id"], "-feedback_distance.txt", data["submission"]["feedback_distance"], c["name"])
82 self._response_store(self.datadir+data["callback_id"], "-feedback_course.txt", data["submission"]["feedback_course"], c["name"])
83 self._response_store(self.datadir+data["callback_id"], "-feedback_hours.txt", data["submission"]["feedback_hours"], c["name"])
84 self._response_store(self.datadir+data["callback_id"], "-feedback_modus.txt", data["submission"]["feedback_modus"], c["name"])
85 self._response_store(self.datadir+data["callback_id"], "-feedback_modus_like.txt", data["submission"]["feedback_modus_like"], c["name"])
86 self._response_store(self.datadir+data["callback_id"], "-feedback_fsinf.txt", data["submission"]["feedback_fsinf"], c["name"])
88 request.respond(200, {})
91 def _open_dialog(self, request, data):
92 callback_id = "res-"+data["team_id"]+"-"+data["channel_id"]+"-"+data["user_id"]
94 feedback_distance = self._response_load(self.datadir+callback_id, "-feedback_distance.txt")
95 feedback_course = self._response_load(self.datadir+callback_id, "-feedback_course.txt")
96 feedback_hours = self._response_load(self.datadir+callback_id, "-feedback_hours.txt")
97 feedback_modus = self._response_load(self.datadir+callback_id, "-feedback_modus.txt")
98 feedback_modus_like = self._response_load(self.datadir+callback_id, "-feedback_modus_like.txt", "0")
99 feedback_fsinf = self._response_load(self.datadir+callback_id, "-feedback_fsinf.txt")
102 "callback_id": callback_id,
103 "title": "Course feedback",
104 "submit_label":"Submit",
106 "display_name": "Was there an online option? Was it announced? Was it possible to complete the course only online? (Max. 3000 characters)",
107 "placeholder": "Type here. Max. 3000 characters.\n+ What did you think about the online option?\n+ Was it announced?\n+ Was it possible to complete the course only online?",
108 "name": "feedback_distance",
110 "help_text": "All your imput on all questions will be anonymized during aggregation. Aggregated input will be analyzed by FSInf and further used/forwarded to the course admins, the deans of study affairs, the vice rector for teaching and possibly others that are deemed able to fix things. You can edit/clear it at any time.",
112 "default": feedback_distance
114 "display_name": "How many hours did you invest into this course?",
115 "placeholder": "Hours. Positive integer.",
116 "name": "feedback_hours",
120 "default": feedback_hours
122 "display_name": "Describe the modalities (issues encountered and possible improvements) in a few words (Max. 3000 characters)",
123 "placeholder": "Type here. Max. 3000 characters.\n+ What was it? \n+ Were deadlines announced in a timely manner?\n+ Was the grading surprising?\n+ Random changes during the semester?",
124 "name": "feedback_modus",
127 "default": feedback_modus
129 "display_name": "How did you like the modalities of this course? (Do you want it to be applied to others?)",
130 "placeholder": "Type here. Max. 3000 characters.",
131 "name": "feedback_modus_like",
134 {"text": "I loved it", "value": "2" },
135 {"text": "I liked it", "value": "1" },
136 {"text": "I'm indifferent/Don't want to answer (default)", "value": "0" },
137 {"text": "I disliked it", "value": "-1" },
138 {"text": "I hated it", "value": "-2" },
141 "default": feedback_modus_like
143 "display_name": "Other comment/feedback about the course (Max. 3000 characters)",
144 "placeholder": "Type here. Max. 3000 characters.\n+ What should be changed?\n+ What was unfair?\n+ What was really good?",
145 "name": "feedback_course",
148 "default": feedback_course
150 "display_name": "Feedback (about this survey) for FSInf (Max. 3000 characters)",
151 "placeholder": "Type here. Max. 3000 characters.",
152 "name": "feedback_fsinf",
154 "help_text": "Your input will be anonymized during aggregation. Aggregated input will be analyzed by FSInf. We will not forward this text to anyone. You can edit/clear it at any time.",
156 "default": feedback_fsinf
160 self.bot.api.open_dialog(data["trigger_id"], self.URL+"/dialog", dialog)
163 def _response_load(self, path, filename, default=""):
166 if os.path.isfile(path):
167 with open(path, "r") as f:
172 def _response_store(self, path, filename, data, channelname=""):
174 data = str(data).strip()
175 if data == "" or data == "None" or data == "0":
176 if os.path.isfile(path):
178 self.bot.debug_chan("``course-feedback::"+channelname+filename+"`` result deleted.")
180 with open(path, "w") as f:
182 self.bot.debug_chan("``course-feedback::"+channelname+filename+"`` result stored.\n```\n"+data+"\n```")