Ekratia’s documentation!

Ekratia is a new way of making democracy. Members of Ekratia use the Internet to vote directly for new ideas and laws. In case they are too busy to vote, members can delegate their vote to other members. Members can change their delegates on real time.

Contents:

Installation

Clone the Ekratia repository:

$ git clone git@github.com:ekratia/ekratia.git

This project was created using cookiecutterdjango_

Setup project locally

The steps below will get you up and running with a local development environment. We assume you have the following installed:

  • pip
  • virtualenv
  • PostgreSQL

First make sure to create and activate a virtualenv, then open a terminal at the project root and install the os dependencies:

$ sudo ./install_os_dependencies.sh install

Then install the requirements for your local development:

$ pip install -r requirements/local.txt

Then, create a PostgreSQL database with the following command. We will call the database ekratia

$ createdb ekratia

You can now run the usual Django migrate and runserver command:

$ python manage.py migrate

$ python manage.py runserver

Live reloading and Sass CSS compilation

If you’d like to take advantage of live reloading and Sass / Compass CSS compilation you can do so with the included Grunt task.

Make sure that nodejs is installed. Then in the project root run:

$ npm install

Now you just need:

$ grunt serve

The base app will now run as it would with the usual manage.py runserver but with live reloading and Sass compilation enabled.

To get live reloading to work you’ll probably need to install an appropriate browser extension

Run Unit Tests

python manage.py test

Front-end Application

We use bower to manage the Front-end dependencies. The project already has a compiled and minified version of the dependencies. So you only need to run it when adding new dependencies.

npm -g install bower
bower install

Path of front-end libraries: /bower_components

API

API endpoints used in the Ekratia application to be consumed inside the application.

The endpoints are located inside /api/v1/<endpoint>

Users

class ekratia.users.api.UserView(**kwargs)

API User details Endpoint

model

alias of User

serializer_class

alias of UserSerializer

class ekratia.users.serializers.UserSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)

Serializer for User Model

Conversations

class ekratia.conversations.api.ThreadDetail(**kwargs)

API class to retrieve, update or delete a thread instance.

serializer_class

alias of ThreadSerializer

class ekratia.conversations.api.ThreadList(**kwargs)

API class to list all threads, or create a new thread.

serializer_class

alias of ThreadSerializer

Delegates

class ekratia.delegates.api.AssignedDelegates(**kwargs)

API class for User Delegates

model

alias of User

serializer_class

alias of UserSerializer

class ekratia.delegates.api.AvailableDelegates(**kwargs)

API class for Users Listing

get_queryset()

Lists the users of the system name: Optional name to filter the user

serializer_class

alias of UserSerializer

class ekratia.delegates.api.UserDelegateDetail(**kwargs)

Delegate Detail View Class

serializer_class

alias of UserDelegateSerializer

Referendums

class ekratia.referendums.api.ReferendumComments(**kwargs)

List Comments of the referendum in a Tree

create_root_comment(referendum)

Creates a root Comment

get(request, pk, format=None)

Lists the tree of comments for the thread

get_referendum(pk)

Get Referendum by Primary Key

update_information_from_tree(tree)

Recursive function to go through tree and update information Updating: User information, Vote value

Graphs

Ekratia uses Networkx library to operate with graphs. GraphEkratia is a class that extends from DiGraph and implements required methods for the Ekratia vote system.

class ekratia.core.graphs.GraphEkratia(*args, **kwargs)

Extends Digraph Class to add methods used in the voting system.

  • How to use it:

    graph = GraphEkratia()
    graph.add_user_id(1)
    graph.add_users_ids([1,2,3])
    
add_user_id(user_id)

Add a nodes from user_id

Param:user_id User Id.
add_users_ids(users_ids)

Adds nodes to the graph from a list of user ids

Param:list users_ids: List of user ids
attach_predecessors(node)

Attach the predecessors of a node

Param:node Id.
attach_succesors(node)

Attach the succesors of a node

Param:node Id.
get_pagerank_values()

dictionary of pagerank values for the nodes of the graph

Param:user_id User Id.
Returns:pagerank calculation
Return type:dict
get_sigma_representation()

Representation for Sigma JS library

Returns:dict of nodes and edges
Return type:dict
get_user_id_delegates(user_id)

returns a list of ids of succesors of the node

Param:user_id User Id.
Returns:list of user ids
Return type:list
get_user_id_delegates_to_me(user_id)

returns a list of ids of predecessors of the node

Param:user_id User Id.
Returns:list of user ids
Return type:list
queue_node(node)

Queue a node for future revision

Param:node Id.
retrieve_node()

