Giter VIP home page Giter VIP logo

inloop's Introduction

⚠️ Deprecation Notice ⚠️

INLOOP has reached its end of life, development has stopped and this repository has been archived.

This project is no longer supported and maintained by the Software Technology Group at TU Dresden.

The original README has been preserved below.

    ___       ___       ___     ___       ___       ___
   /\  \     /\__\     /\__\   /\  \     /\  \     /\  \
  _\:\  \   /:| _|_   /:/  /  /::\  \   /::\  \   /::\  \
 /\/::\__\ /::|/\__\ /:/__/  /:/\:\__\ /:/\:\__\ /::\:\__\
 \::/\/__/ \/|::/  / \:\  \  \:\/:/  / \:\/:/  / \/\::/  /
  \:\__\     |:/  /   \:\__\  \::/  /   \::/  /     \/__/
   \/__/     \/__/     \/__/   \/__/     \/__/

Backend tests Code style: black

INLOOP, the INteractive Learning center for Object-Oriented Programming, is a Python web application to manage online programming courses, powered by Git, Django and Docker:

  • Flexible: you can write tasks for arbitrary programming languages, as long as they can be packaged into a Docker image.
  • Secure: solutions are checked inside a minimal and strictly isolated Docker container, which is discarded after execution.
  • Modern: push-based (or periodic pull-based) import of tasks, Markdown task descriptions and accompanying material from a Git repository.
  • Fast & scalable: solutions are processed asynchronously by a configurable number of background workers.
  • Easy to extend: modern, modular and PEP-8 compliant Python 3 code base with less than 3000 lines of code, good test coverage and continuous integration tests.

Quick start

INLOOP requires the following software:

Using the command line, a development instance can be set up as follows:

  1. If you don't have it already, install poetry and nox. If you run into trouble, the known "good" versions of the build tools are listed in tools/ci-requirements.txt.

  2. Install and check the setup of required software with ./support/scripts/debian_setup.sh (Debian/Ubuntu) or ./support/scripts/macos_setup.sh (macOS). The script complains if it finds things that are not configured correctly and displays hints how to fix it.

  3. On macOS, please run:

        export CFLAGS="-I$(brew --prefix [email protected])/include $CFLAGS"
        export LDFLAGS="-L$(brew --prefix [email protected])/lib $LDFLAGS"
    

    (This is necessary on Python 3.7 or newer to build the psycopg2 library.)

  4. Run make init and make loaddb to bootstrap your local development environment.

  5. Start and monitor the development web server and huey workers with make run.

  6. The previous step prints the address and port number of the local webserver that was started. You can immediately log in using the demo user accounts admin, staff, and student with the password secret.

You can use our Java example task repository as a starting point to explore INLOOP.

Demo

INLOOP demo on YouTube

Documentation

Publications

Authors and credits

Project lead: Martin Morgenstern
Contributors: Dominik Muhs, Philipp Matthes, Julian Catoni

INLOOP borrows its concept from the Praktomat, developed originally at the University of Passau and later at the Karlsruhe Institute of Technology:

License

INLOOP is released under GPL version 3 (see LICENSE.txt).

Copyright (C) 2015-2022 The INLOOP authors.

INLOOP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

INLOOP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

inloop's People

Contributors

dmuhs avatar freakyblue avatar martinmo avatar philippmatthes avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

inloop's Issues

Flatpages result in 404

When clicking on a link that is supposed to show a simple flatpage, the server responds with a 404 and returns No FlatPage matches the given query.

This bug can be resolved by moving the actual templates from the home module to an active part of the system and updating the urlconf to redirect all flatpage queries (including a catchall pattern)

Unpythonic getters/ setters in account models

As @martinmo pointed out in 9eef9a0, there are some unpythonic uses of getattr() and setattr() in accounts/models.py. During development I supposed that only they were calling __getattr__() and the like to get the model field's value. However, the methods are also called by the dotted notation. The functions are only meant for dynamic getting and setting of methods and variables and unnecessary here. They should be replaced by the dotted notation.

Chrome ignores autocomplete in registration form

Problem

When accessing the registration form, Chrome automatically fills in the user data it has already saved (username and password) for the domain even though the autocomplete="off" attribute has been set for the input. Adding the attribute to the form-tag didn't change the result.

Possible solution

