README.md 3.02 KB
Newer Older
Hans-Nikolai Viessmann's avatar
Hans-Nikolai Viessmann committed
1 2
CodeFuboard
-----------
Hans-Nikolai Viessmann's avatar
Hans-Nikolai Viessmann committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

This is a leaderboard built using Python (Flask, and more...) where students
can submit code assignments and compare their performance metrics against
other students.

The leaderboard is built to be simple (and dirty), and should be fairly easy
to get working in any environment that has Python 3 and some webserver with
WCGI support.

Setup
=====

Make sure you have the packages listed in `requirements.txt`. After this you
can call Flask to run the app:

```sh
19 20
$ export FLASK_APP=codefuboard/__init__.py
$ mkdir instance/
Hans-Nikolai Viessmann's avatar
Hans-Nikolai Viessmann committed
21 22 23 24 25 26 27 28 29
$ flask run
```

We use Flask-AppConfig, as such various config values can be found in
`default_config.py`.

**Remember to update the `SECRET_KEY`, use some random value before using
this app in production!**

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
### ReCaptcha

This application can make use of ReCaptcha for all forms (including admin
login). Generate a the ReCaptacha keys and add these to the `default_config.py`
file.

### Webserver Setup

To run this app in a production environment one needs to proxy the Python driven process
through a webserver. For this I use Nginx together with uWSGI to handle the Python process.

In this example, I have cloned the app into `/srv/http/lb/codefuboard`. Additionally I created
a system user `codefu` which will _run_ the app. The uWSGI configuration for this is below:

```ini
[uwsgi]
strict = true
plugins = python
chdir = /srv/http/lb/codefuboard
module = wsgi
callable = app

master = true
processes = 4
threads = 2
vacuum = true                          ; Delete sockets during shutdown
single-interpreter = true
die-on-term = true
need-app = true
enable-threads = true
buffer-size = 65535
lazy = true
chmod-socket = 775
socket = /srv/http/lb/run/%n.sock
pidfile = /srv/http/lb/run/.pid
disable-logging = false
log-4xx = true
log-5xx = true
logto = /srv/http/lb/log/%n.log
uid = codefu
gid = codefu
```

I then setup Nginx with the following server configuration:

```nginx
server {
        server_name  <SERVER DOMAIN>;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        root /srv/http/lb;
        error_log /srv/http/lb/log/error.log;
        access_log /srv/http/lb/log/access.log;

        location / {
                try_files $uri @context;
        }

        location /leaderboard/ {
                rewrite ^/leaderboard/(.*)$ /lb/$1 permanent;
        }

        location @context {
                include uwsgi_params;
                uwsgi_pass unix:/srv/http/lb/run/lb.sock;
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
}
```

### Database

The app was initially built supporting pickledb as its db-backend. This proved
not to scale well at all in production. So I moved it to use Redis together with
MsgPack. The latter can encode most Python structures into strings, making it easy
to store complex structures in Redis.

Hans-Nikolai Viessmann's avatar
Hans-Nikolai Viessmann committed
116 117 118 119 120
Todo
====

- support notion of students and teams
  - teams need to pre-register?