Retrieves a node from the queue

Returns:node
Return type:int
set_exclude_list(users_ids)

Excludes list of ids from the Graph

Parameters:users_ids (list) – List of users ids
visit_node(node)

Add a nodes to visited list

Param:node Id.

Users

Application in charge to handle all the information and methods of the users.

Models

class ekratia.users.models.User(id, password, last_login, is_superuser, username, first_name, last_name, email, is_staff, is_active, date_joined, rank)
delegate_to(user)

Creates a delegated user Return the Delegate object

get_available_delegates(name=None)

Get users not delegated by me and exclude myself @param name: Filters available users by name

get_avatar

Gets avatar from Django Avatar or Facebook

get_data_dictionary()

Return dictionary with basic information of the user

get_full_name_or_username

Get Full Name or Username

get_graph()

Creates a graph for the user based on Delegation

get_users_delegated_by_me()

Returns users delegated by me

get_users_delegated_to_me()

Returns users that delegated me

get_vote_referendum(referendum)

Return the value vote on a referendum if exists

undelegate_to(user)

Undelegate an user Returns True if success

vote_count_for_referendum(referendum)

Calculates vote value depending on Delegates

Views

class ekratia.users.views.UserDetailView(**kwargs)

User Detail View. Displays the details for an user

get_context_data(**kwargs)

Insert the single object into the context dict.

model

alias of User

class ekratia.users.views.UserListView(**kwargs)

List users view

model

alias of User

class ekratia.users.views.UserUpdateView(**kwargs)

Update Users View

model

alias of User

Delegates

Models

class ekratia.delegates.models.Delegate(*args, **kwargs)

Delegate Model stores the delegations made by user in the system.

delegate

User to delegate

user

User delgating

Views

class ekratia.delegates.views.UserDelegatesView(**kwargs)

View with an Interface to list the delegated users and the available users to be delegated.

Referendums

Models

class ekratia.referendums.models.Referendum(*args, **kwargs)

Entity that refers to a proposal made by an user to modify the rules. It is opened for vote for a period of time and the members vote to approve it or not.

approved = None

True If approved, False if not approved, None not set yet

calculate_votes()

Calculates total votes based on ReferendumUserVote

check_status()

Method used to update status when necessary

comment

Comment thread

comment_points = None

Rating due to the comments, used to establish trendy referendums

count_comments()

Counts the comments of the referendunm, updates the value and returns the count.

Returns:count of comments
Return type:int
date = None

Date of creation

get_count_votes()

Calculates total votes for referenudm

get_graph()

Get graph of the referendum

Returns:Graph of the referendum
Return type:ekratia.core.graphs.GraphEkratia
get_num_negative_votes()

Return the total negative votes

get_num_positive_votes()

Returns total positive votes

get_total_votes()

Calculates total votes for referenudm

get_total_votes_absolute()

Returns Total ov votes for the referendum

is_approved()

Get partial results and determines if It’s approved or not

Returns:True or False
Return type:boolean
is_finished()

Method to establish id Referendum is open for vote.

Returns:True or False
Return type:boolean
is_open()

Method to establish id Referendum is open for vote.

Returns:True or False
Return type:boolean
num_comments = None

Number of comments

open_remaining_time()

Returns the remaining time for vote.

Returns:Remianing time for referendum to finish
Return type:datetime.timedelta()
open_time = None

Open Time of Referendum

points = None

Total Value of the Referndum

slug = None

Slugify version of the title

status = None

Status options: created, open, finished

text_add_rules = None

Text that will add to rules

text_remove_rules = None

Rules that will remove from rules

title = None

Title of the referendum

total_yes = None

Total Values for Stats

update_totals()

Update totals in the Database Returns the updated referendum

user

User who created referendum

vote_process(user, value)

Processes a vote for the referendum

Param:user User object
Param:value Vote value (-1 or 1)
class ekratia.referendums.models.ReferendumUserVote(*args, **kwargs)

ReferendumUserVote Model: Stores votes from users to Referendums

date = None

Time of the vote

referendum

ekratia.referendums.models.Referendum

user

ekratia.users.models.User

value = None

Vote Value

Views

class ekratia.referendums.views.ReferendumArchivedListView(**kwargs)

List of Referendums

model

alias of Referendum

class ekratia.referendums.views.ReferendumCreateView(**kwargs)

Creates a Referendum

form_class

alias of ReferendumForm

form_valid(form)

If the form is valid, save the associated model.

model

alias of Referendum

class ekratia.referendums.views.ReferendumDetailView(**kwargs)

Detail View for a Referendum

get_context_data(**kwargs)