The templates are correctly rendered with autocomplete="off" attbributes written into their widget. That's why I am certain this is a bug in Google Chrome ignoring an HTML5 standard. I will do some research on a workaround.

Category tiles on start page

After logging in, all tasks are listed on one page. It would be nice if there was a menu (e.g., represented as tiles as the "new tab" page in Google Chrome or Firefox) linking to the different categories:

+-----------------+--------------------+
| Basic exercises | Advanced exercises |
|           (4/5) |              (2/8) |
+-----------------+--------------------+
| Past exams      |                    |
|          (0/12) |                    |
+-----------------+--------------------+

It would also be nice if the number of already solved tasks is visible.

NoReverseMatch at /tasks/

Problem

The auth_logout view cannot be found with reverse matching even though it is located in the accounts module (just as the auth_login view which doesn't cause an error).

Steps to reproduce the bug

Simply log in as a user (doesn't matter whether admin or regular test user).

Traceback

Environment:


Request Method: GET
Request URL: http://localhost:8000/tasks/

Django Version: 1.7.2
Python Version: 2.7.6
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.sites',
 'django.contrib.staticfiles',
 'django.contrib.flatpages',
 'tinymce',
 'accounts',
 'tasks')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware')


Template error:
In template /home/spoons/django-dev/tud_praktomat_neu/templates/base.html, error at line 23
   Reverse for 'auth_logout' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'$logout/$']
   13 :   <div id="navbar">


   14 :     <ul id="menu">


   15 :       {% if user.is_anonymous %}


   16 :       <li><a href="{% url 'index' %}">Home</a></li>


   17 :       <li><a href="{% url 'about' %}">About</a></li>


   18 :       <li><a href="{% url 'accounts:auth_login' %}">Login</a></li>


   19 :       {% else %}


   20 :       <li><a href="{% url 'tasks:index' %}">Home</a></li>


   21 :       <li><a href="#">{{ user.get_username }}</a></li>


   22 :       <li><a href="#">Settings</a></li>


   23 :       <li><a href=" {% url 'accounts:auth_logout' %} ">Logout</a></li>


   24 :       {% endif %}


   25 :     </ul>


   26 :   </div>


   27 :   <div id="content">


   28 :     {% block content %}


   29 :     {% endblock %}


   30 :   </div>


   31 :   <div id="footer">


   32 : 


   33 :   </div>


Traceback:
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  111.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/home/spoons/django-dev/tud_praktomat_neu/tasks/views.py" in index
  18.           'exam_tasks' : exam_tasks,
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/shortcuts.py" in render
  48.     return HttpResponse(loader.render_to_string(*args, **kwargs),
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/loader.py" in render_to_string
  178.         return t.render(context_instance)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  148.             return self._render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  126.         return compiled_parent._render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  312.                 return nodelist.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  458.                         six.reraise(*exc_info)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  444.             url = reverse(view_name, args=args, kwargs=kwargs, current_app=context.current_app)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/urlresolvers.py" in reverse
  551.     return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/urlresolvers.py" in _reverse_with_prefix
  468.                              (lookup_view_s, args, kwargs, len(patterns), patterns))

Exception Type: NoReverseMatch at /tasks/
Exception Value: Reverse for 'auth_logout' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'$logout/$']

Add support for production deployment

This is a meta issue. I will open more issues relating to the following work items:

  • Ensure the app runs fine on a real webserver (nginx/gunicorn) with a real database backend (PostgreSQL)
  • Prepare deployment-ready settings file (e.g., generate a private SECRET_KEY)
  • Add deployment helper scripts/tools, if needed (Fabric?)
  • Add deployment documentation

RuntimeWarning: Naive datetime

When running ./manage.py tests the output will contain the following:

(dj17)spoons@xps:~/django-dev/tud_praktomat_neu$ ./manage.py test
Creating test database for alias 'default'...
/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py:1282: RuntimeWarning: DateTimeField UserProfile.date_joined received a naive datetime (2015-03-24 18:44:36.887420) while time zone support is active.
  RuntimeWarning)

./home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py:1282: RuntimeWarning: DateTimeField UserProfile.date_joined received a naive datetime (2015-03-24 18:44:36.890318) while time zone support is active.
  RuntimeWarning)

....
----------------------------------------------------------------------
Ran 5 tests in 0.281s

OK
Destroying test database for alias 'default'...

