Commit bbc1c50606a4da44b9782157d5ddea36e0afcf11
1 parent
f75a344f
Exists in
master
and in
1 other branch
- documentation update: setting up letsencrypt certificates.
- fixed duplicate generation of questions. - fixed crash when all topics are solved.
Showing
3 changed files
with
50 additions
and
32 deletions
Show diff stats
BUGS.md
| 1 | + | |
| 1 | 2 | BUGS: |
| 2 | 3 | |
| 3 | 4 | - guardar state cada vez que topico termina |
| 4 | -- logs mostram que está a gerar cada pergunta 2 vezes...?? | |
| 5 | 5 | - reload da página rebenta o estado. |
| 6 | 6 | - indicar o topico actual no sidebar |
| 7 | 7 | - session management. close after inactive time. |
| ... | ... | @@ -9,7 +9,6 @@ BUGS: |
| 9 | 9 | |
| 10 | 10 | TODO: |
| 11 | 11 | |
| 12 | -- letsencrypt.org | |
| 13 | 12 | - logs de debug devem indicar o user. |
| 14 | 13 | - implementar http com redirect para https. |
| 15 | 14 | - topicos no sidebar devem ser links para iniciar um topico acessivel. os inacessiveis devem estar inactivos. |
| ... | ... | @@ -17,8 +16,10 @@ TODO: |
| 17 | 16 | - mostrar comments quando falha a resposta |
| 18 | 17 | - generators not working: bcrypt (ver blog) |
| 19 | 18 | |
| 20 | -SOLVED: | |
| 19 | +FIXED: | |
| 21 | 20 | |
| 21 | +- logs mostram que está a gerar cada pergunta 2 vezes...?? | |
| 22 | +- letsencrypt.org | |
| 22 | 23 | - alterar password. |
| 23 | 24 | - barra de progresso a funcionar |
| 24 | 25 | - mostra tópicos do lado esquerdo, indicando quais estão feitos | ... | ... |
README.md
| ... | ... | @@ -71,12 +71,28 @@ First we need to create a database: |
| 71 | 71 | |
| 72 | 72 | ### SSL Certificates |
| 73 | 73 | |
| 74 | +#### Selfsigned | |
| 74 | 75 | We also need certificates for https. Generate selfsigned certificates using openssl: |
| 75 | 76 | |
| 76 | 77 | openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes |
| 77 | 78 | |
| 78 | 79 | and place them in `aprendizations/certs`. |
| 79 | 80 | |
| 81 | +#### LetsEcrypt | |
| 82 | + | |
| 83 | + sudo pkg install py27-certbot # FreeBSD | |
| 84 | + | |
| 85 | +Shutdown any server running and the firewall, and then run the script to generate the certificate: | |
| 86 | + | |
| 87 | + sudo pfctl -d # disable pf firewall | |
| 88 | + sudo certbot certonly --standalone -d bit.xdi.uevora.pt | |
| 89 | + sudo pfctl -e; sudo pfctl -f /etc/pf.conf # enable pf firewall | |
| 90 | + | |
| 91 | +Certificates are saved under `/usr/local/etc/letsencrypt/live/bit.xdi.uevora.pt/` which is readable only by root. | |
| 92 | + | |
| 93 | +Copy them to `aprendizations/certs` with names `cert.pem` and `key.pem`. And change permissions to be readble (FIXME how to do it with security?) | |
| 94 | + | |
| 95 | + | |
| 80 | 96 | ### Testing |
| 81 | 97 | |
| 82 | 98 | Run a demonstration: |
| ... | ... | @@ -126,16 +142,16 @@ To correct in FreeBSD, edit `~/.login_conf` to use UTF-8, for example: |
| 126 | 142 | |
| 127 | 143 | ## Useful sqlite3 queries |
| 128 | 144 | |
| 129 | -- Which students have already done at least one topic? | |
| 145 | +Which students have already done at least one topic? | |
| 130 | 146 | |
| 131 | 147 | sqlite3 students.db "select distinct student_id from studenttopic" |
| 132 | 148 | |
| 133 | 149 | |
| 134 | -- How many topics have done each student? | |
| 150 | +How many topics have done each student? | |
| 135 | 151 | |
| 136 | 152 | sqlite3 students.db "select student_id, count(topic_id) from studenttopic group by student_id" |
| 137 | 153 | |
| 138 | 154 | |
| 139 | -- What questions have more wrong answers? | |
| 155 | +What questions have more wrong answers? | |
| 140 | 156 | |
| 141 | 157 | sqlite3 students.db "select count(ref), ref from answers where grade<1.0 group by ref order by count(ref) desc" | ... | ... |
knowledge.py
| ... | ... | @@ -20,27 +20,30 @@ class Knowledge(object): |
| 20 | 20 | self.depgraph = depgraph |
| 21 | 21 | self.state = state # {node: level, node: level, ...} |
| 22 | 22 | |
| 23 | - self.topic = self.topic_generator() | |
| 24 | - self.current_topic = next(self.topic) | |
| 23 | + self.topic_sequence = nx.topological_sort(self.depgraph) # FIXME | |
| 25 | 24 | |
| 26 | - self.questions = self.generate_questions_for_topic(self.current_topic) | |
| 27 | - self.current_question = None | |
| 28 | - self.finished_questions = [] | |
| 25 | + # select a topic to do | |
| 26 | + self.new_topic() | |
| 29 | 27 | |
| 30 | 28 | # ------------------------------------------------------------------------ |
| 31 | - def topic_generator(self): | |
| 32 | - self.topic_sequence = nx.topological_sort(self.depgraph) # FIXME for now... | |
| 33 | - for t in self.topic_sequence: | |
| 34 | - if self.state.get(t, 0.0) > 0.999: | |
| 35 | - continue | |
| 36 | - self.questions = self.generate_questions_for_topic(t) | |
| 37 | - logger.info(f'Generated {len(self.questions)} questions for topic "{t}"') | |
| 38 | - yield t | |
| 29 | + def new_topic(self, topic=None): | |
| 30 | + if topic is None: | |
| 31 | + # select the first topic that has level < 0.9 | |
| 32 | + for topic in self.topic_sequence: | |
| 33 | + if self.state.get(topic, 0.0) < 0.9: | |
| 34 | + break | |
| 35 | + | |
| 36 | + # FIXME if all are > 0.9, will stay in the last one forever... | |
| 37 | + self.current_topic = topic | |
| 38 | + self.current_topic_idx = self.topic_sequence.index(topic) | |
| 39 | + self.questions = self.generate_questions_for_topic(topic) | |
| 40 | + self.current_question = None | |
| 41 | + self.finished_questions = [] | |
| 39 | 42 | |
| 40 | 43 | # ------------------------------------------------------------------------ |
| 41 | 44 | def generate_questions_for_topic(self, topic): |
| 42 | - factory = self.depgraph.node[topic]['factory'] | |
| 43 | - return [q.generate() for q in factory] | |
| 45 | + factory_list = self.depgraph.node[topic]['factory'] | |
| 46 | + return [q.generate() for q in factory_list] | |
| 44 | 47 | |
| 45 | 48 | # ------------------------------------------------------------------------ |
| 46 | 49 | def get_current_question(self): |
| ... | ... | @@ -58,26 +61,24 @@ class Knowledge(object): |
| 58 | 61 | def get_topic_progress(self): |
| 59 | 62 | return len(self.finished_questions) / (len(self.finished_questions) + len(self.questions)) |
| 60 | 63 | |
| 61 | - # --- generates a new question given the current state ------------------- | |
| 64 | + # ------------------------------------------------------------------------ | |
| 65 | + # if answer to current question is correct generates a new question | |
| 66 | + # otherwise returns none | |
| 62 | 67 | def new_question(self): |
| 63 | 68 | logger.debug('Knowledge.new_question()') |
| 64 | 69 | |
| 65 | - if self.current_question is None or self.current_question.get('grade', 0.0) > 0.9999: | |
| 70 | + if self.current_question is None or \ | |
| 71 | + self.current_question.get('grade', 0.0) > 0.9: | |
| 66 | 72 | |
| 73 | + # if no more questions in this topic, go to the next one | |
| 74 | + # keep going if there are no questions in the next topics | |
| 67 | 75 | while not self.questions: |
| 68 | - # finished topic! | |
| 69 | 76 | self.state[self.current_topic] = 1.0 |
| 70 | - self.finished_questions = [] | |
| 71 | - try: | |
| 72 | - self.current_topic = next(self.topic) | |
| 73 | - except StopIteration: | |
| 74 | - self.topic = self.topic_generator() | |
| 75 | - self.current_topic = next(self.topic) | |
| 76 | - self.questions = self.generate_questions_for_topic(self.current_topic) | |
| 77 | + self.new_topic() | |
| 77 | 78 | |
| 78 | 79 | self.current_question = self.questions.pop(0) |
| 79 | 80 | self.current_question['start_time'] = datetime.now() |
| 80 | - self.finished_questions.append(self.current_question) # FIXME not yet finished... | |
| 81 | + self.finished_questions.append(self.current_question) | |
| 81 | 82 | |
| 82 | 83 | return self.current_question |
| 83 | 84 | ... | ... |