Commit 39126690d4a973ef609803def914600a8d3a224f

Authored by Miguel Barão
1 parent d5cecbcf
Exists in master and in 1 other branch dev

removes npm and node dependency.

update looks and documentation.
include freebsd configuration files.
remove delay on POST answers since animations already have delay.
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
... ... @@ -30,7 +30,7 @@ are progressively uncovered as the students progress.
30 30 '''
31 31  
32 32 APP_NAME = 'aprendizations'
33   -APP_VERSION = '2021.08.dev1'
  33 +APP_VERSION = '2021.11.dev1'
34 34 APP_DESCRIPTION = __doc__
35 35  
36 36 __author__ = 'Miguel Barão'
... ...
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
... ... @@ -1 +0,0 @@
1   -../../node_modules/codemirror/
2 0 \ No newline at end of file
aprendizations/static/fontawesome-free
... ... @@ -1 +0,0 @@
1   -../../node_modules/@fortawesome/fontawesome-free/
2 0 \ No newline at end of file
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>
... ...
freebsd/pf.conf 0 → 100644
... ... @@ -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 +
... ...
freebsd/rc.conf 0 → 100644
... ... @@ -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   -}
package.json
... ... @@ -1,9 +0,0 @@
1   -{
2   - "description": "Javascript libraries required to run the server",
3   - "email": "mjsb@uevora.pt",
4   - "dependencies": {
5   - "@fortawesome/fontawesome-free": "^5.15.3",
6   - "codemirror": "^5.59.4"
7   - },
8   - "private": true
9   -}