This is due to the fact that normal datetime objects have been used that don't comply to Django's timezone-aware datetime objects. The fix is simple, however there are some things to keep in mind when working with fixtures, which is going to be a thing in the future when the developer setup should be improved by initial datasets.

Add exercise management

To handle large amounts of exercises, there should be a separate module. It should take care of the following:

  • save description and task specs
  • save task unittests
  • allow editing
  • handle file system access
  • export and import tasks

File system structure in the media directory should be as follows

|-- media
|   |-- exercises
|       |-- HelloWorld
|       |   |-- unittests
|       |   |   |-- TestCapitalLetters.java
|       |   |   |-- TestPrintFunction.java
|       |   |   `-- TestEnglishLanguage.java
|       |   |-- HelloWorld.java
|       |   `-- ExampleFile.java
|       | 
|       `-- LibraryInventory
|           |-- unittests
|           |   |-- TestEmptyInput.java
|           |   `-- TestRunningOutOfIdeas.java
|           `-- LibraryInventory.java

The files only serve as a template (not in a Django context) and provide a common base for all users to start with the task. Furthermore, this design ensures that the unittests can only be changed by a privileged user.

Add registration mechanism

Currently the admin has to create all users. The goal is a registration form where users can enter their own data and create an account. The server then sends a message containing an activation link to the given email address. Once it is clicked, the account gets activated.

Add validator for category creation

In the category creation, there is still a great need for a validator. It should make sure that short ID's that are contained in the URL format should contain only numbers and letters, while they should not start with a number.

Find a cool new project (and repository) name

tud_praktomat_neu ... no! Maybe we can also find an alternative to Praktomat in order to distinguish our software from the original one from KIT. prktmt is quite cool, but I think people will have a hard time to memorize it correctly. Here are some proposals from the lab:

DRACHE - DResdens Awesome Code Hacking Environment
SOFTEIS - SOFtware Test EInheit für Studenten
SEB - Software Engineering Butler
POOL - Practical Object Oriented Learning
POOSTL - Practical OO Software Technology Learning

We should use this thread to collect more ideas...

Test failure after rebase

After making a git rebase in the tests branch, the following output is given:

(dj17)spoons@xps:~/django-dev/tud_praktomat_neu$ ./manage.py test
Creating test database for alias 'default'...
EE...
======================================================================
ERROR: test_invalid_inputs (tasks.tests.TaskModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/spoons/django-dev/tud_praktomat_neu/tasks/tests.py", line 16, in setUp
    slug='active-task'
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/manager.py", line 92, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/query.py", line 370, in create
    obj = self.model(**kwargs)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/base.py", line 440, in __init__
    setattr(self, field.name, rel_obj)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 597, in __set__
    self.field.rel.to._meta.object_name,
ValueError: Cannot assign "'unittest'": "Task.author" must be a "UserProfile" instance.

======================================================================
ERROR: test_task_is_active (tasks.tests.TaskModelTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/spoons/django-dev/tud_praktomat_neu/tasks/tests.py", line 16, in setUp
    slug='active-task'
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/manager.py", line 92, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/query.py", line 370, in create
    obj = self.model(**kwargs)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/base.py", line 440, in __init__
    setattr(self, field.name, rel_obj)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 597, in __set__
    self.field.rel.to._meta.object_name,
ValueError: Cannot assign "'unittest'": "Task.author" must be a "UserProfile" instance.

----------------------------------------------------------------------
Ran 5 tests in 0.279s

FAILED (errors=2)
Destroying test database for alias 'default'...

This is due to the fact that the setUp method is assigning a String to Task.author instead of a UserProfile instance. The mistake is obvious, however, everything seemed to work fine before the rebase - even though neither the Task nor the UserProfile models changed.

NoReverseMatch at /tasks/

Problem

The object url of a task - generated based on the slug - cannot be reverse matched when generating the task table once a user logs in and the index page is supposed to be rendered.

Error

Reverse for 'detail' with arguments '(u'hello-world',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'$task/(?P<slug>[-\\w]+)/$']

The error indicates that this is a fine-tuning issue in the allocation of names and url generation.

Traceback

Environment:


Request Method: GET
Request URL: http://localhost:8000/tasks/

Django Version: 1.7.2
Python Version: 2.7.6
Installed Applications:
('django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.sites',
 'django.contrib.staticfiles',
 'django.contrib.flatpages',
 'tinymce',
 'accounts',
 'tasks')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware')


Template error:
In template /home/spoons/django-dev/tud_praktomat_neu/tasks/templates/tasks/index.html, error at line 16
   Reverse for 'detail' with arguments '(u'hello-world',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'$task/(?P<slug>[-\\w]+)/$']
   6 :             {% for task in basic_tasks %}
   7 :                 {% if forloop.first %}
   8 :                     <tr>
   9 :                         <th>Title</th>
   10 :                         <th>Published</th>
   11 :                         <th>Due to</th>
   12 :                         <th>Completed</th>
   13 :                     </tr>
   14 :                 {% endif %}
   15 :                 <tr>
   16 :                     <td><a href=" {% url 'tasks:detail' task.slug %} ">{{ task.title }}</a></td>
   17 :                     <td>{{ task.publication_date }}</td>
   18 :                     <td>{{ task.deadline_date }}</td>
   19 :                     <td>TODO</td>
   20 :                 </tr>
   21 :             {% empty %}
   22 :                 No Basic exercises to do.
   23 :             {% endfor %}
   24 :         </table>
   25 :         <br/>
   26 : 


Traceback:
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  111.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/home/spoons/django-dev/tud_praktomat_neu/tasks/views.py" in index
  19.     'exam_tasks': exam_tasks,
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/shortcuts.py" in render
  48.     return HttpResponse(loader.render_to_string(*args, **kwargs),
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/loader.py" in render_to_string
  178.         return t.render(context_instance)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  148.             return self._render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  126.         return compiled_parent._render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in _render
  142.         return self.nodelist.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  65.                 result = block.nodelist.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/base.py" in render
  844.                 bit = self.render_node(node, context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/debug.py" in render_node
  80.             return node.render(context)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  201.                             nodelist.append(node.render(context))
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  458.                         six.reraise(*exc_info)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  444.             url = reverse(view_name, args=args, kwargs=kwargs, current_app=context.current_app)
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/urlresolvers.py" in reverse
  551.     return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs))
File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/urlresolvers.py" in _reverse_with_prefix
  468.                              (lookup_view_s, args, kwargs, len(patterns), patterns))

Exception Type: NoReverseMatch at /tasks/
Exception Value: Reverse for 'detail' with arguments '(u'hello-world',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'$task/(?P<slug>[-\\w]+)/$']

ImportError during migration

When tinymce was removed as user editor to pave the way for Markdown deployment, the django-tinymce dependency was removed from dev-requirements.txt in the root folder. However, tinymce is still being used as flatpage editor in the django-flatpages-tinymce module, which obviously depends on a regular tinymce installation.

% ./manage.py migrate     
Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/spoons/github/INLOOP/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/home/spoons/github/INLOOP/venv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 354, in execute
    django.setup()
  File "/home/spoons/github/INLOOP/venv/local/lib/python2.7/site-packages/django/__init__.py", line 21, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/home/spoons/github/INLOOP/venv/local/lib/python2.7/site-packages/django/apps/registry.py", line 85, in populate
    app_config = AppConfig.create(entry)
  File "/home/spoons/github/INLOOP/venv/local/lib/python2.7/site-packages/django/apps/config.py", line 87, in create
    module = import_module(entry)
  File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
    __import__(name)
ImportError: No module named tinymce

Therefore the tinymce dependency should be temporarily reintroduced until the flatpages will also be rendered in Markdown and there really are no more dependencies.

Add a custom error page

In case the server experiences an internal error (404, 500, etc.) and DEBUG = False in the settings, a generic error page should be shown.

Better Category Management

Currently the categories are managed by a form where the categories from the database are dynamically added as fields. This doesn't look well on the code-side and styling the template-side is hard enough, too. It would be wiser to add specialized views for deletion and editing and only reference them with links or buttons.

Switch to Python 3.x ASAP

As we have discussed in the last meeting, Python 2.x is not recommended for new projects, and we should migrate to Python 3.x now.

Fortunately, our source base is still very small (about 800 LOC) and a quick check shows only print statement syntax errors for Python 3, e.g.:

$ python3 -m compileall accounts tasks prktmt
[...]
Compiling 'accounts/validators.py'...
***   File "accounts/validators.py", line 6
    print "Validation of mat_num failed!"
                                        ^
SyntaxError: Missing parentheses in call to 'print'

Compiling 'accounts/views.py'...
***   File "accounts/views.py", line 21
    print error_msg % user_form.errors
                  ^
SyntaxError: Missing parentheses in call to 'print'
[...]

The third party libs used in our project work fine with Python 3.

IntegrityError at /tasks/new_exercise/

Problem

When submitting a new task with a certain category, an IntegrityError is thrown if there already is a task in that category: UNIQUE constraint failed: tasks_task.category_id.

However, the relation between the task object and its category is expressed by a ForeignKey which is a many-to-one relationship. There should be no such constraint.

Relevant POST Info

e_dead_date: u'04/18/2016 18:27'
e_desc: u'test'
e_pub_date: u'04/17/2015 18:27'
e_title: u'Test'
e_cat: u'advancedstuff'

Traceback

Traceback:
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  111.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/home/spoons/tud_praktomat_neu/tasks/views.py" in submit_new_exercise
  227.                 slug=slugify(unicode(form.data['e_title'])))
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/manager.py" in manager_method
  92.                 return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/query.py" in create
  372.         obj.save(force_insert=True, using=self.db)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/base.py" in save
  589.                        force_update=force_update, update_fields=update_fields)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/base.py" in save_base
  617.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/base.py" in _save_table
  698.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/base.py" in _do_insert
  731.                                using=using, raw=raw)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/manager.py" in manager_method
  92.                 return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/query.py" in _insert
  921.         return query.get_compiler(using=using).execute_sql(return_id)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  920.                 cursor.execute(sql, params)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
  81.             return super(CursorDebugWrapper, self).execute(sql, params)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
  65.                 return self.cursor.execute(sql, params)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/utils.py" in __exit__
  94.                 six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/backends/utils.py" in execute
  65.                 return self.cursor.execute(sql, params)
File "/home/spoons/tud_praktomat_neu/venv/local/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py" in execute
  485.         return Database.Cursor.execute(self, query, params)

Exception Type: IntegrityError at /tasks/new_exercise/
Exception Value: UNIQUE constraint failed: tasks_task.category_id

Add solution management

To handle the user submitted solutions and store them, a separate module should be introduced. It should provide the following functionality:

  • store the solutions of all users and associate them with their profile
  • hand them to the checker and present an overview of the submitted data
  • load data from existing solutions and present them in the editor

The file system structure in the media folder should be as follows:

|-- media
|   |-- solutions
|       |-- l33thax0r
|       |   |-- HelloWorld
|       |       |-- '2015-02-15 17:00'
|       |       |   |-- HelloWorld.java
|       |       |   |-- TestFile.java
|       |       |   `-- Results.txt
|       |       |
|       |       `-- '2015-02-16 08:00'
|       |           |-- HelloWorld.java
|       |           |-- TestFile.java
|       |           `-- Results.txt
|       |
|       `-- test_user
|           |-- HelloWorld
|               |-- '2015-02-10 12:00'
|               |   |-- HelloWorld.java
|               |   |-- TestFile.java
|               |   `-- Results.txt
|               |
|               `-- '2015-02-12 03:00'
|                   |-- HelloWorld.java
|                   |-- TestFile.java
|                   `-- Results.txt

