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.
1 -  
2 # BUGS 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 - internal server error ao fazer logout no macos python3.8 6 - internal server error ao fazer logout no macos python3.8
6 - GET can get filtered by browser cache 7 - GET can get filtered by browser cache
7 - topicos chapter devem ser automaticamente completos assim que as dependencias 8 - topicos chapter devem ser automaticamente completos assim que as dependencias
1 # Getting Started 1 # Getting Started
2 2
3 -Latest review: 2021-07-08 3 +Latest review: 2021-11-08
4 4
5 ## Installation 5 ## Installation
6 6
7 To complete the installation we will need to perform the following steps: 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 To use the software we need to: 14 To use the software we need to:
17 15
18 -1. initialize database 16 +1. initialize a database
19 2. go to the demo directory (or an existing course) 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 Each of these steps is explained below. 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 ```sh 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 ### Install pip 33 ### Install pip
64 34
65 Install `pip` from the system package manager: 35 Install `pip` from the system package manager:
66 36
67 ```sh 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 sudo port install py39-pip # MacOS 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 In the end you should be able to run `pip --version` and `python3 -c "import 43 In the end you should be able to run `pip --version` and `python3 -c "import
76 sqlite3"` without errors. 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 `Library/Application Support/pip/pip.conf` (MacOS) and add the lines 49 `Library/Application Support/pip/pip.conf` (MacOS) and add the lines
81 50
82 ```ini 51 ```ini
@@ -84,30 +53,22 @@ Edit the configuration file `~/.config/pip/pip.conf` (FreeBSD, Linux) or @@ -84,30 +53,22 @@ Edit the configuration file `~/.config/pip/pip.conf` (FreeBSD, Linux) or
84 user = yes 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 ```sh 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 Python packages are usually installed in: 62 Python packages are usually installed in:
102 63
103 * `~/.local/lib/python3.8/site-packages/` in Linux/FreeBSD. 64 * `~/.local/lib/python3.8/site-packages/` in Linux/FreeBSD.
104 * `~/Library/python/3.9/lib/python/site-packages/` in MacOS. 65 * `~/Library/python/3.9/lib/python/site-packages/` in MacOS.
105 66
106 When aprendizations is installed with pip, all the dependencies are also 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 ```sh 73 ```sh
113 ~/.local/bin # Linux/FreeBSD 74 ~/.local/bin # Linux/FreeBSD
@@ -119,6 +80,7 @@ and can be run from the terminal: @@ -119,6 +80,7 @@ and can be run from the terminal:
119 ```sh 80 ```sh
120 aprendizations --version 81 aprendizations --version
121 aprendizations --help 82 aprendizations --help
  83 +initdb-aprendizations --help
122 ``` 84 ```
123 85
124 ### SSL Certificates 86 ### SSL Certificates
@@ -172,7 +134,7 @@ chmod 400 cert.pem privkey.pem @@ -172,7 +134,7 @@ chmod 400 cert.pem privkey.pem
172 134
173 User data is maintained in a sqlite3 database which has to be created manually 135 User data is maintained in a sqlite3 database which has to be created manually
174 using the `initdb-aprendizations` command. The database file should be located 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 For example, to run the included demo do: 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,12 +152,12 @@ The default password is equal to the user name, if left undefined.
190 152
191 ### Running the demo 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 for initial testing. Run it with 156 for initial testing. Run it with
195 157
196 ```sh 158 ```sh
197 cd demo 159 cd demo
198 -aprendizations demo.yaml 160 +aprendizations courses.yaml
199 ``` 161 ```
200 162
201 Open the browser at [https://127.0.0.1:8443](https://127.0.0.1:8443). 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,28 +199,25 @@ pflog_logfile="/var/log/pflog"
237 199
238 Reboot or `sudo service pf start`. 200 Reboot or `sudo service pf start`.
239 201
  202 +Example configuration files are in the `freebsd` directory.
  203 +
240 ### Testing the system 204 ### Testing the system
241 205
242 Make sure the following steps have been done: 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 * initialized database with at least 1 user 210 * initialized database with at least 1 user
249 * generate and copy certificates to the appropriate place 211 * generate and copy certificates to the appropriate place
250 * (optional) configure the firewall to do port forwarding 212 * (optional) configure the firewall to do port forwarding
251 -* run `aprendizations demo.yaml --check` 213 +* run `aprendizations courses.yaml --check`
252 214
253 ## Keeping aprendizations updated 215 ## Keeping aprendizations updated
254 216
255 To update aprendizations to the latest version do: 217 To update aprendizations to the latest version do:
256 218
257 ```sh 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 ## Troubleshooting 223 ## Troubleshooting
@@ -304,22 +263,6 @@ To fix this issue you need to allow animations in the Operating System: @@ -304,22 +263,6 @@ To fix this issue you need to allow animations in the Operating System:
304 * On MacOS or iOS search for reduced motion and switch it **OFF** 263 * On MacOS or iOS search for reduced motion and switch it **OFF**
305 (Preferences -> Acessibility -> Display -> Reduce motion). 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 ## FAQ 266 ## FAQ
324 267
325 Common database manipulations: 268 Common database manipulations:
aprendizations/__init__.py
@@ -30,7 +30,7 @@ are progressively uncovered as the students progress. @@ -30,7 +30,7 @@ are progressively uncovered as the students progress.
30 ''' 30 '''
31 31
32 APP_NAME = 'aprendizations' 32 APP_NAME = 'aprendizations'
33 -APP_VERSION = '2021.08.dev1' 33 +APP_VERSION = '2021.11.dev1'
34 APP_DESCRIPTION = __doc__ 34 APP_DESCRIPTION = __doc__
35 35
36 __author__ = 'Miguel Barão' 36 __author__ = 'Miguel Barão'
aprendizations/learnapp.py
@@ -86,7 +86,7 @@ class LearnApp(): @@ -86,7 +86,7 @@ class LearnApp():
86 for course_file in config.get('topics_from', []): 86 for course_file in config.get('topics_from', []):
87 course_conf = load_yaml(course_file) # course configuration 87 course_conf = load_yaml(course_file) # course configuration
88 # FIXME set defaults?? 88 # FIXME set defaults??
89 - logger.info('%6d topics imported from %s', 89 + logger.info('%6d topics from %s',
90 len(course_conf["topics"]), course_file) 90 len(course_conf["topics"]), course_file)
91 self._populate_graph(course_conf) 91 self._populate_graph(course_conf)
92 logger.info('Graph has %d topics', len(self.deps)) 92 logger.info('Graph has %d topics', len(self.deps))
@@ -327,7 +327,7 @@ class LearnApp(): @@ -327,7 +327,7 @@ class LearnApp():
327 logger.warning('"%s" could not start course "%s"', uid, course_id) 327 logger.warning('"%s" could not start course "%s"', uid, course_id)
328 raise LearnException() from exc 328 raise LearnException() from exc
329 else: 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,15 +609,17 @@ class LearnApp():
609 This should be modified to have a "visible" flag 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 with Session(self._engine, future=True) as session: 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 # all students in the database FIXME only with answers of this course 623 # all students in the database FIXME only with answers of this course
622 students = session.execute(query_students).all() 624 students = session.execute(query_students).all()
623 625
@@ -625,6 +627,9 @@ class LearnApp(): @@ -625,6 +627,9 @@ class LearnApp():
625 student_topics = session.execute(query_student_topics).all() 627 student_topics = session.execute(query_student_topics).all()
626 628
627 # answer performance 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 total = dict(session.execute(query_total).all()) 633 total = dict(session.execute(query_total).all())
629 right = dict(session.execute(query_right).all()) 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,6 +85,7 @@ class QuestionRadio(Question):
84 choose (int) # only used if shuffle=True 85 choose (int) # only used if shuffle=True
85 ''' 86 '''
86 87
  88 + # ------------------------------------------------------------------------
