Commit 39126690d4a973ef609803def914600a8d3a224f
1 parent
d5cecbcf
Exists in
master
and in
1 other branch
removes npm and node dependency.
update looks and documentation. include freebsd configuration files. remove delay on POST answers since animations already have delay.
Showing
16 changed files
with
182 additions
and
194 deletions
Show diff stats
BUGS.md
1 | - | |
2 | 1 | # BUGS |
3 | 2 | |
4 | -- se na especificacao de um curso, a referencia do topico nao existir como directorio, rebenta. | |
3 | +- nao esta a respeitar o numero de tentativas `max_tries`. | |
4 | +- se na especificacao de um curso, a referencia do topico nao existir como | |
5 | + directorio, rebenta. | |
5 | 6 | - internal server error ao fazer logout no macos python3.8 |
6 | 7 | - GET can get filtered by browser cache |
7 | 8 | - topicos chapter devem ser automaticamente completos assim que as dependencias | ... | ... |
README.md
1 | 1 | # Getting Started |
2 | 2 | |
3 | -Latest review: 2021-07-08 | |
3 | +Latest review: 2021-11-08 | |
4 | 4 | |
5 | 5 | ## Installation |
6 | 6 | |
7 | 7 | To complete the installation we will need to perform the following steps: |
8 | 8 | |
9 | -1. install python3, pip and npm | |
10 | -2. download aprendizations from the repository | |
11 | -3. install javascript libraries (with npm) | |
12 | -4. install aprendizations (with pip) | |
13 | -5. generate SSL certificates | |
14 | -6. configure the firewall (optional) | |
9 | +1. install python3 and pip | |
10 | +2. install aprendizations | |
11 | +3. generate SSL certificates | |
12 | +4. configure the firewall (optional) | |
15 | 13 | |
16 | 14 | To use the software we need to: |
17 | 15 | |
18 | -1. initialize database | |
16 | +1. initialize a database | |
19 | 17 | 2. go to the demo directory (or an existing course) |
20 | -3. run `aprendizations demo.yaml` | |
18 | +3. run `aprendizations courses.yaml` | |
21 | 19 | |
22 | 20 | Each of these steps is explained below. |
23 | 21 | |
24 | -### Install python3 with sqlite3 support and npm | |
22 | +### Install python3 with sqlite3 support | |
25 | 23 | |
26 | -Python can be installed either from the system package management or compiled | |
27 | -from sources. | |
28 | - | |
29 | -#### Installing from the system package manager | |
30 | - | |
31 | -```sh | |
32 | -sudo pkg install python3 npm # FreeBSD | |
33 | -sudo apt install python3 npm # Linux (Ubuntu) | |
34 | -sudo port install python38 npm7 # MacOS | |
35 | -``` | |
36 | - | |
37 | -In FreeBSD also install `py3X-sqlite3` where `X` is the python version. | |
38 | - | |
39 | -#### Installing from source (outdated) | |
40 | - | |
41 | -Make sure that the build tools and libraries are installed: | |
24 | +Minimum supported version is python3.8. The installed versions depend on the | |
25 | +operating system default. | |
42 | 26 | |
43 | 27 | ```sh |
44 | -# Ubuntu: | |
45 | -sudo apt install build-essential libssl-dev zlib1g-dev libncurses5-dev \ | |
46 | - libncursesw5-dev libreadline-dev libsqlite3-dev libgdbm-dev libdb5.3-dev \ | |
47 | - libbz2-dev libexpat1-dev liblzma-dev tk-dev libffi-dev | |
28 | +sudo pkg install python3 py38-sqlite3 # FreeBSD | |
29 | +sudo apt install python3 # Linux (Ubuntu) | |
30 | +sudo port install python39 # MacOS | |
48 | 31 | ``` |
49 | 32 | |
50 | -Download [python](http://www.python.org) and | |
51 | - | |
52 | -```sh | |
53 | -tar xvfJ Python-3.7.tar.xz | |
54 | -cd Python-3.7 | |
55 | -./configure --prefix=$HOME/.local --enable-optimizations | |
56 | -make && make install | |
57 | -``` | |
58 | - | |
59 | -This will install python locally under `~/.local/bin`. Make sure to add it to | |
60 | -your `PATH` in `~/.profile`. If `~/bin` is already in the path, just make a | |
61 | -symbolic link `ln -s ~/.local/bin ~/bin`. | |
62 | - | |
63 | 33 | ### Install pip |
64 | 34 | |
65 | 35 | Install `pip` from the system package manager: |
66 | 36 | |
67 | 37 | ```sh |
68 | -sudo apt install python3-pip # Ubuntu | |
69 | -sudo pkg py38-pip # FreeBSD | |
38 | +sudo pkg install py38-pip # FreeBSD | |
39 | +sudo apt install python3-pip # Linux (Ubuntu) | |
70 | 40 | sudo port install py39-pip # MacOS |
71 | 41 | ``` |
72 | 42 | |
73 | -Then run `python3 -m pip install -U pip` to install latest version into your | |
74 | -user account under `~/.local/bin`. | |
75 | 43 | In the end you should be able to run `pip --version` and `python3 -c "import |
76 | 44 | sqlite3"` without errors. |
77 | -In some systems, `pip` can be named `pip3`, `pip3.8` or `pip-3.8`. | |
45 | +In some systems, `pip` can be named `pip3`, `pip3.8` or `pip-3.8`, etc. | |
78 | 46 | |
79 | -Edit the configuration file `~/.config/pip/pip.conf` (FreeBSD, Linux) or | |
47 | +Packages should **not** be installed system-wide. To install locally in the user | |
48 | +area, edit the configuration file `~/.config/pip/pip.conf` (FreeBSD, Linux) or | |
80 | 49 | `Library/Application Support/pip/pip.conf` (MacOS) and add the lines |
81 | 50 | |
82 | 51 | ```ini |
... | ... | @@ -84,30 +53,22 @@ Edit the configuration file `~/.config/pip/pip.conf` (FreeBSD, Linux) or |
84 | 53 | user = yes |
85 | 54 | ``` |
86 | 55 | |
87 | -This will set pip to install modules in the user area (recommended). | |
88 | - | |
89 | -### Download and install aprendizations | |
56 | +### Install aprendizations | |
90 | 57 | |
91 | 58 | ```sh |
92 | -git clone https://git.xdi.uevora.pt/mjsb/aprendizations.git | |
93 | -cd aprendizations | |
94 | -npm install # install javascript libraries | |
95 | -pip install . # install aprendizations and dependencies | |
59 | +pip install git+https://git.xdi.uevora.pt/mjsb/aprendizations.git | |
96 | 60 | ``` |
97 | 61 | |
98 | -Javascript libraries are installed in `aprendizations/node_modules` and are | |
99 | -linked from `aprendizations/aprendizations/static`. | |
100 | - | |
101 | 62 | Python packages are usually installed in: |
102 | 63 | |
103 | 64 | * `~/.local/lib/python3.8/site-packages/` in Linux/FreeBSD. |
104 | 65 | * `~/Library/python/3.9/lib/python/site-packages/` in MacOS. |
105 | 66 | |
106 | 67 | When aprendizations is installed with pip, all the dependencies are also |
107 | -installed. The javascript libraries previously installed with npm are copied to | |
108 | -the above directory and the cloned repository is no longer needed. | |
68 | +installed. | |
109 | 69 | |
110 | -At this point `aprendizations` is installed in | |
70 | +At this point, the commands `aprendizations` and `initdb-aprendizations` are | |
71 | +installed in | |
111 | 72 | |
112 | 73 | ```sh |
113 | 74 | ~/.local/bin # Linux/FreeBSD |
... | ... | @@ -119,6 +80,7 @@ and can be run from the terminal: |
119 | 80 | ```sh |
120 | 81 | aprendizations --version |
121 | 82 | aprendizations --help |
83 | +initdb-aprendizations --help | |
122 | 84 | ``` |
123 | 85 | |
124 | 86 | ### SSL Certificates |
... | ... | @@ -172,7 +134,7 @@ chmod 400 cert.pem privkey.pem |
172 | 134 | |
173 | 135 | User data is maintained in a sqlite3 database which has to be created manually |
174 | 136 | using the `initdb-aprendizations` command. The database file should be located |
175 | -in the same directory as the main YAML configuration file. | |
137 | +in the same directory as the main YAML configuration file (`courses.yaml`). | |
176 | 138 | |
177 | 139 | For example, to run the included demo do: |
178 | 140 | |
... | ... | @@ -190,12 +152,12 @@ The default password is equal to the user name, if left undefined. |
190 | 152 | |
191 | 153 | ### Running the demo |
192 | 154 | |
193 | -The application includes a small example in `demo/demo.yaml` that can be used | |
155 | +The application includes a small example in `demo/courses.yaml` that can be used | |
194 | 156 | for initial testing. Run it with |
195 | 157 | |
196 | 158 | ```sh |
197 | 159 | cd demo |
198 | -aprendizations demo.yaml | |
160 | +aprendizations courses.yaml | |
199 | 161 | ``` |
200 | 162 | |
201 | 163 | Open the browser at [https://127.0.0.1:8443](https://127.0.0.1:8443). |
... | ... | @@ -237,28 +199,25 @@ pflog_logfile="/var/log/pflog" |
237 | 199 | |
238 | 200 | Reboot or `sudo service pf start`. |
239 | 201 | |
202 | +Example configuration files are in the `freebsd` directory. | |
203 | + | |
240 | 204 | ### Testing the system |
241 | 205 | |
242 | 206 | Make sure the following steps have been done: |
243 | 207 | |
244 | -* installed python3, pip and npm | |
245 | -* git-cloned the aprendizations from the main repository | |
246 | -* installed javascript libraries with npm | |
247 | -* installed aprendizations with pip | |
208 | +* installed python3 and pip | |
209 | +* installed aprendizations using pip | |
248 | 210 | * initialized database with at least 1 user |
249 | 211 | * generate and copy certificates to the appropriate place |
250 | 212 | * (optional) configure the firewall to do port forwarding |
251 | -* run `aprendizations demo.yaml --check` | |
213 | +* run `aprendizations courses.yaml --check` | |
252 | 214 | |
253 | 215 | ## Keeping aprendizations updated |
254 | 216 | |
255 | 217 | To update aprendizations to the latest version do: |
256 | 218 | |
257 | 219 | ```sh |
258 | -cd aprendizations | |
259 | -git pull # get latest version | |
260 | -npm update # update javascript libraries | |
261 | -pip install -U . # updates installed version | |
220 | +pip install -U git+https://git.xdi.uevora.pt/mjsb/aprendizations.git | |
262 | 221 | ``` |
263 | 222 | |
264 | 223 | ## Troubleshooting |
... | ... | @@ -304,22 +263,6 @@ To fix this issue you need to allow animations in the Operating System: |
304 | 263 | * On MacOS or iOS search for reduced motion and switch it **OFF** |
305 | 264 | (Preferences -> Acessibility -> Display -> Reduce motion). |
306 | 265 | |
307 | - | |
308 | -#### The application runs but questions do not show up | |
309 | - | |
310 | -Some operating systems have an option to disable animations to try to avoid | |
311 | -motion sickness in some people. Browsers will check this option with the OS and | |
312 | -prevent animate.css library to work. Since questions have several animations, | |
313 | -these will will not work and nothing is shown on the page. | |
314 | - | |
315 | -To fix this issue you need to allow animations in the Operating System: | |
316 | - | |
317 | -- On windows 10, go to System Preferences, search for "Show animations in | |
318 | - windows" and turn it **ON**. | |
319 | -- On MacOS or iOS search for reduced motion and switch it **OFF** | |
320 | - (Preferences -> Acessibility -> Display -> Reduce motion). | |
321 | - | |
322 | - | |
323 | 266 | ## FAQ |
324 | 267 | |
325 | 268 | Common database manipulations: | ... | ... |
aprendizations/__init__.py
aprendizations/learnapp.py
... | ... | @@ -86,7 +86,7 @@ class LearnApp(): |
86 | 86 | for course_file in config.get('topics_from', []): |
87 | 87 | course_conf = load_yaml(course_file) # course configuration |
88 | 88 | # FIXME set defaults?? |
89 | - logger.info('%6d topics imported from %s', | |
89 | + logger.info('%6d topics from %s', | |
90 | 90 | len(course_conf["topics"]), course_file) |
91 | 91 | self._populate_graph(course_conf) |
92 | 92 | logger.info('Graph has %d topics', len(self.deps)) |
... | ... | @@ -327,7 +327,7 @@ class LearnApp(): |
327 | 327 | logger.warning('"%s" could not start course "%s"', uid, course_id) |
328 | 328 | raise LearnException() from exc |
329 | 329 | else: |
330 | - logger.info('User "%s" started course "%s"', uid, course_id) | |
330 | + logger.info('User "%s" course "%s"', uid, course_id) | |
331 | 331 | |
332 | 332 | # ------------------------------------------------------------------------ |
333 | 333 | # |
... | ... | @@ -609,15 +609,17 @@ class LearnApp(): |
609 | 609 | This should be modified to have a "visible" flag |
610 | 610 | ''' |
611 | 611 | |
612 | - logger.info('User "%s" get rankings for %s', uid, course_id) | |
613 | - query_students = select(Student.id, Student.name) | |
614 | - query_student_topics = select(StudentTopic.student_id, | |
615 | - StudentTopic.topic_id, | |
616 | - StudentTopic.level, | |
617 | - StudentTopic.date) | |
618 | - query_total = select(Answer.student_id, func.count(Answer.ref)) | |
619 | - query_right = select(Answer.student_id, func.count(Answer.ref)).where(Answer.grade == 1.0) | |
612 | + logger.info('User "%s" rankings for "%s"', uid, course_id) | |
620 | 613 | with Session(self._engine, future=True) as session: |
614 | + query_students = select(Student.id, Student.name) | |
615 | + query_student_topics = select(StudentTopic.student_id, | |
616 | + StudentTopic.topic_id, | |
617 | + StudentTopic.level, | |
618 | + StudentTopic.date) | |
619 | + query_total = select(Answer.student_id, func.count(Answer.ref)) | |
620 | + query_right = select(Answer.student_id, func.count(Answer.ref)) \ | |
621 | + .where(Answer.grade == 1.0) | |
622 | + | |
621 | 623 | # all students in the database FIXME only with answers of this course |
622 | 624 | students = session.execute(query_students).all() |
623 | 625 | |
... | ... | @@ -625,6 +627,9 @@ class LearnApp(): |
625 | 627 | student_topics = session.execute(query_student_topics).all() |
626 | 628 | |
627 | 629 | # answer performance |
630 | + | |
631 | + # FIXME this does not work when nobody has done anything... | |
632 | + # FIXME row to dict seems to be deprecated in 1.4+ | |
628 | 633 | total = dict(session.execute(query_total).all()) |
629 | 634 | right = dict(session.execute(query_right).all()) |
630 | 635 | ... | ... |
aprendizations/questions.py
1 | 1 | ''' |
2 | -Classes the implement several types of questions. | |
2 | +File: aprendizations/questions.py | |
3 | +Description: Classes the implement several types of questions. | |
3 | 4 | ''' |
4 | 5 | |
5 | 6 | |
... | ... | @@ -84,6 +85,7 @@ class QuestionRadio(Question): |
84 | 85 | choose (int) # only used if shuffle=True |
85 | 86 | ''' |
86 | 87 | |
88 | + # ------------------------------------------------------------------------ | |
87 | 89 | def gen(self) -> None: |
88 | 90 | ''' |
89 | 91 | Sets defaults, performs checks and generates the actual question |
... | ... | @@ -692,4 +694,4 @@ class QFactory(): |
692 | 694 | # ------------------------------------------------------------------------ |
693 | 695 | def generate(self) -> Question: |
694 | 696 | '''generate question (synchronous version)''' |
695 | 697 | - return asyncio.get_event_loop().run_until_complete(self.gen_async()) |
698 | + return asyncio.get_event_loop().run_until_complete(self.gen_async()) | |
696 | 699 | \ No newline at end of file | ... | ... |
aprendizations/serve.py
... | ... | @@ -411,7 +411,7 @@ class QuestionHandler(BaseHandler): |
411 | 411 | return |
412 | 412 | |
413 | 413 | # --- brain hacking ;) |
414 | - await asyncio.sleep(1.5) | |
414 | + # await asyncio.sleep(1.5) | |
415 | 415 | |
416 | 416 | # --- answers are in a list. fix depending on question type |
417 | 417 | qtype = self.learn.get_student_question_type(user) | ... | ... |
aprendizations/static/codemirror
aprendizations/static/fontawesome-free
aprendizations/templates/courses.html
... | ... | @@ -8,13 +8,17 @@ |
8 | 8 | <meta name="author" content="Miguel Barão"> |
9 | 9 | <link rel="icon" href="favicon.ico"> |
10 | 10 | <!-- Styles --> |
11 | - <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> | |
11 | + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> | |
12 | + <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet"> | |
13 | + | |
12 | 14 | <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}"> |
13 | 15 | <link rel="stylesheet" href="{{static_url('css/sticky-footer-navbar.css')}}"> |
14 | 16 | <!-- Scripts --> |
15 | - <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> | |
16 | - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script> | |
17 | - <script defer src="{{static_url('fontawesome-free/js/all.min.js')}}"></script> | |
17 | + <script src="https://code.jquery.com/jquery-3.6.0.min.js" | |
18 | + integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> | |
19 | + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" | |
20 | + integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> | |
21 | + | |
18 | 22 | <script defer src="{{static_url('js/maintopics.js')}}"></script> |
19 | 23 | |
20 | 24 | <title>{{appname}}</title> |
... | ... | @@ -22,14 +26,14 @@ |
22 | 26 | |
23 | 27 | <body> |
24 | 28 | <!-- ===== navbar ======================================================== --> |
25 | - <nav class="navbar navbar-expand-sm navbar-dark bg-primary fixed-top shadow"> | |
29 | + <nav class="navbar navbar-expand-sm navbar-dark bg-secondary fixed-top shadow"> | |
26 | 30 | <div class="container-fluid"> |
27 | 31 | <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> |
28 | 32 | <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> |
29 | 33 | <span class="navbar-toggler-icon"></span> |
30 | 34 | </button> |
31 | 35 | |
32 | - <div class="collapse navbar-collapse" id="navbarNavText"> | |
36 | + <div class="collapse navbar-collapse" id="navbarText"> | |
33 | 37 | <ul class="navbar-nav"> |
34 | 38 | <li class="nav-item"><a class="nav-link active" aria-current="page" href="/courses">Cursos</a></li> |
35 | 39 | <li class="nav-item"><a class="nav-link disabled" href="#">Tópicos</a></li> | ... | ... |
aprendizations/templates/maintopics-table.html
... | ... | @@ -9,28 +9,29 @@ |
9 | 9 | <link rel="icon" href="/static/favicon.ico"> |
10 | 10 | |
11 | 11 | <!-- Styles --> |
12 | - <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> | |
12 | + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> | |
13 | + <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet"> | |
14 | + | |
13 | 15 | <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}"> |
14 | 16 | |
15 | 17 | <!-- Scripts --> |
16 | 18 | <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> |
17 | - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script> | |
19 | + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> | |
18 | 20 | |
19 | - <script defer src="{{static_url('fontawesome-free/js/all.min.js')}}"></script> | |
20 | 21 | <script defer src="{{static_url('js/maintopics.js')}}"></script> |
21 | 22 | |
22 | 23 | <title>{{appname}}</title> |
23 | 24 | </head> |
24 | 25 | <!-- ===================================================================== --> |
25 | 26 | <body> |
26 | -<nav class="navbar navbar-expand-sm navbar-dark bg-primary fixed-top shadow"> | |
27 | +<nav class="navbar navbar-expand-sm navbar-dark bg-secondary fixed-top shadow"> | |
27 | 28 | <div class="container-fluid"> |
28 | 29 | <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> |
29 | 30 | <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> |
30 | 31 | <span class="navbar-toggler-icon"></span> |
31 | 32 | </button> |
32 | 33 | |
33 | - <div class="collapse navbar-collapse" id="navbarNavText"> | |
34 | + <div class="collapse navbar-collapse" id="navbarText"> | |
34 | 35 | <ul class="navbar-nav"> |
35 | 36 | <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li> |
36 | 37 | <li class="nav-item"><a class="nav-link active" aria-current="page" href="#">Tópicos</a></li> | ... | ... |
aprendizations/templates/rankings.html
... | ... | @@ -8,25 +8,27 @@ |
8 | 8 | <meta name="author" content="Miguel Barão"> |
9 | 9 | <link rel="icon" href="/static/favicon.ico"> |
10 | 10 | <!-- Styles --> |
11 | - <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> | |
11 | + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous"> | |
12 | + <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet"> | |
13 | + | |
12 | 14 | <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}"> |
13 | 15 | <!-- Scripts --> |
14 | - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script> | |
15 | - <script defer src="{{static_url('fontawesome-free/js/all.min.js')}}"></script> | |
16 | + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> | |
17 | + | |
16 | 18 | <script defer src="{{static_url('js/maintopics.js')}}"></script> |
17 | 19 | |
18 | 20 | <title>{{appname}}</title> |
19 | 21 | </head> |
20 | 22 | <!-- ===================================================================== --> |
21 | 23 | <body> |
22 | -<nav class="navbar navbar-expand-sm navbar-dark bg-primary fixed-top"> | |
24 | +<nav class="navbar navbar-expand-sm navbar-dark bg-secondary fixed-top shadow"> | |
23 | 25 | <div class="container-fluid"> |
24 | 26 | <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> |
25 | 27 | <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> |
26 | 28 | <span class="navbar-toggler-icon"></span> |
27 | 29 | </button> |
28 | 30 | |
29 | - <div class="collapse navbar-collapse" id="navbarNavText"> | |
31 | + <div class="collapse navbar-collapse" id="navbarText"> | |
30 | 32 | <ul class="navbar-nav"> |
31 | 33 | <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li> |
32 | 34 | <li class="nav-item"><a class="nav-link" href="/course/{{course_id}}">Tópicos</a></li> |
... | ... | @@ -53,8 +55,8 @@ |
53 | 55 | <div class="container"> |
54 | 56 | <h1 class="display-6">{{ course_title }}</h1> |
55 | 57 | |
56 | -<table class="table table-hover"> | |
57 | - <col width="100"> | |
58 | +<table class="table table-sm table-hover"> | |
59 | + <!-- <col width="100"> --> | |
58 | 60 | <thead> |
59 | 61 | <tr> |
60 | 62 | <th scope="col" class="text-center">Posição</th> |
... | ... | @@ -65,7 +67,7 @@ |
65 | 67 | </thead> |
66 | 68 | <tbody> |
67 | 69 | {% for i,r in enumerate(rankings) %} |
68 | - <tr class="{{ 'table-primary' if r[0] == uid else '' }}"> | |
70 | + <tr class="{{ 'table-secondary' if r[0] == uid else '' }}"> | |
69 | 71 | <td class="text-center"> <!-- rank --> |
70 | 72 | <strong> |
71 | 73 | {{ '<i class="fas fa-crown fa-lg text-warning"></i>' if i==0 else i+1 }} | ... | ... |
aprendizations/templates/topic.html
... | ... | @@ -6,18 +6,17 @@ |
6 | 6 | <meta name="author" content="Miguel Barão" /> |
7 | 7 | <link rel="icon" href="/static/favicon.ico"> |
8 | 8 | |
9 | - <!-- Styles --> | |
10 | - <!-- <link rel="stylesheet" href="{{static_url('mdbootstrap/css/bootstrap.min.css')}}"> --> | |
11 | - <!-- <link rel="stylesheet" href="{{static_url('mdbootstrap/css/mdb.min.css')}}"> --> | |
12 | - | |
13 | - <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous"> | |
9 | + <!-- Styles ---------------------------------------------------------- --> | |
10 | + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" | |
11 | + integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous" /> | |
12 | + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" /> | |
14 | 13 | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> |
14 | + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.3/codemirror.min.css" /> | |
15 | + <!-- local styles --> | |
16 | + <link rel="stylesheet" href="{{static_url('css/github.css')}}" /> | |
17 | + <link rel="stylesheet" href="{{static_url('css/topic.css')}}" /> | |
15 | 18 | |
16 | - <link rel="stylesheet" href="{{static_url('codemirror/lib/codemirror.css')}}"> | |
17 | - <link rel="stylesheet" href="{{static_url('css/github.css')}}"> | |
18 | - <link rel="stylesheet" href="{{static_url('css/topic.css')}}"> | |
19 | - | |
20 | - <!-- MathJax3 --> | |
19 | + <!-- Scripts --------------------------------------------------------- --> | |
21 | 20 | <script> |
22 | 21 | MathJax = { |
23 | 22 | tex: { |
... | ... | @@ -28,14 +27,14 @@ |
28 | 27 | } |
29 | 28 | }; |
30 | 29 | </script> |
31 | - <!-- Scripts --> | |
32 | 30 | <script async type="text/javascript" id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> |
31 | + <script defer src="https://code.jquery.com/jquery-3.6.0.min.js" | |
32 | + integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> | |
33 | + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" | |
34 | + integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script> | |
35 | + <script defer src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.3/codemirror.min.js"></script> | |
33 | 36 | |
34 | - <script defer src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> | |
35 | - <script defer src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script> | |
36 | - | |
37 | - <script defer src="{{static_url('fontawesome-free/js/all.min.js')}}"></script> | |
38 | - <script defer src="{{static_url('codemirror/lib/codemirror.js')}}"></script> | |
37 | + <!-- local scripts --> | |
39 | 38 | <script defer src="{{static_url('js/topic.js')}}"></script> |
40 | 39 | |
41 | 40 | <title>{{appname}}</title> |
... | ... | @@ -44,18 +43,18 @@ |
44 | 43 | <body> |
45 | 44 | <!-- Progress bar --> |
46 | 45 | <div class="progress fixed-top" style="height: 70px; border-radius: 0px;"> |
47 | - <div class="progress-bar bg-warning" id="topic_progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 1em;width: 0%"></div> | |
46 | + <div class="progress-bar bg-secondary" id="topic_progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="min-width: 1em;width: 0%"></div> | |
48 | 47 | </div> |
49 | 48 | |
50 | 49 | <!-- Navbar --> |
51 | - <nav class="navbar navbar-expand-sm navbar-dark bg-primary fixed-top shadow"> | |
50 | + <nav class="navbar navbar-expand-sm navbar-dark bg-secondary fixed-top shadow"> | |
52 | 51 | <div class="container-fluid"> |
53 | 52 | <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> |
54 | 53 | <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> |
55 | 54 | <span class="navbar-toggler-icon"></span> |
56 | 55 | </button> |
57 | 56 | |
58 | - <div class="collapse navbar-collapse" id="navbarNavText"> | |
57 | + <div class="collapse navbar-collapse" id="navbarText"> | |
59 | 58 | <ul class="navbar-nav"> |
60 | 59 | <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li> |
61 | 60 | <li class="nav-item"><a class="nav-link active" aria-current="page" href="/course/{{course_id}}">Tópicos</a></li> | ... | ... |
... | ... | @@ -0,0 +1,60 @@ |
1 | + | |
2 | +# Public interface | |
3 | +ext_if="em0" | |
4 | + | |
5 | +table <bruteforce> persist | |
6 | + | |
7 | +# Set and drop these IP ranges on public interface | |
8 | +martians = "{ 0.0.0.0/8, 127.0.0.0/8 }" | |
9 | + | |
10 | +# 192.168.0.0/16, 172.16.0.0/12, \ | |
11 | +# 10.0.0.0/8, 169.254.0.0/16, 192.0.2.0/24, \ | |
12 | +# 240.0.0.0/4 }" | |
13 | + | |
14 | +webports = "{ 8080, 8443 }" | |
15 | + | |
16 | +tcp_services = "{ domain, ntp, smtp, www, https, ssh }" | |
17 | +udp_services = "{ domain, ntp }" | |
18 | + | |
19 | +# Skip all PF processing on loopback interface | |
20 | +set skip on lo | |
21 | + | |
22 | +# Log statistics on interface | |
23 | +set loginterface $ext_if | |
24 | + | |
25 | +# Packet normalization | |
26 | +scrub in all | |
27 | + | |
28 | +# Redirect http and https ports to 8080 and 8443, resp. | |
29 | +no rdr on $ext_if proto tcp from <bruteforce> to any | |
30 | +rdr on $ext_if proto tcp from any to any port 80 -> 127.0.0.1 port 8080 | |
31 | +rdr on $ext_if proto tcp from any to any port 443 -> 127.0.0.1 port 8443 | |
32 | + | |
33 | +# Default policy | |
34 | +#block return in log all | |
35 | +#block out all | |
36 | + | |
37 | +# Protect against spoofed or forget IP addresses | |
38 | +antispoof quick for $ext_if | |
39 | + | |
40 | +# Drop all Non-Routable Addresses | |
41 | +block drop in quick on $ext_if from $martians to any | |
42 | +block drop out quick on $ext_if from any to $martians | |
43 | + | |
44 | +# Protect against SSH attacks | |
45 | +block quick from <bruteforce> | |
46 | +pass quick proto { tcp, udp } from any to any port ssh \ | |
47 | + flags S/SA keep state \ | |
48 | + (max-src-conn 10, max-src-conn-rate 5/3, \ | |
49 | + overload <bruteforce> flush global) | |
50 | + | |
51 | +# Allow Ping | |
52 | +pass inet proto icmp icmp-type echoreq | |
53 | + | |
54 | +# Webserver | |
55 | +#pass proto tcp from any to $ext_if port $webports | |
56 | + | |
57 | +# Allow essential outgoing traffic | |
58 | +pass out quick on $ext_if proto tcp to any port $tcp_services | |
59 | +pass out quick on $ext_if proto udp to any port $udp_services | |
60 | + | ... | ... |
... | ... | @@ -0,0 +1,21 @@ |
1 | +zfs_enable="YES" | |
2 | +clear_tmp_enable="YES" | |
3 | +hostname="bit.xdi.uevora.pt" | |
4 | +ifconfig_em0="inet 193.137.120.198 netmask 255.255.255.192" | |
5 | +defaultrouter="193.137.120.254" | |
6 | +ifconfig_em0_ipv6="inet6 accept_rtadv" | |
7 | +sshd_enable="YES" | |
8 | +ntpd_enable="YES" | |
9 | +ntpd_sync_on_start="YES" | |
10 | +powerd_enable="NO" | |
11 | + | |
12 | +# pf firewall | |
13 | +pf_enable="YES" | |
14 | +#pf_flags="-T expire 86400" | |
15 | +pf_rules="/etc/pf.conf" | |
16 | +#pflog_enable="YES" | |
17 | +#pflog_flags="" | |
18 | +#pflog_logfile="/var/log/pflog" | |
19 | + | |
20 | +# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable | |
21 | +dumpdev="NO" | ... | ... |
package-lock.json
... | ... | @@ -1,39 +0,0 @@ |
1 | -{ | |
2 | - "name": "aprendizations", | |
3 | - "lockfileVersion": 2, | |
4 | - "requires": true, | |
5 | - "packages": { | |
6 | - "": { | |
7 | - "dependencies": { | |
8 | - "@fortawesome/fontawesome-free": "^5.15.3", | |
9 | - "codemirror": "^5.59.4" | |
10 | - } | |
11 | - }, | |
12 | - "node_modules/@fortawesome/fontawesome-free": { | |
13 | - "version": "5.15.4", | |
14 | - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", | |
15 | - "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", | |
16 | - "hasInstallScript": true, | |
17 | - "engines": { | |
18 | - "node": ">=6" | |
19 | - } | |
20 | - }, | |
21 | - "node_modules/codemirror": { | |
22 | - "version": "5.62.2", | |
23 | - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz", | |
24 | - "integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw==" | |
25 | - } | |
26 | - }, | |
27 | - "dependencies": { | |
28 | - "@fortawesome/fontawesome-free": { | |
29 | - "version": "5.15.4", | |
30 | - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz", | |
31 | - "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==" | |
32 | - }, | |
33 | - "codemirror": { | |
34 | - "version": "5.62.2", | |
35 | - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.62.2.tgz", | |
36 | - "integrity": "sha512-tVFMUa4J3Q8JUd1KL9yQzQB0/BJt7ZYZujZmTPgo/54Lpuq3ez4C8x/ATUY/wv7b7X3AUq8o3Xd+2C5ZrCGWHw==" | |
37 | - } | |
38 | - } | |
39 | -} |