The date format should comply with the ISO standards and look like this:

In [20]: datetime.datetime.now().strftime("%Y-%m-%d %I:%M")
Out[20]: '2015-02-16 08:22'

The result file should hold the JVM traceback data and some stats that the checker will return. There should be a mechanism that automatically deletes old solution versions once a certain number (e.g. 8) is reached, but also offer the user to recover some earlier submitted versions.

Optional: A functionality to save the solutions (without having them handed to the checker) to save progress.

Old and scheduled tasks are still shown

When entering the index view, all tasks are shown - even when their publication dates lie in the future or their deadline dates have already been met. The querysets need better filtering!
And maybe some tests to make sure this never happens again...

Add a generic checker implementation

The core functionality should be a module that takes user-submitted file, runs a test against it and returns the output. The checker will have access to the media directory where templates and user solutions are stored.

Notes:

  • Tests are currently JUnit tests, but in future it should be possible to easily extend it to Checkstyle checks, Findbugs analysis etc.
  • The compilation process must be started before
  • Because the tests run untrusted user-submitted code, there must be a strict isolation from the system

What resources should be restricted or isolated:

  • (CPU) time
  • File access
  • Network access

Sandboxing techniques:

  • Java Security Manager (used in the old Praktomat)
  • POSIX resource limits
  • cgroups on Linux