87 def gen(self) -> None: 89 def gen(self) -> None:
88 ''' 90 '''
89 Sets defaults, performs checks and generates the actual question 91 Sets defaults, performs checks and generates the actual question
@@ -692,4 +694,4 @@ class QFactory(): @@ -692,4 +694,4 @@ class QFactory():
692 # ------------------------------------------------------------------------ 694 # ------------------------------------------------------------------------
693 def generate(self) -> Question: 695 def generate(self) -> Question:
694 '''generate question (synchronous version)''' 696 '''generate question (synchronous version)'''
695 - return asyncio.get_event_loop().run_until_complete(self.gen_async()) 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 \ No newline at end of file 699 \ No newline at end of file
aprendizations/serve.py
@@ -411,7 +411,7 @@ class QuestionHandler(BaseHandler): @@ -411,7 +411,7 @@ class QuestionHandler(BaseHandler):
411 return 411 return
412 412
413 # --- brain hacking ;) 413 # --- brain hacking ;)
414 - await asyncio.sleep(1.5) 414 + # await asyncio.sleep(1.5)
415 415
416 # --- answers are in a list. fix depending on question type 416 # --- answers are in a list. fix depending on question type
417 qtype = self.learn.get_student_question_type(user) 417 qtype = self.learn.get_student_question_type(user)
aprendizations/static/codemirror
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -../../node_modules/codemirror/  
2 \ No newline at end of file 0 \ No newline at end of file
aprendizations/static/fontawesome-free
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -../../node_modules/@fortawesome/fontawesome-free/  
2 \ No newline at end of file 0 \ No newline at end of file
aprendizations/templates/courses.html
@@ -8,13 +8,17 @@ @@ -8,13 +8,17 @@
8 <meta name="author" content="Miguel Barão"> 8 <meta name="author" content="Miguel Barão">
9 <link rel="icon" href="favicon.ico"> 9 <link rel="icon" href="favicon.ico">
10 <!-- Styles --> 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 <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}"> 14 <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}">
13 <link rel="stylesheet" href="{{static_url('css/sticky-footer-navbar.css')}}"> 15 <link rel="stylesheet" href="{{static_url('css/sticky-footer-navbar.css')}}">
14 <!-- Scripts --> 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 <script defer src="{{static_url('js/maintopics.js')}}"></script> 22 <script defer src="{{static_url('js/maintopics.js')}}"></script>
19 23
20 <title>{{appname}}</title> 24 <title>{{appname}}</title>
@@ -22,14 +26,14 @@ @@ -22,14 +26,14 @@
22 26
23 <body> 27 <body>
24 <!-- ===== navbar ======================================================== --> 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 <div class="container-fluid"> 30 <div class="container-fluid">
27 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> 31 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora">
28 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> 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 <span class="navbar-toggler-icon"></span> 33 <span class="navbar-toggler-icon"></span>
30 </button> 34 </button>
31 35
32 - <div class="collapse navbar-collapse" id="navbarNavText"> 36 + <div class="collapse navbar-collapse" id="navbarText">
33 <ul class="navbar-nav"> 37 <ul class="navbar-nav">
34 <li class="nav-item"><a class="nav-link active" aria-current="page" href="/courses">Cursos</a></li> 38 <li class="nav-item"><a class="nav-link active" aria-current="page" href="/courses">Cursos</a></li>
35 <li class="nav-item"><a class="nav-link disabled" href="#">Tópicos</a></li> 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,28 +9,29 @@
9 <link rel="icon" href="/static/favicon.ico"> 9 <link rel="icon" href="/static/favicon.ico">
10 10
11 <!-- Styles --> 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 <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}"> 15 <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}">
14 16
15 <!-- Scripts --> 17 <!-- Scripts -->
16 <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script> 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 <script defer src="{{static_url('js/maintopics.js')}}"></script> 21 <script defer src="{{static_url('js/maintopics.js')}}"></script>
21 22
22 <title>{{appname}}</title> 23 <title>{{appname}}</title>
23 </head> 24 </head>
24 <!-- ===================================================================== --> 25 <!-- ===================================================================== -->
25 <body> 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 <div class="container-fluid"> 28 <div class="container-fluid">
28 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> 29 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora">
29 <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 <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 <span class="navbar-toggler-icon"></span> 31 <span class="navbar-toggler-icon"></span>
31 </button> 32 </button>
32 33
33 - <div class="collapse navbar-collapse" id="navbarNavText"> 34 + <div class="collapse navbar-collapse" id="navbarText">
34 <ul class="navbar-nav"> 35 <ul class="navbar-nav">
35 <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li> 36 <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li>
36 <li class="nav-item"><a class="nav-link active" aria-current="page" href="#">Tópicos</a></li> 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,25 +8,27 @@
8 <meta name="author" content="Miguel Barão"> 8 <meta name="author" content="Miguel Barão">
9 <link rel="icon" href="/static/favicon.ico"> 9 <link rel="icon" href="/static/favicon.ico">
10 <!-- Styles --> 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 <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}"> 14 <link rel="stylesheet" href="{{static_url('css/maintopics.css')}}">
13 <!-- Scripts --> 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 <script defer src="{{static_url('js/maintopics.js')}}"></script> 18 <script defer src="{{static_url('js/maintopics.js')}}"></script>
17 19
18 <title>{{appname}}</title> 20 <title>{{appname}}</title>
19 </head> 21 </head>
20 <!-- ===================================================================== --> 22 <!-- ===================================================================== -->
21 <body> 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 <div class="container-fluid"> 25 <div class="container-fluid">
24 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> 26 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora">
25 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> 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 <span class="navbar-toggler-icon"></span> 28 <span class="navbar-toggler-icon"></span>
27 </button> 29 </button>
28 30
29 - <div class="collapse navbar-collapse" id="navbarNavText"> 31 + <div class="collapse navbar-collapse" id="navbarText">
30 <ul class="navbar-nav"> 32 <ul class="navbar-nav">
31 <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li> 33 <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li>
32 <li class="nav-item"><a class="nav-link" href="/course/{{course_id}}">Tópicos</a></li> 34 <li class="nav-item"><a class="nav-link" href="/course/{{course_id}}">Tópicos</a></li>
@@ -53,8 +55,8 @@ @@ -53,8 +55,8 @@
53 <div class="container"> 55 <div class="container">
54 <h1 class="display-6">{{ course_title }}</h1> 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 <thead> 60 <thead>
59 <tr> 61 <tr>
60 <th scope="col" class="text-center">Posição</th> 62 <th scope="col" class="text-center">Posição</th>
@@ -65,7 +67,7 @@ @@ -65,7 +67,7 @@
65 </thead> 67 </thead>
66 <tbody> 68 <tbody>
67 {% for i,r in enumerate(rankings) %} 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 <td class="text-center"> <!-- rank --> 71 <td class="text-center"> <!-- rank -->
70 <strong> 72 <strong>
71 {{ '<i class="fas fa-crown fa-lg text-warning"></i>' if i==0 else i+1 }} 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,18 +6,17 @@
6 <meta name="author" content="Miguel Barão" /> 6 <meta name="author" content="Miguel Barão" />
7 <link rel="icon" href="/static/favicon.ico"> 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 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" /> 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 <script> 20 <script>
22 MathJax = { 21 MathJax = {
23 tex: { 22 tex: {
@@ -28,14 +27,14 @@ @@ -28,14 +27,14 @@
28 } 27 }
29 }; 28 };
30 </script> 29 </script>
31 - <!-- Scripts -->  
32 <script async type="text/javascript" id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> 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 <script defer src="{{static_url('js/topic.js')}}"></script> 38 <script defer src="{{static_url('js/topic.js')}}"></script>
40 39
41 <title>{{appname}}</title> 40 <title>{{appname}}</title>
@@ -44,18 +43,18 @@ @@ -44,18 +43,18 @@
44 <body> 43 <body>
45 <!-- Progress bar --> 44 <!-- Progress bar -->
46 <div class="progress fixed-top" style="height: 70px; border-radius: 0px;"> 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 </div> 47 </div>
49 48
50 <!-- Navbar --> 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 <div class="container-fluid"> 51 <div class="container-fluid">
53 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora"> 52 <img src="{{static_url('logo_horizontal.png')}}" height="48" width="120" class="navbar-brand" alt="UEvora">
54 <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> 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 <span class="navbar-toggler-icon"></span> 54 <span class="navbar-toggler-icon"></span>
56 </button> 55 </button>
57 56
58 - <div class="collapse navbar-collapse" id="navbarNavText"> 57 + <div class="collapse navbar-collapse" id="navbarText">
59 <ul class="navbar-nav"> 58 <ul class="navbar-nav">
60 <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li> 59 <li class="nav-item"><a class="nav-link" href="/courses">Cursos</a></li>
61 <li class="nav-item"><a class="nav-link active" aria-current="page" href="/course/{{course_id}}">Tópicos</a></li> 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 @@ @@ -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 @@ @@ -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,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,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 -}