Insert the single object into the context dict.

model

alias of Referendum

class ekratia.referendums.views.ReferendumListView(**kwargs)

List of Referendums

class ekratia.referendums.views.ReferendumOpenView(*args, **kwargs)

Open Referendum and redirects back to Referendum

class ekratia.referendums.views.ReferendumProcessVoteView(*args, **kwargs)

Process refrendum vote and redirects back to Referendum

class ekratia.referendums.views.ReferendumResultsView(**kwargs)

Referendum results

get_context_data(**kwargs)

Insert the single object into the context dict.

model

alias of Referendum

class ekratia.referendums.views.ReferendumVoteView(**kwargs)

Detail View for a Referendum

get_context_data(**kwargs)

Insert the single object into the context dict.

Conversations

Models

class ekratia.threads.models.Comment(*args, **kwargs)

Comment Model: Comments under Threads and other comments

calculate_votes()

Calculates total votes based on CommentUserVote

class ekratia.threads.models.CommentManager

Manager for the comment model. Allows custom operation on the tree.

class ekratia.threads.models.CommentUserVote(*args, **kwargs)

CommentUserVote Model: Stores votes from users to Comments

Views

class ekratia.conversations.views.ThreadCreateView(**kwargs)

Creates a Thread

form_class

alias of ThreadForm

form_valid(form)

If the form is valid, save the associated model.

model

alias of Thread

class ekratia.conversations.views.ThreadDetailView(**kwargs)

Detail View for a Thread

get_context_data(**kwargs)

Insert the single object into the context dict.

model

alias of Thread

class ekratia.conversations.views.ThreadListView(**kwargs)

List of Threads

model

alias of Thread

Settings

This project relies extensively on environment settings which will not work with Apache/mod_wsgi setups. It has been deployed successfully with both Gunicorn/Nginx and even uWSGI/Nginx.

For configuration purposes, the following table maps environment variables to their Django setting:

Environment Variable Django Setting Development Default Production Default
DJANGO_ADMIN_URL n/a r’^admin/’ raises error
DJANGO_CACHES CACHES (default) locmem redis
DJANGO_DATABASES DATABASES (default) See code See code
DJANGO_DEBUG DEBUG True False
DJANGO_SECRET_KEY SECRET_KEY CHANGEME!!! raises error
DJANGO_SECURE_BROWSER_XSS_FILTER SECURE_BROWSER_XSS_FILTER n/a True
DJANGO_SECURE_SSL_REDIRECT SECURE_SSL_REDIRECT n/a True
DJANGO_SECURE_CONTENT_TYPE_NOSNIFF SECURE_CONTENT_TYPE_NOSNIFF n/a True
DJANGO_SECURE_FRAME_DENY SECURE_FRAME_DENY n/a True
DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS HSTS_INCLUDE_SUBDOMAINS n/a True
DJANGO_SESSION_COOKIE_HTTPONLY SESSION_COOKIE_HTTPONLY n/a True
DJANGO_SESSION_COOKIE_SECURE SESSION_COOKIE_SECURE n/a False
DJANGO_DEFAULT_FROM_EMAIL DEFAULT_FROM_EMAIL n/a “your_project_name <noreply@your_domain_name>”
DJANGO_SERVER_EMAIL SERVER_EMAIL n/a “your_project_name <noreply@your_domain_name>”
DJANGO_EMAIL_SUBJECT_PREFIX EMAIL_SUBJECT_PREFIX n/a “[your_project_name] “
DJANGO_ALLOWED_HOSTS ALLOWED_HOSTS [‘*’] [‘your_domain_name’]

Deployment

This project relies on Environment variables.

Please make sure you define the variables according to the Settings page:

Deployment to Production on AWS

System requirements for Debian based servers: requirements.apt

Python requirements: requirements.txt

It was succesfully tested and mounted on AWS using Python Virtualenv, UWSGI and NGINX.

UWSGI Configuration

[uwsgi]
vhost = true
plugins = python
socket = /tmp/ekratia.sock
master = true
enable-threads = true
processes = 4
wsgi-file = /srv/apps/ekratia/config/wsgi.py
virtualenv = /home/ubuntu/.virtualenvs/env
chdir = /srv/apps/ekratia/

Nginx Configuration

server {
    listen 80;
    access_log /var/log/nginx/ekratia.access.log;
    error_log /var/log/nginx/ekratia.error.log;

    location / {
        uwsgi_pass      unix:///tmp/ekratia.sock;
        include     uwsgi_params;
    }
}

The used services were:

Amazon EC2
Amazon RDS PostgresSQL
Amazon S3

Indices and tables