More Form Feedback

The registration and login forms are still lacking feedback of missing or invalid fields.

Provide important or just useful information in solution editor

The source code editor could show important information or useful tips concerning the code entered.

Here's a list of things the editor could show:

  • Warning/error if there is a package declaration
  • Standard programming warnings, e.g. use of = instead of == in a condition, use of == to compare String objects, assignments like a=a

If you have another idea, just add it here.

Embed Ace editor into user page

The templates have to be modified in a way that the Ace code editor is available for the user. The following things have to be done:

  • embed the editor as described in the guide here
  • implement JavaScript to change the default properties as described here
  • let Django insert the database text (user or default) into the ace-div
  • offer the option to reset the text to the default content (delete all user progress)

InvalidBasesError when executing syncdb and migrate

Referring to Django 1.7 Bugtracker Ticket 22708 leading to Django 1.7 Bugtracker Ticket 22965:
django.contrib.auth is unmigrated and depends on the migrated custom user model.

Possible solution: Creating a "fake" custom user model as in 22965.

Steps to reproduce the bug

  1. git clone https://github.com/st-tu-dresden/tud_praktomat_neu.git
  2. Switch to virtualenv with required libraries
  3. ./migrate.py syncdb

The error also occurs when ./manage.py migrate is issued, which has the same functionality as syncdb when run for the first time.

Traceback

Operations to perform:
  Synchronize unmigrated apps: accounts, tasks, tinymce
  Apply all migrations: sessions, admin, sites, flatpages, contenttypes, auth
Synchronizing apps without migrations:
  Creating tables...
    Creating table accounts_user
    Creating table tasks_task
    Creating table tasks_taskfile
  Installing custom SQL...
  Installing indexes...
Running migrations:
  Applying contenttypes.0001_initial...Traceback (most recent call last):
  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/base.py", line 533, in handle
    return self.handle_noargs(**options)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/commands/syncdb.py", line 27, in handle_noargs
    call_command("migrate", **options)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 115, in call_command
    return klass.execute(*args, **defaults)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 161, in handle
    executor.migrate(targets, plan, fake=options.get("fake", False))
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 68, in migrate
    self.apply_migration(migration, fake=fake)
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 96, in apply_migration
    if self.detect_soft_applied(migration):                                                                                                     
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 140, in detect_soft_applied            
    apps = project_state.render()
  File "/home/spoons/venv/dj17/local/lib/python2.7/site-packages/django/db/migrations/state.py", line 75, in render
    "for more" % new_unrendered_models
django.db.migrations.state.InvalidBasesError: Cannot resolve bases for [<ModelState: 'accounts.User'>]
This can happen if you are inheriting models from an app with migrations (e.g. contrib.auth) in an app with no migrations; see https://docs.djangoproject.com/en/1.7/topics/migrations/#dependencies for more

Write file upload validators

The file upload functionality in the ExerciseEditForm and ExerciseSubmissionForm currently does not validate the given files in any way and uploads everything given to it to the server into the respective folders.

This way of handling files is prone to denial of service attacks that aim at completely filling up the server's space. The correct way to avoid this would be validators that check the file headers, their size and ending.

The validators can either be added as a form validator or inside the filesystem utils.

Catch ValueError for registration

In the registration process, when entering a wrong matriculation number, the server displays a ValueError message, even though the mat_num_validator is supposed to raise a ValidationError.

A possible fault could lie in the uses of the validator in the registration form and the model

It could also be normal behaviour, as the ValidationError is handed up the framework internals until another exception is thrown. If that's the case, a proper error message should be displayed in the registration template to let the user know.

Form feedback

As for now the forms don't give any feedback to the user except for redirect success messages. There still is a need for error messages that inform the user about a failed validation or missing content.

Set correct permissions

As for now the interface changes depending on the rights of the logged in user. However, if a link like http://localhost:8000/accounts/new_course/ is directly entered, it is also possible for non-staff members to access the functionality. This should be fixed by manipulating the permission decorators attached to the views.

Submit and download solutions

In addition to the editor displayed in the browser, the user must be able to submit and download solution files from and to his or her computer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.