+
+
+
+ Cabina de votación
+
+
+
+
Selecciona tu respuesta para
+ esta votación
+
+{% endblock %}
{% block extrahead %}
-
+
+
+
+ Decide-Cabina
+
+
+
+
Registrate para empezar con tus votaciones
+
+{% endblock %}
+
+{% block extrahead %}
+
+
+
+
+ Decide-Cabina
+
+
+
+
Panel de administrador para peticiones
+
+{% endblock %}
+
+{% block extrahead %}
+
+
+
+
+ Decide-Cabina
+
+
+
+
Panel de usuario para peticiones
+
+{% endblock %}
+
+{% block extrahead %}
+
+
+
+
+ Decide-Cabina
+
+
+
+
Selecciona tu respuesta para esta votación
+
+{% endblock %}
+
+{% block extrahead %}
+
+
¡Bienvenido/a a la cabina de votación de Decide-Rio Tinto!
+
+ {% if not user.is_authenticated %}
+
+ Te encuentras en la página de inicio. Si quieres participar en una votación debes logearte primero. Pulsa aqui para logearte.
+
+
+
+ Si no estas registrado, pulsa aquí para registrarte.
+
+ {% endif %}
+
+
+ Ten en cuenta que solo podrás participar en aquellas votaciones en las que un administrador te haya censado.
+
+
+ {% if user.is_authenticated %}
+ {% if not user.is_superuser %}
+
+ Si quieres solicitar ser censado en una votación, pulsa aquí
+
+ {% endif %}
+ {% endif %}
+
+ {% if user.is_superuser %}
+
+ Como administrador, puedes ver las peticiones actuales pulsando aquí
+
+ {% endif %}
+
+
+ Actualmente tenemos estás votaciones abiertas:
+
+
+
+
+
+
+ | Nombre de la votacion |
+ Id de la votacion |
+ Fecha de inicio |
+
+
+
+
+ {% for v in allVotaciones %}
+
+
+ | {{v.name}} |
+ {{v.id}} |
+ {{v.start_date}} |
+
+
+ {% endfor %}
+
+
+ {% if user.is_authenticated %}
+ {% if not listaVacia %}
+
Estas son las votaciones en las que estas censado:
+
+
+
+
+ | Nombre de la votacion |
+ Id de la votacion |
+ Fecha de inicio |
+
+
+
+
+ {% for a in votacionesCensado %}
+
+
+ | {{a.name}} |
+ {{a.id}} |
+ {{a.start_date}} |
+
+
+ {% endfor %}
+
+
+ {% endif %}
+ {% if listaVacia %}
+
Actualmente no estas censado en ninguna votacion.
+ {% endif %}
+ {% endif %}
+
+
+
+ Sobre nosotros
+
diff --git a/decide/booth/tests.py b/decide/booth/tests.py
index 7ce503c2dd..10106c122a 100644
--- a/decide/booth/tests.py
+++ b/decide/booth/tests.py
@@ -1,3 +1,295 @@
-from django.test import TestCase
+from django.test import Client
+from rest_framework.test import APIClient
+from rest_framework.test import APITestCase
+from django.urls import reverse
+from django.contrib.staticfiles.testing import StaticLiveServerTestCase
+
+from selenium import webdriver
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.common.by import By
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.common.action_chains import ActionChains
+
+from base import mods
+from base.tests import BaseTestCase
+from census.models import Census
+from .models import PeticionCenso
+
+from django.urls import reverse
+from voting.models import Voting
+from voting.serializers import VotingSerializer
+from .views import BoothView
+
# Create your tests here.
+class PeticionTestCase(BaseTestCase):
+
+ def setUp(self):
+
+ self.p = PeticionCenso(id=1, desc="Votacion 1", user_id=1)
+ self.p.save()
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+ self.p = None
+
+ def test_peticion_exists(self):
+ p = PeticionCenso.objects.get(id=1)
+ self.assertEquals(p.desc, "Votacion 1")
+ self.assertEquals(p.user_id, 1)
+
+ def test_peticion_notExists(self):
+ p = PeticionCenso.objects.get(id=1)
+ self.assertNotEquals(p.desc, "Votacion 2")
+ self.assertNotEquals(p.user_id, 2)
+
+ def test_peticion_toString(self):
+ p = PeticionCenso.objects.get(id=1)
+ self.assertEquals(str(p), "Votacion 1")
+
+ def test_bad_form_peticion(self):
+ self.login(user='admin', password='qwerty')
+ data = {'desc': ''}
+ peticiones = PeticionCenso.objects.all().count()
+ response = self.client.post('/booth/peticionCenso/', data, follow=True)
+ self.assertEqual(response.status_code, 200)
+ peticionesDespues = PeticionCenso.objects.all().count()
+ self.assertEqual(peticionesDespues, peticiones)
+
+
+'''class VotingTestCase(BaseTestCase):
+
+ def test_get_votings(self, mocker):
+ expected_results = [Voting(
+ voting_id=4,
+ name="EGC",
+ desc="Aprobar EGC no es fácil",
+ question(
+ yesorno="¿Vamos a aprobar EGC?",
+ options(
+ y="Yes",
+ n="No")),
+ start_date="2021-01-08T15:29:52.040435",
+ end_date=None,
+ url="http://localhost:8000/booth/4",
+ pubkey="a1s2d3f4g5h6j7k8l9",
+ voted=False
+ )]
+ qs = MockSet(expected_results[0])
+ mocker.patch.object(Voting.objects, 'get_queryset', return_value=qs)
+
+ result = list(Voting.objects.get_id(4))
+
+ assert result == expected_results
+ assert str(result[0]) == expected_results[0].code
+
+ def test_get_votings_fail(self, mocker):
+ expected_results = [Voting(
+ voting_id=4,
+ name="PGPI",
+ desc="Aprobar PGPI no es fácil",
+ question(
+ yesorno="¿Vamos a aprobar PGPI?",
+ options(
+ y="Yes",
+ n="No")),
+ start_date="2021-01-08T15:29:52.040435",
+ end_date=None,
+ url="http://localhost:8000/booth/4",
+ pubkey="a1s2d3f4g5h3j7k8l9",
+ voted=False
+ )]
+
+ qs = MockSet(expected_results[0])
+ mocker.patch.object(Voting.objects, 'get_queryset', return_value=qs)
+
+ result = list(Voting.objects.get_id(4))
+
+ assert result != expected_results
+ assert str(result[0]) != expected_results[0].code
+
+
+ def test_expected_serialized_json(self):
+ expected_results = {
+ "voting_id": 4,
+ "name": "EGC",
+ "desc": "Aprobar EGC no es fácil",
+ "question": {
+ "yesorno": "¿Vamos a aprobar EGC?",
+ "options": {
+ "y": "Yes",
+ "n": "No"}},
+ "start_date":"2021-01-08T15:29:52.040435",
+ "end_date":None,
+ "url":"http://localhost:8000/booth/4",
+ "pub-key": "a1s2d3f4g5h6j7k8l9",
+ "voted": False
+ }
+ voting = Voting(**expected_results)
+ results = VotingSerializer(voting).data
+
+ assert results == expected_results
+
+
+ def test_raise_error_when_missing_required_field(self):
+ incomplete_data = {
+ "voting_id": 4,
+ "desc": "Aprobar EGC no es fácil",
+ "question": {
+ "yesorno": "¿Vamos a aprobar EGC?",
+ "options": {
+ "y": "Yes",
+ "n": "No"}},
+ "start_date":"2021-01-08T15:29:52.040435",
+ "end_date":None,
+ "pub-key": "a1s2d3f4g5h6j7k8l9",
+ "voted": False
+ }
+
+ serializer = VotingSerializer(data=incomplete_data)
+
+ with pytest.raises(ValidationError):
+ serializer.is_valid(raise_exception=True)
+
+
+ def test_list(self, rf, mocker):
+ url = self.client.get('/booth/', follow=True)
+ request = rf.get(url)
+
+ queryset = MockSet(
+ Voting(
+ voting_id=4,
+ name="EGC",
+ desc="Aprobar EGC no es fácil",
+ question(
+ yesorno="¿Vamos a aprobar EGC?",
+ options(
+ y="Yes",
+ n="No")),
+ start_date="2021-01-08T15:29:52.040435",
+ end_date=None,
+ url="http://localhost:8000/booth/4",
+ pubkey="a1s2d3f4g5h6j7k8l9",
+ voted=False
+ )
+ )
+
+ mocker.patch.object(BoothView, 'get_queryset', return_value=queryset)
+ response = BoothView.as_view({'get': 'list'})(request).render()
+
+ assert response.status_code == 200
+ assert len(json.loads(response.content)) == 1'''
+
+# ..................................Test de Interfaz............................................
+
+
+class InterfazLogin(StaticLiveServerTestCase):
+
+ def setUp(self):
+ # Load base test functionality for decide
+ self.base = BaseTestCase()
+ self.base.setUp()
+
+ options = webdriver.ChromeOptions()
+ options.headless = True
+ self.driver = webdriver.Chrome(options=options)
+
+ super().setUp()
+
+ def tearDown(self):
+ super().tearDown()
+ self.driver.quit()
+
+ self.base.tearDown()
+
+ '''def test_login_success(self):
+ self.driver.get(f'{self.live_server_url}/booth/login/')
+ self.driver.find_element(By.CSS_SELECTOR, ".container > .d-flex").click()
+ self.driver.find_element(By.NAME, "username").send_keys("admin")
+ self.driver.find_element(By.NAME, "password").send_keys("qwerty")
+ self.driver.find_element(By.NAME, "password").send_keys(Keys.ENTER)
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/')'''
+
+ def test_login_wrong(self):
+ self.driver.get(f'{self.live_server_url}/booth/login/')
+ self.driver.find_element(By.CSS_SELECTOR, ".container > .d-flex").click()
+ self.driver.find_element(By.NAME, "username").send_keys("admin")
+ self.driver.find_element(By.NAME, "password").send_keys("no es la contraseña")
+ self.driver.find_element(By.NAME, "password").send_keys(Keys.ENTER)
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/login/')
+
+ '''def test_call_view_votings_authenticated(self):
+ response = self.client.get('/booth/', follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'booth/booth.html')
+
+ def test_call_view_voted_authenticated(self):
+ response = self.client.get(reverse('hasVotado'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'booth/hasVotado.html') '''
+
+ def test_wrongPassword(self):
+ self.driver.get(f'{self.live_server_url}/booth/register')
+ self.driver.find_element_by_id('id_username').send_keys("Alejandrogm1")
+ self.driver.find_element_by_id('id_email').send_keys("alejandrogm@gmail.com")
+ self.driver.find_element_by_id('id_password1').send_keys("ale")
+ self.driver.find_element_by_id('id_password2').send_keys("ale")
+ self.driver.find_element_by_css_selector('.botonLogin').click()
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/register/')
+ self.driver.find_elements(By.XPATH, "//p[contains(.,\'Esta contraseña es demasiado corta. Debe contener al menos 8 caracteres.\')]")
+
+ def test_wrongExistingUser(self):
+ self.driver.get(f'{self.live_server_url}/booth/register')
+ self.driver.find_element_by_id('id_username').send_keys("admin")
+ self.driver.find_element_by_id('id_email').send_keys("alejandrogm@gmail.com")
+ self.driver.find_element_by_id('id_password1').send_keys("passtest")
+ self.driver.find_element_by_id('id_password2').send_keys("passtest")
+ self.driver.find_element_by_css_selector('.botonLogin').click()
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/register/')
+ self.driver.find_elements(By.XPATH, "//p[contains(.,\'Ya existe un usuario con ese nombre.\')]")
+
+ def test_wrongConfirmPassword(self):
+ self.driver.get(f'{self.live_server_url}/booth/register')
+ self.driver.find_element_by_id('id_username').send_keys("Alejandrogm1")
+ self.driver.find_element_by_id('id_email').send_keys("alejandrogm@gmail.com")
+ self.driver.find_element_by_id('id_password1').send_keys("alegonmar")
+ self.driver.find_element_by_id('id_password2').send_keys("alegonmar2")
+ self.driver.find_element_by_css_selector('.botonLogin').click()
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/register/')
+ self.driver.find_elements(By.XPATH, "//p[contains(.,\'Los dos campos de contraseña no coinciden.\')]")
+
+ def test_wrongCommonPassword(self):
+ self.driver.get(f'{self.live_server_url}/booth/register')
+ self.driver.find_element_by_id('id_username').send_keys("Alejandrogm1")
+ self.driver.find_element_by_id('id_email').send_keys("alejandrogm@gmail.com")
+ self.driver.find_element_by_id('id_password1').send_keys("123456789")
+ self.driver.find_element_by_id('id_password2').send_keys("123456789")
+ self.driver.find_element_by_css_selector('.botonLogin').click()
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/register/')
+ self.driver.find_elements(By.XPATH, "//p[contains(.,\'Esta contraseña es demasiado común.\')]")
+
+ def test_wrongSimilarUsernamePassword(self):
+ self.driver.get(f'{self.live_server_url}/booth/register')
+ self.driver.find_element_by_id('id_username').send_keys("Alejandrogm1")
+ self.driver.find_element_by_id('id_email').send_keys("alejandrogm@gmail.com")
+ self.driver.find_element_by_id('id_password1').send_keys("alejandrogm")
+ self.driver.find_element_by_id('id_password2').send_keys("alejandrogm")
+ self.driver.find_element_by_css_selector('.botonLogin').click()
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/register/')
+ self.driver.find_elements(By.XPATH, "//p[contains(.,\'La contraseña es demasiado similar a la de nombre de usuario.\')]")
+
+ '''def test_correctRegister(self):
+ self.driver.get(f'{self.live_server_url}/booth/register')
+ self.driver.find_element_by_id('id_username').send_keys("Alejandrogm1")
+ self.driver.find_element_by_id('id_email').send_keys("alejandrogm@gmail.com")
+ self.driver.find_element_by_id('id_password1').send_keys("passtest")
+ self.driver.find_element_by_id('id_password2').send_keys("passtest")
+ self.driver.find_element_by_css_selector('.botonLogin').click()
+ self.assertEquals(self.driver.current_url, f'{self.live_server_url}/booth/login/')
+ self.driver.find_element_by_name('username').send_keys("Alejandrogm1")
+ self.driver.find_element_by_name('password').send_keys("passtest")
+ self.driver.find_element_by_css_selector('.btn').click()'''
+
diff --git a/decide/booth/urls.py b/decide/booth/urls.py
index b25a3fa6ed..a720ecebac 100644
--- a/decide/booth/urls.py
+++ b/decide/booth/urls.py
@@ -1,7 +1,16 @@
from django.urls import path
from .views import BoothView
-
+from . import views
urlpatterns = [
- path('
/', BoothView.as_view()),
+ path('/', BoothView.as_view(), name="booth"),
+ path('login/', views.loginPage, name="login"),
+ path('', views.welcome, name="welcome"),
+ path('logout/', views.logoutUser, name="logout"),
+ path('register/', views.registerPage, name="register"),
+ path('hasVotado/', views.hasVotado, name="hasVotado"),
+ path('peticionCenso/', views.peticionCensoUsuario, name="peticionCensoUsuario"),
+ path('peticionCensoAdmin/', views.peticionCensoAdmin, name="peticionCensoAdmin"),
+ path('deletePeticion//', views.deletePeticion, name="deletePeticion"),
+ path('about/', views.about, name="about"),
]
diff --git a/decide/booth/views.py b/decide/booth/views.py
index 7e560d2706..b1ac8e0a9a 100644
--- a/decide/booth/views.py
+++ b/decide/booth/views.py
@@ -1,31 +1,278 @@
import json
+import datetime
+
from django.views.generic import TemplateView
from django.conf import settings
from django.http import Http404
+from voting.models import Voting
from base import mods
+from voting.models import Voting
+from census.models import Census
+from store.models import Vote
+from django.shortcuts import render, redirect
+from django.http import HttpResponse
+from django.forms import inlineformset_factory
+from django.contrib.auth.forms import UserCreationForm
+
+from django.contrib.auth.models import User
+from django.contrib.auth import authenticate, login, logout
+
+from django.contrib import messages
+from django.contrib.auth.decorators import login_required
+from .forms import CrearUsuario, YesOrNoForm
+from voting.views import VotingView, VotingUpdate
+from voting.models import Voting, Question, PoliticalParty, YesOrNoQuestion
+from rest_framework.renderers import TemplateHTMLRenderer
+from lib2to3.fixes.fix_input import context
+from django.http import HttpResponseForbidden
+from .forms import CrearUsuario
+from .forms import PeticionForm
+from .models import PeticionCenso
+
+
+# Create your views here.
# TODO: check permissions and census
class BoothView(TemplateView):
+ renderer_classes = [TemplateHTMLRenderer]
template_name = 'booth/booth.html'
+ '''q = Question.objects.create(desc="¿Esto es un ejemplo?")
+ p = PoliticalParty.objects.create(name="Political23313", acronym="P223133", description="Esto es una descripción", leader="Líder2", president="Presidente2")
+ v = Voting.objects.create(name="Votación 1", desc="Esto es un ejemplo", question=q, political_party=p, start_date="2021-01-12 00:00", end_date="2021-01-30 00:00", url="http://localhost:8000/booth/")'''
def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- vid = kwargs.get('voting_id', 0)
+ y = {
+ "voting_id": 4,
+ "name": "EGC",
+ "desc": "Aprobar EGC no es fácil",
+ "question": {
+ "yesorno": "¿Vamos a aprobar EGC?",
+ "options": {
+ "y": "Yes",
+ "n": "No"}},
+ "start_date":"2021-01-08T15:29:52.040435",
+ "end_date":None,
+ "url":"http://localhost:8000/booth/4",
+ "pub-key": "a1s2d3f4g5h6j7k8l9",
+ "voted": False
+ }
+
+ x = {
+ "voting_id": 4,
+ "name": "EGC",
+ "desc": "Aprobar EGC no es facil",
+ "question": {
+ "multiple": "¿Vamos a aprobar EGC?",
+ "options": {
+ 1: "Yes",
+ 2: "No",
+ 3: "Pa febrero"}},
+ "start_date":"2021-01-08T15:29:52.040435",
+ "end_date":"2021-01-20T15:29:52.040435",
+ "url":"http://localhost:8000/booth/4",
+ "pub-key": "a1s2d3f4g5h6j7k8l9",
+ "voted": False
+ }
+
+ '''context = super().get_context_data(**kwargs)
+ vid = kwargs.get('voting_id', 0)
+ print(vid)
try:
- r = mods.get('voting', params={'id': vid})
-
+ voting = Voting.objects.get(url=voting_url)
+ r = mods.get('voting', params={'id': voting.id})
# Casting numbers to string to manage in javascript with BigInt
# and avoid problems with js and big number conversion
for k, v in r[0]['pub_key'].items():
r[0]['pub_key'][k] = str(v)
+ print(str(v))
context['voting'] = json.dumps(r[0])
except:
raise Http404
+ context['KEYBITS'] = settings.KEYBITS'''
+
+ y['start_date'] = self.format_fecha(y['start_date'])
+ y['end_date'] = self.format_fecha(y['end_date'])
+ y['KEYBITS'] = settings.KEYBITS
+ y['voting'] = json.dumps(y)
+
+ return y
+
+ # formateo fecha "2021-01-12 00:00",
+
+ def format_fecha(self, fecha):
+ result = None
+
+ if fecha != None:
+ fecha = fecha.replace("T", " ").replace("Z", "")
+ date_time = datetime.datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S.%f')
+ result = date_time.strftime('%d/%m/%Y a las %H:%M:%S')
+
+ return result
+
+
+def loginPage(request):
+ if request.user.is_authenticated:
+ return redirect('welcome')
+ else:
+ if request.method == 'POST':
+ username = request.POST.get('username')
+ password = request.POST.get('password')
+
+ user = authenticate(request, username=username, password=password)
+
+ if user is not None:
+ login(request, user)
+ return redirect('welcome')
+ else:
+ messages.info(request, 'Usuario o contraseña incorrectos')
+
+ context = {}
+ return render(request, 'booth/login.html', context)
+
+
+@login_required(login_url='login')
+def yesOrNo(request):
+ formulario = YesOrNoForm()
+ choice = None
+ if request.method == 'POST':
+ formulario = YesOrNoForm(request.POST)
+ if formulario.is_valid():
+ choice = YesOrNoQuestion.objects.filter(choice=formulario.cleaned_data['choice'])
+ print(choice)
+ print(formulario)
+ return render(request, 'booth.html', {'formulario':formulario, 'choice':choice})
+
+'''@login_required(login_url='login')
+def multiple(request):
+ formulario = MultipleForm()
+ option = None
+ if request.method == 'POST':
+ formulario = Multiple(request.POST)
+ if formulario.is_valid():
+ option = MultipleQuestion.objects.filter(option=formulario.cleaned_data['option'])
+ print(option)
+ print(formulario)
+ return render(request, 'booth.html', {'formularioMultiple':formulario, 'option':option})'''
+
+
+def welcome(request):
+ context={}
+ listaUltimasVotaciones=[]
+ listaUltimasVotaciones=ultimasVotaciones()
+ listaCensada=listaCensadaIds(request.user.id)
+ votacionesUsuarioCensado=votacionesPorUsuario(listaCensada, request.user.id)
+ context = {'allVotaciones':listaUltimasVotaciones, 'votacionesCensado':votacionesUsuarioCensado, 'listaVacia':False}
+ if len(votacionesUsuarioCensado)==0:
+ context['listaVacia']=True
+ return render(request, "booth/welcome.html", context)
+
+
+@login_required(login_url='login')
+def peticionCensoAdmin(request):
+ context={}
+ if not request.user.is_superuser:
+ return HttpResponseForbidden()
+ else:
+ listaUltimasPeticiones=[]
+ listaUltimasPeticiones=ultimasPeticiones()
+ context = {'allPeticiones':listaUltimasPeticiones, 'listaVacia':False}
+ if len(listaUltimasPeticiones)==0:
+ context['listaVacia']=True
+ return render(request, "booth/peticionCensoAdmin.html", context)
+
+@login_required(login_url='login')
+def peticionCensoUsuario(request):
+ form = PeticionForm()
+ if request.method == 'POST':
+ form = PeticionForm(request.POST)
+ if form.is_valid():
+ obj = form.save(commit=False)
+ obj.user_id = request.user.id
+ obj.save()
+
+ return redirect('welcome')
+
+
+ context = {'form':form}
+ return render(request, 'booth/peticionCensoUsuario.html', context)
+
+
+@login_required(login_url='login')
+def hasVotado(request):
+ return render(request, "booth/hasVotado.html")
+
+
+def logoutUser(request):
+ logout(request)
+ return redirect('welcome')
+
+
+def registerPage(request):
+ if request.user.is_authenticated:
+ return redirect('welcome')
+ else:
+ form = CrearUsuario()
+ if request.method == 'POST':
+ form = CrearUsuario(request.POST)
+ if form.is_valid():
+ form.save()
+ user = form.cleaned_data.get('username')
+
+ return redirect('login')
+
+ context = {'form':form}
+ return render(request, 'booth/register.html', context)
+
+
+def votacionesPorUsuario(votacionesId, user_id):
+ listaVotaciones=[]
+ totalVotaciones = Voting.objects.all().filter(id__in=votacionesId, end_date__isnull=True)
+ for v in totalVotaciones:
+ votos = Vote.objects.filter(voting_id=v.id, voter_id=user_id)
+ if votos.count()==0:
+ listaVotaciones.append(v)
+
+ return listaVotaciones
+
+def ultimasVotaciones():
+ listaVotaciones=[]
+ totalVotaciones = Voting.objects.all().filter(end_date__isnull=True).order_by('-start_date')
+ for v in totalVotaciones:
+ listaVotaciones.append(v)
+ return listaVotaciones
+
+def listaCensadaIds(user_id):
+ listaCensadaIds = []
+ totalListaCensada = Census.objects.all().filter(voter_id=user_id)
+ if totalListaCensada.count() != 0:
+ for c in totalListaCensada:
+ listaCensadaIds.append(c.voting_id)
+
+ return listaCensadaIds
+
+def ultimasPeticiones():
+ listaPeticiones=[]
+ totalPeticiones = PeticionCenso.objects.all()
+ for t in totalPeticiones:
+ listaPeticiones.append(t)
+ return listaPeticiones
- context['KEYBITS'] = settings.KEYBITS
+@login_required(login_url='login')
+def deletePeticion(request, pk):
+ if not request.user.is_superuser:
+ return HttpResponseForbidden()
+ else:
+ peticion = PeticionCenso.objects.get(id=pk)
+ if request.method == "POST":
+ peticion.delete()
+ return redirect('peticionCensoAdmin')
- return context
+ context = {'item':peticion}
+ return render(request, 'booth/deletePeticion.html', context)
+
+def about(request):
+ return render(request, "booth/about.html")
diff --git a/decide/decide/settings.py b/decide/decide/settings.py
index 1d22b67324..37e35cb3e2 100644
--- a/decide/decide/settings.py
+++ b/decide/decide/settings.py
@@ -11,11 +11,11 @@
"""
import os
+from django.utils.translation import ugettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
@@ -27,7 +27,6 @@
ALLOWED_HOSTS = []
-
# Application definition
INSTALLED_APPS = [
@@ -75,6 +74,7 @@
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -100,8 +100,16 @@
},
]
-WSGI_APPLICATION = 'decide.wsgi.application'
+LANGUAGES = [
+ ('es', _('Español')),
+ ('en-us', _('English'))
+]
+LOCALE_PATHS = [
+ os.path.join(BASE_DIR, 'locale')
+]
+
+WSGI_APPLICATION = 'decide.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
@@ -117,7 +125,6 @@
}
}
-
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
@@ -136,13 +143,12 @@
},
]
-
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
-TIME_ZONE = 'UTC'
+TIME_ZONE = 'Europe/Madrid'
USE_I18N = True
@@ -150,7 +156,6 @@
USE_TZ = True
-
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
# Static files (CSS, JavaScript, Images)
@@ -178,5 +183,4 @@
for k, v in config.items():
vars()[k] = v
-
INSTALLED_APPS = INSTALLED_APPS + MODULES
diff --git a/decide/local_settings.py b/decide/local_settings.py
new file mode 100644
index 0000000000..494b68fac3
--- /dev/null
+++ b/decide/local_settings.py
@@ -0,0 +1,42 @@
+ALLOWED_HOSTS = ["*"]
+
+# Modules in use, commented modules that you won't use
+MODULES = [
+ 'authentication',
+ 'base',
+ 'booth',
+ 'census',
+ 'mixnet',
+ 'postproc',
+ 'store',
+ 'visualizer',
+ 'voting',
+]
+
+APIS = {
+ 'authentication': 'http://localhost:8000',
+ 'base': 'http://localhost:8000',
+ 'booth': 'http://localhost:8000',
+ 'census': 'http://localhost:8000',
+ 'mixnet': 'http://localhost:8000',
+ 'postproc': 'http://localhost:8000',
+ 'store': 'http://localhost:8000',
+ 'visualizer': 'http://localhost:8000',
+ 'voting': 'http://localhost:8000',
+}
+
+BASEURL = 'http://localhost:8000'
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql',
+ 'NAME': 'decide',
+ 'USER': 'decide',
+ 'PASSWORD': 'decide',
+ 'HOST': 'localhost',
+ 'PORT': '5432',
+ }
+}
+
+# number of bits for the key, all auths should use the same number of bits
+KEYBITS = 256
diff --git a/decide/postproc/views.py b/decide/postproc/views.py
index f4c5de1e5f..4f4989a7e3 100644
--- a/decide/postproc/views.py
+++ b/decide/postproc/views.py
@@ -29,10 +29,17 @@ def post(self, request):
]
"""
- t = request.data.get('type', 'IDENTITY')
- opts = request.data.get('options', [])
-
- if t == 'IDENTITY':
- return self.identity(opts)
+ typeOfData = request.data.get('type', 'IDENTITY')
+ options = request.data.get('options', [])
+
+ if typeOfData == 'IDENTITY':
+ return self.identity(options)
+
+ elif typeOfData == 'PARIDAD':
+ check = self.check_json(options)
+ if check:
+ return Response(self.paridad(options))
+ else:
+ return Response({'message' : 'No se cumplen los ratios de paridad 60%-40%'})
return Response({})
diff --git a/decide/test-scripts/js/index.html.py b/decide/test-scripts/js/index.html.py
new file mode 100644
index 0000000000..238b00be9c
--- /dev/null
+++ b/decide/test-scripts/js/index.html.py
@@ -0,0 +1,43 @@
+XXXX
+ XXXXXXX XXXXXXXXXX XXXXXXXX XXXXXXXX XX XXX XXXX XX XXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXX
+
+XXXXXX
+
+XXXXXX
+XXXXXXX
+
+XXXXXX
+ XXXX XXXXX XXXXXXXXXXXXXXXX XXXXX
+ XXX XXXXX XXXXXXXXXXXXXXX XXXXX
+ XXXXXXXX XXXXX XXXXXXXXXXXXXXXXXXXX
+
+ XXXX XXXXXX XX XXXXXXXX XXX XXXXXX XXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ XXXX XXX XXXXXXX XXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ XXXX XXXXXXX XXXXXXX XXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ XXXXXXXX
+ XXX XX X XXX XXXXXX XX XXXXX XX XXXXXXX
+ XXX XXX X XXXXX
+
+ XXX XXXXX X X
+ XX XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XX XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XX XXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XX
+ XXX XXXXXX X XXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXX XXXXXX X XXXXXXXXXXXXXXXXXXXXXX XXXXXXXX
+
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X XXXX X XXX X XXXX X XXX X XXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X XXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX X XXXXXXXXXXXXXXXXXXXXXXX X XXX X XXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXXXX
+XXXXXXX
diff --git a/decide/visualizer/templates/visualizer/visualizer.html.py b/decide/visualizer/templates/visualizer/visualizer.html.py
new file mode 100644
index 0000000000..922eeefe40
--- /dev/null
+++ b/decide/visualizer/templates/visualizer/visualizer.html.py
@@ -0,0 +1,66 @@
+BBBBBBB BBBBBBBBBBB
+BBBB BBBB BBBBBB
+
+BBBBB BBBBBBBBB
+ XXXXX XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX
+ XXXXX XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX
+ XXXXX XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXX XXXXXXBBBBBB BBBBBBBBBBBBBBBBBX XX
+BBBBBBBB
+
+BBBBB BBBBBBB
+ XXXX XXXXXXXXXXXXXXXXXXXX
+ XXXX XXXXXX XXX
+ XXXXXXXXX XXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXXXXXX
+
+ XXXX XXXXXXXXXXXXX XXXXXXXXXXX
+ XXXXXX XXXXXXXXX XX X XX XXXXXXXXXXX XXXXXXX
+
+ XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX XXXXXXXXXXXXXX
+ XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XX XXXXXXXXXX
+ XXXX XXXXXXX
+ XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ XXXXXX XXXXXXXXXXXX XXXXXXXXXXXXXX XXXXXXXXXXXXXXX
+ XXXXXXX
+ XXXX
+ XXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXX
+ XXXXX
+ XXXXXXXX
+ XXXXXXX
+ XXX XXXXXXXXXX XX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXXXXXXXXXXXXXXXXXXXX
+ XXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXX
+ XXXXXXXX
+ XXXXXXXX
+ XXXXXX
+
+ XXXXXX
+ XXXXXX
+BBBBBBBB
+
+BBBBB BBBBBBBBB
+ XXXX XXXXX XXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ XXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+ XXXXXXXX
+ XXX XXXXXX X FFFFX
+ XXX XXX X XXX XXXXX
+ XXXXXXXXXXX XXXXXX XXXXXX
+ XXX XXXXXXXXXXXXXXXXXX
+ XXXXX X
+ XXXXXXX XXXXXX
+ X
+ XX
+ XXXXXXXXX
+XXXXXXX
+BBBBBBBB
diff --git a/decide/voting/admin.py b/decide/voting/admin.py
index dff206a94f..37ad22d836 100644
--- a/decide/voting/admin.py
+++ b/decide/voting/admin.py
@@ -4,7 +4,8 @@
from .models import QuestionOption
from .models import Question
from .models import Voting
-
+from .models import YesOrNoQuestion
+from .models import PoliticalParty
from .filters import StartedFilter
@@ -35,6 +36,9 @@ class QuestionAdmin(admin.ModelAdmin):
inlines = [QuestionOptionInline]
+class YesOrNoQuestionAdmin(admin.ModelAdmin):
+ pass
+
class VotingAdmin(admin.ModelAdmin):
list_display = ('name', 'start_date', 'end_date')
readonly_fields = ('start_date', 'end_date', 'pub_key',
@@ -46,5 +50,12 @@ class VotingAdmin(admin.ModelAdmin):
actions = [ start, stop, tally ]
+class PoliticalPartyAdmin(admin.ModelAdmin):
+ readonly_fields = ('president',)
+
admin.site.register(Voting, VotingAdmin)
admin.site.register(Question, QuestionAdmin)
+#Añadido register para preguntas YesOrNoQuestion
+admin.site.register(YesOrNoQuestion, YesOrNoQuestionAdmin)
+admin.site.register(PoliticalParty,PoliticalPartyAdmin)
+
diff --git a/decide/voting/migrations/0004_auto_20210115_1139.py b/decide/voting/migrations/0004_auto_20210115_1139.py
new file mode 100644
index 0000000000..13ade7a1e0
--- /dev/null
+++ b/decide/voting/migrations/0004_auto_20210115_1139.py
@@ -0,0 +1,47 @@
+# Generated by Django 2.0 on 2021-01-15 10:39
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0003_auto_20180605_0842'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PoliticalParty',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200)),
+ ('acronym', models.CharField(max_length=10)),
+ ('description', models.TextField(blank=True, null=True)),
+ ('leader', models.CharField(max_length=200)),
+ ('president', models.CharField(blank=True, max_length=151, null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='YesOrNoQuestion',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('desc', models.TextField()),
+ ('choice', models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], max_length=1)),
+ ],
+ ),
+ migrations.AddField(
+ model_name='voting',
+ name='url',
+ field=models.CharField(help_text='http://localhost:8000/booth/', max_length=40, null=True),
+ ),
+ migrations.AlterUniqueTogether(
+ name='politicalparty',
+ unique_together={('name', 'acronym', 'leader')},
+ ),
+ migrations.AddField(
+ model_name='voting',
+ name='political_party',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.PoliticalParty'),
+ ),
+ ]
diff --git a/decide/voting/migrations/0004_voting_url.py b/decide/voting/migrations/0004_voting_url.py
new file mode 100644
index 0000000000..ca8888b089
--- /dev/null
+++ b/decide/voting/migrations/0004_voting_url.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.0 on 2021-01-01 18:10
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0003_auto_20180605_0842'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='voting',
+ name='url',
+ field=models.CharField(default='_voting_decide_example', max_length=40),
+ preserve_default=False,
+ ),
+ ]
diff --git a/decide/voting/migrations/0004_yesorno_yesornooption.py b/decide/voting/migrations/0004_yesorno_yesornooption.py
new file mode 100644
index 0000000000..164881b653
--- /dev/null
+++ b/decide/voting/migrations/0004_yesorno_yesornooption.py
@@ -0,0 +1,29 @@
+# Generated by Django 2.0 on 2021-01-06 12:25
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0003_auto_20180605_0842'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='YesOrNo',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('desc', models.TextField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='YesOrNoOption',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('option', models.TextField()),
+ ('yesorno', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='voting.YesOrNo')),
+ ],
+ ),
+ ]
diff --git a/decide/voting/migrations/0005_auto_20210101_1813.py b/decide/voting/migrations/0005_auto_20210101_1813.py
new file mode 100644
index 0000000000..984b21cf75
--- /dev/null
+++ b/decide/voting/migrations/0005_auto_20210101_1813.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.0 on 2021-01-01 18:13
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0004_voting_url'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='voting',
+ name='url',
+ field=models.CharField(help_text='http://localhost:8000/booth/', max_length=40),
+ ),
+ ]
diff --git a/decide/voting/migrations/0005_auto_20210106_1519.py b/decide/voting/migrations/0005_auto_20210106_1519.py
new file mode 100644
index 0000000000..e54741adc9
--- /dev/null
+++ b/decide/voting/migrations/0005_auto_20210106_1519.py
@@ -0,0 +1,26 @@
+# Generated by Django 2.0 on 2021-01-06 15:19
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0004_yesorno_yesornooption'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='yesornooption',
+ name='yesorno',
+ ),
+ migrations.AddField(
+ model_name='yesorno',
+ name='choice',
+ field=models.CharField(choices=[('Y', 'Yes'), ('N', 'No')], default='N', max_length=1),
+ preserve_default=False,
+ ),
+ migrations.DeleteModel(
+ name='YesOrNoOption',
+ ),
+ ]
diff --git a/decide/voting/migrations/0006_auto_20210106_1523.py b/decide/voting/migrations/0006_auto_20210106_1523.py
new file mode 100644
index 0000000000..ac5649d592
--- /dev/null
+++ b/decide/voting/migrations/0006_auto_20210106_1523.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.0 on 2021-01-06 15:23
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0005_auto_20210106_1519'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='yesorno',
+ name='choice',
+ field=models.CharField(blank=True, choices=[('Y', 'Yes'), ('N', 'No')], max_length=1),
+ ),
+ ]
diff --git a/decide/voting/migrations/0007_auto_20210106_1529.py b/decide/voting/migrations/0007_auto_20210106_1529.py
new file mode 100644
index 0000000000..94656b799e
--- /dev/null
+++ b/decide/voting/migrations/0007_auto_20210106_1529.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.0 on 2021-01-06 15:29
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0006_auto_20210106_1523'),
+ ]
+
+ operations = [
+ migrations.RenameModel(
+ old_name='YesOrNo',
+ new_name='YesOrNoQuestion',
+ ),
+ ]
diff --git a/decide/voting/migrations/0008_merge_20210106_2217.py b/decide/voting/migrations/0008_merge_20210106_2217.py
new file mode 100644
index 0000000000..9615158112
--- /dev/null
+++ b/decide/voting/migrations/0008_merge_20210106_2217.py
@@ -0,0 +1,14 @@
+# Generated by Django 2.0 on 2021-01-06 22:17
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0007_auto_20210106_1529'),
+ ('voting', '0005_auto_20210101_1813'),
+ ]
+
+ operations = [
+ ]
diff --git a/decide/voting/migrations/0009_auto_20210106_2332.py b/decide/voting/migrations/0009_auto_20210106_2332.py
new file mode 100644
index 0000000000..4af8d8e416
--- /dev/null
+++ b/decide/voting/migrations/0009_auto_20210106_2332.py
@@ -0,0 +1,34 @@
+# Generated by Django 2.0 on 2021-01-06 23:32
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('voting', '0008_merge_20210106_2217'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PoliticalParty',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200)),
+ ('acronym', models.CharField(max_length=10)),
+ ('description', models.TextField(blank=True, null=True)),
+ ('leader', models.CharField(max_length=200)),
+ ('president', models.CharField(blank=True, max_length=151, null=True)),
+ ],
+ ),
+ migrations.AlterUniqueTogether(
+ name='politicalparty',
+ unique_together={('name', 'acronym', 'leader')},
+ ),
+ migrations.AddField(
+ model_name='voting',
+ name='political_party',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='voting', to='voting.PoliticalParty'),
+ ),
+ ]
diff --git a/decide/voting/models.py b/decide/voting/models.py
index a10ab2bcb6..7ff255758a 100644
--- a/decide/voting/models.py
+++ b/decide/voting/models.py
@@ -1,10 +1,13 @@
-from django.db import models
-from django.contrib.postgres.fields import JSONField
-from django.db.models.signals import post_save
-from django.dispatch import receiver
+import urllib
from base import mods
from base.models import Auth, Key
+from django.contrib.postgres.fields import JSONField
+from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
+from django.db import models
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+from django.utils.translation import gettext_lazy as _
class Question(models.Model):
@@ -13,6 +16,35 @@ class Question(models.Model):
def __str__(self):
return self.desc
+class PoliticalParty(models.Model):
+
+ name = models.CharField(max_length=200)
+ acronym = models.CharField(max_length=10)
+ description = models.TextField(blank=True, null=True)
+ leader = models.CharField(max_length=200)
+ president = models.CharField(max_length=151, blank=True, null=True)
+
+ def __str__(self):
+ return '{} ({}) - {}'.format(self.acronym, self.name, self.leader)
+
+
+ class Meta:
+ unique_together = (('name', 'acronym', 'leader'),)
+
+
+
+#Añadida clase YesOrNo
+
+class YesOrNoQuestion(models.Model):
+ desc = models.TextField()
+ CHOICES = (
+ ('Y', 'Yes'),
+ ('N', 'No'),
+ )
+ choice = models.CharField(max_length=1, choices=CHOICES, blank=True)
+
+ def __str__(self):
+ return self.desc
class QuestionOption(models.Model):
question = models.ForeignKey(Question, related_name='options', on_delete=models.CASCADE)
@@ -27,11 +59,12 @@ def save(self):
def __str__(self):
return '{} ({})'.format(self.option, self.number)
-
class Voting(models.Model):
name = models.CharField(max_length=200)
desc = models.TextField(blank=True, null=True)
question = models.ForeignKey(Question, related_name='voting', on_delete=models.CASCADE)
+ political_party = models.ForeignKey(PoliticalParty, related_name='voting', on_delete=models.CASCADE, null=True)
+ # ,blank=True
start_date = models.DateTimeField(blank=True, null=True)
end_date = models.DateTimeField(blank=True, null=True)
@@ -41,6 +74,25 @@ class Voting(models.Model):
tally = JSONField(blank=True, null=True)
postproc = JSONField(blank=True, null=True)
+ url = models.CharField(max_length=40, help_text=u"http://localhost:8000/booth/")
+
+
+ def clean_fields(self, exclude=None):
+ super(Voting, self).clean_fields(exclude)
+
+ url = urllib.parse.quote_plus(self.url.encode('utf-8'))
+
+ if Voting.objects.filter(url=url).exists():
+ raise ValidationError({'url': "The url already exists."})
+
+ def save(self, *args, **kwargs):
+ try:
+ Voting.objects.get(name=self.name)
+ except:
+ encode_url = urllib.parse.quote_plus(self.url.encode('utf-8'))
+ self.url = encode_url
+
+ super(Voting, self).save(*args, **kwargs)
def create_pubkey(self):
if self.pub_key or not self.auths.count():
diff --git a/decide/voting/serializers.py b/decide/voting/serializers.py
index 0713519528..e53909e72a 100644
--- a/decide/voting/serializers.py
+++ b/decide/voting/serializers.py
@@ -1,6 +1,6 @@
from rest_framework import serializers
-from .models import Question, QuestionOption, Voting
+from .models import Question, QuestionOption, Voting, YesOrNoQuestion, PoliticalParty
from base.serializers import KeySerializer, AuthSerializer
@@ -34,3 +34,21 @@ class SimpleVotingSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Voting
fields = ('name', 'desc', 'question', 'start_date', 'end_date')
+
+class YesOrNoQuestionSerializer(serializers.HyperlinkedModelSerializer):
+ desc = serializers.CharField()
+ CHOICES = (
+ ('Y', 'Yes'),
+ ('N', 'No'),
+ )
+ choice = serializers.ChoiceField(choices=CHOICES)
+ class Meta:
+ model = YesOrNoQuestion
+ fields = ('desc', 'choice')
+
+
+
+class PoliticalPartySerializer(serializers.HyperlinkedModelSerializer):
+ class Meta:
+ model = PoliticalParty
+ fields = ('name', 'acronym', 'description', 'leader', 'president')
\ No newline at end of file
diff --git a/decide/voting/views.py b/decide/voting/views.py
index 2f2227edc9..7397e7e73d 100644
--- a/decide/voting/views.py
+++ b/decide/voting/views.py
@@ -5,17 +5,22 @@
from rest_framework import generics, status
from rest_framework.response import Response
-from .models import Question, QuestionOption, Voting
+from .models import Question, QuestionOption, Voting, YesOrNoQuestion
from .serializers import SimpleVotingSerializer, VotingSerializer
from base.perms import UserIsStaff
from base.models import Auth
+from rest_framework.renderers import TemplateHTMLRenderer
class VotingView(generics.ListCreateAPIView):
+
+ '''renderer_classes = [TemplateHTMLRenderer]
+ template_name = './booth/booth.html'''
+
queryset = Voting.objects.all()
serializer_class = VotingSerializer
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
- filter_fields = ('id', )
+ filter_fields = ('id',)
def get(self, request, *args, **kwargs):
version = request.version
@@ -35,9 +40,7 @@ def post(self, request, *args, **kwargs):
question = Question(desc=request.data.get('question'))
question.save()
- for idx, q_opt in enumerate(request.data.get('question_opt')):
- opt = QuestionOption(question=question, option=q_opt, number=idx)
- opt.save()
+
voting = Voting(name=request.data.get('name'), desc=request.data.get('desc'),
question=question)
voting.save()
@@ -99,3 +102,4 @@ def put(self, request, voting_id, *args, **kwars):
msg = 'Action not found, try with start, stop or tally'
st = status.HTTP_400_BAD_REQUEST
return Response(msg, status=st)
+
diff --git a/egc/bin/Activate.ps1 b/egc/bin/Activate.ps1
new file mode 100644
index 0000000000..2fb3852c3c
--- /dev/null
+++ b/egc/bin/Activate.ps1
@@ -0,0 +1,241 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies:
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+ [Parameter(Mandatory = $false)]
+ [String]
+ $VenvDir,
+ [Parameter(Mandatory = $false)]
+ [String]
+ $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+ # Revert to original values
+
+ # The prior prompt:
+ if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+ Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+ Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+ }
+
+ # The prior PYTHONHOME:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+ Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+ }
+
+ # The prior PATH:
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+ Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+ Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+ }
+
+ # Just remove the VIRTUAL_ENV altogether:
+ if (Test-Path -Path Env:VIRTUAL_ENV) {
+ Remove-Item -Path env:VIRTUAL_ENV
+ }
+
+ # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+ if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+ Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+ }
+
+ # Leave deactivate function in the global namespace if requested:
+ if (-not $NonDestructive) {
+ Remove-Item -Path function:deactivate
+ }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+ [String]
+ $ConfigDir
+) {
+ Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+ # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+ $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+ # An empty map will be returned if no config file is found.
+ $pyvenvConfig = @{ }
+
+ if ($pyvenvConfigPath) {
+
+ Write-Verbose "File exists, parse `key = value` lines"
+ $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+ $pyvenvConfigContent | ForEach-Object {
+ $keyval = $PSItem -split "\s*=\s*", 2
+ if ($keyval[0] -and $keyval[1]) {
+ $val = $keyval[1]
+
+ # Remove extraneous quotations around a string value.
+ if ("'""".Contains($val.Substring(0, 1))) {
+ $val = $val.Substring(1, $val.Length - 2)
+ }
+
+ $pyvenvConfig[$keyval[0]] = $val
+ Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+ }
+ }
+ }
+ return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+ Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+ Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+ $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+ Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+ Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+ Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+ if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+ Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+ $Prompt = $pyvenvCfg['prompt'];
+ }
+ else {
+ Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)"
+ Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+ $Prompt = Split-Path -Path $venvDir -Leaf
+ }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+ Write-Verbose "Setting prompt to '$Prompt'"
+
+ # Set the prompt to include the env name
+ # Make sure _OLD_VIRTUAL_PROMPT is global
+ function global:_OLD_VIRTUAL_PROMPT { "" }
+ Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+ New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+ function global:prompt {
+ Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+ _OLD_VIRTUAL_PROMPT
+ }
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+ Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+ Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
diff --git a/egc/bin/activate b/egc/bin/activate
new file mode 100644
index 0000000000..7c22ae991c
--- /dev/null
+++ b/egc/bin/activate
@@ -0,0 +1,76 @@
+# This file must be used with "source bin/activate" *from bash*
+# you cannot run it directly
+
+deactivate () {
+ # reset old environment variables
+ if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+ PATH="${_OLD_VIRTUAL_PATH:-}"
+ export PATH
+ unset _OLD_VIRTUAL_PATH
+ fi
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+ PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+ export PYTHONHOME
+ unset _OLD_VIRTUAL_PYTHONHOME
+ fi
+
+ # This should detect bash and zsh, which have a hash command that must
+ # be called to get it to forget past commands. Without forgetting
+ # past commands the $PATH changes we made may not be respected
+ if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
+ hash -r
+ fi
+
+ if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+ PS1="${_OLD_VIRTUAL_PS1:-}"
+ export PS1
+ unset _OLD_VIRTUAL_PS1
+ fi
+
+ unset VIRTUAL_ENV
+ if [ ! "${1:-}" = "nondestructive" ] ; then
+ # Self destruct!
+ unset -f deactivate
+ fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+VIRTUAL_ENV="/home/josrojrom1/aboutCabinaView/decide-1/egc"
+export VIRTUAL_ENV
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/bin:$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+ _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+ unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+ _OLD_VIRTUAL_PS1="${PS1:-}"
+ if [ "x(egc) " != x ] ; then
+ PS1="(egc) ${PS1:-}"
+ else
+ if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
+ # special case for Aspen magic directories
+ # see http://www.zetadev.com/software/aspen/
+ PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
+ else
+ PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
+ fi
+ fi
+ export PS1
+fi
+
+# This should detect bash and zsh, which have a hash command that must
+# be called to get it to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
+ hash -r
+fi
diff --git a/egc/bin/activate.csh b/egc/bin/activate.csh
new file mode 100644
index 0000000000..672f2ea740
--- /dev/null
+++ b/egc/bin/activate.csh
@@ -0,0 +1,37 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+# Created by Davide Di Blasi .
+# Ported to Python 3.3 venv by Andrew Svetlov
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV "/home/josrojrom1/aboutCabinaView/decide-1/egc"
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/bin:$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+ if ("egc" != "") then
+ set env_name = "egc"
+ else
+ if (`basename "VIRTUAL_ENV"` == "__") then
+ # special case for Aspen magic directories
+ # see http://www.zetadev.com/software/aspen/
+ set env_name = `basename \`dirname "$VIRTUAL_ENV"\``
+ else
+ set env_name = `basename "$VIRTUAL_ENV"`
+ endif
+ endif
+ set prompt = "[$env_name] $prompt"
+ unset env_name
+endif
+
+alias pydoc python -m pydoc
+
+rehash
diff --git a/egc/bin/activate.fish b/egc/bin/activate.fish
new file mode 100644
index 0000000000..076d26e78f
--- /dev/null
+++ b/egc/bin/activate.fish
@@ -0,0 +1,75 @@
+# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org)
+# you cannot run it directly
+
+function deactivate -d "Exit virtualenv and return to normal shell environment"
+ # reset old environment variables
+ if test -n "$_OLD_VIRTUAL_PATH"
+ set -gx PATH $_OLD_VIRTUAL_PATH
+ set -e _OLD_VIRTUAL_PATH
+ end
+ if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+ set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+ set -e _OLD_VIRTUAL_PYTHONHOME
+ end
+
+ if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+ functions -e fish_prompt
+ set -e _OLD_FISH_PROMPT_OVERRIDE
+ functions -c _old_fish_prompt fish_prompt
+ functions -e _old_fish_prompt
+ end
+
+ set -e VIRTUAL_ENV
+ if test "$argv[1]" != "nondestructive"
+ # Self destruct!
+ functions -e deactivate
+ end
+end
+
+# unset irrelevant variables
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV "/home/josrojrom1/aboutCabinaView/decide-1/egc"
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/bin" $PATH
+
+# unset PYTHONHOME if set
+if set -q PYTHONHOME
+ set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+ set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+ # fish uses a function instead of an env var to generate the prompt.
+
+ # save the current fish_prompt function as the function _old_fish_prompt
+ functions -c fish_prompt _old_fish_prompt
+
+ # with the original prompt function renamed, we can override with our own.
+ function fish_prompt
+ # Save the return status of the last command
+ set -l old_status $status
+
+ # Prompt override?
+ if test -n "(egc) "
+ printf "%s%s" "(egc) " (set_color normal)
+ else
+ # ...Otherwise, prepend env
+ set -l _checkbase (basename "$VIRTUAL_ENV")
+ if test $_checkbase = "__"
+ # special case for Aspen magic directories
+ # see http://www.zetadev.com/software/aspen/
+ printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal)
+ else
+ printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal)
+ end
+ end
+
+ # Restore the return status of the previous command.
+ echo "exit $old_status" | .
+ _old_fish_prompt
+ end
+
+ set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+end
diff --git a/egc/bin/chardetect b/egc/bin/chardetect
new file mode 100755
index 0000000000..757186f2f0
--- /dev/null
+++ b/egc/bin/chardetect
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from chardet.cli.chardetect import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/coverage b/egc/bin/coverage
new file mode 100755
index 0000000000..e74079214c
--- /dev/null
+++ b/egc/bin/coverage
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from coverage.cmdline import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/coverage-3.8 b/egc/bin/coverage-3.8
new file mode 100755
index 0000000000..e74079214c
--- /dev/null
+++ b/egc/bin/coverage-3.8
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from coverage.cmdline import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/coverage3 b/egc/bin/coverage3
new file mode 100755
index 0000000000..e74079214c
--- /dev/null
+++ b/egc/bin/coverage3
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from coverage.cmdline import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/django-admin b/egc/bin/django-admin
new file mode 100755
index 0000000000..f4c181907e
--- /dev/null
+++ b/egc/bin/django-admin
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from django.core.management import execute_from_command_line
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(execute_from_command_line())
diff --git a/egc/bin/django-admin.py b/egc/bin/django-admin.py
new file mode 100755
index 0000000000..7f5e3dab80
--- /dev/null
+++ b/egc/bin/django-admin.py
@@ -0,0 +1,5 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+from django.core import management
+
+if __name__ == "__main__":
+ management.execute_from_command_line()
diff --git a/egc/bin/easy_install b/egc/bin/easy_install
new file mode 100755
index 0000000000..ebfc414084
--- /dev/null
+++ b/egc/bin/easy_install
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from setuptools.command.easy_install import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/easy_install-3.8 b/egc/bin/easy_install-3.8
new file mode 100755
index 0000000000..ebfc414084
--- /dev/null
+++ b/egc/bin/easy_install-3.8
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from setuptools.command.easy_install import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/nosetests b/egc/bin/nosetests
new file mode 100755
index 0000000000..337d58bc56
--- /dev/null
+++ b/egc/bin/nosetests
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from nose import run_exit
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_exit())
diff --git a/egc/bin/nosetests-3.4 b/egc/bin/nosetests-3.4
new file mode 100755
index 0000000000..337d58bc56
--- /dev/null
+++ b/egc/bin/nosetests-3.4
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from nose import run_exit
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(run_exit())
diff --git a/egc/bin/pip b/egc/bin/pip
new file mode 100755
index 0000000000..55c5847b09
--- /dev/null
+++ b/egc/bin/pip
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/pip3 b/egc/bin/pip3
new file mode 100755
index 0000000000..55c5847b09
--- /dev/null
+++ b/egc/bin/pip3
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/pip3.8 b/egc/bin/pip3.8
new file mode 100755
index 0000000000..55c5847b09
--- /dev/null
+++ b/egc/bin/pip3.8
@@ -0,0 +1,8 @@
+#!/home/josrojrom1/aboutCabinaView/decide-1/egc/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pip._internal.cli.main import main
+if __name__ == '__main__':
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+ sys.exit(main())
diff --git a/egc/bin/python b/egc/bin/python
new file mode 120000
index 0000000000..b8a0adbbb9
--- /dev/null
+++ b/egc/bin/python
@@ -0,0 +1 @@
+python3
\ No newline at end of file
diff --git a/egc/bin/python3 b/egc/bin/python3
new file mode 120000
index 0000000000..ae65fdaa12
--- /dev/null
+++ b/egc/bin/python3
@@ -0,0 +1 @@
+/usr/bin/python3
\ No newline at end of file
diff --git a/egc/lib64 b/egc/lib64
new file mode 120000
index 0000000000..7951405f85
--- /dev/null
+++ b/egc/lib64
@@ -0,0 +1 @@
+lib
\ No newline at end of file
diff --git a/egc/man/man1/nosetests.1 b/egc/man/man1/nosetests.1
new file mode 100644
index 0000000000..577284569c
--- /dev/null
+++ b/egc/man/man1/nosetests.1
@@ -0,0 +1,581 @@
+.\" Man page generated from reStructuredText.
+.
+.TH "NOSETESTS" "1" "April 04, 2015" "1.3" "nose"
+.SH NAME
+nosetests \- Nicer testing for Python
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.SH NICER TESTING FOR PYTHON
+.SS SYNOPSIS
+.INDENT 0.0
+.INDENT 3.5
+nosetests [options] [names]
+.UNINDENT
+.UNINDENT
+.SS DESCRIPTION
+.sp
+nose collects tests automatically from python source files,
+directories and packages found in its working directory (which
+defaults to the current working directory). Any python source file,
+directory or package that matches the testMatch regular expression
+(by default: \fI(?:^|[b_.\-])[Tt]est)\fP will be collected as a test (or
+source for collection of tests). In addition, all other packages
+found in the working directory will be examined for python source files
+or directories that match testMatch. Package discovery descends all
+the way down the tree, so package.tests and package.sub.tests and
+package.sub.sub2.tests will all be collected.
+.sp
+Within a test directory or package, any python source file matching
+testMatch will be examined for test cases. Within a test module,
+functions and classes whose names match testMatch and TestCase
+subclasses with any name will be loaded and executed as tests. Tests
+may use the assert keyword or raise AssertionErrors to indicate test
+failure. TestCase subclasses may do the same or use the various
+TestCase methods available.
+.sp
+\fBIt is important to note that the default behavior of nose is to
+not include tests from files which are executable.\fP To include
+tests from such files, remove their executable bit or use
+the \-\-exe flag (see \(aqOptions\(aq section below).
+.SS Selecting Tests
+.sp
+To specify which tests to run, pass test names on the command line:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+nosetests only_test_this.py
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Test names specified may be file or module names, and may optionally
+indicate the test case to run by separating the module or file name
+from the test case name with a colon. Filenames may be relative or
+absolute. Examples:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+nosetests test.module
+nosetests another.test:TestCase.test_method
+nosetests a.test:TestCase
+nosetests /path/to/test/file.py:test_function
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+You may also change the working directory where nose looks for tests
+by using the \-w switch:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+nosetests \-w /path/to/tests
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Note, however, that support for multiple \-w arguments is now deprecated
+and will be removed in a future release. As of nose 0.10, you can get
+the same behavior by specifying the target directories \fIwithout\fP
+the \-w switch:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+nosetests /path/to/tests /another/path/to/tests
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Further customization of test selection and loading is possible
+through the use of plugins.
+.sp
+Test result output is identical to that of unittest, except for
+the additional features (error classes, and plugin\-supplied
+features such as output capture and assert introspection) detailed
+in the options below.
+.SS Configuration
+.sp
+In addition to passing command\-line options, you may also put
+configuration options in your project\(aqs \fIsetup.cfg\fP file, or a .noserc
+or nose.cfg file in your home directory. In any of these standard
+ini\-style config files, you put your nosetests configuration in a
+\fB[nosetests]\fP section. Options are the same as on the command line,
+with the \-\- prefix removed. For options that are simple switches, you
+must supply a value:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+[nosetests]
+verbosity=3
+with\-doctest=1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+All configuration files that are found will be loaded and their
+options combined. You can override the standard config file loading
+with the \fB\-c\fP option.
+.SS Using Plugins
+.sp
+There are numerous nose plugins available via easy_install and
+elsewhere. To use a plugin, just install it. The plugin will add
+command line options to nosetests. To verify that the plugin is installed,
+run:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+nosetests \-\-plugins
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+You can add \-v or \-vv to that command to show more information
+about each plugin.
+.sp
+If you are running nose.main() or nose.run() from a script, you
+can specify a list of plugins to use by passing a list of plugins
+with the plugins keyword argument.
+.SS 0.9 plugins
+.sp
+nose 1.0 can use SOME plugins that were written for nose 0.9. The
+default plugin manager inserts a compatibility wrapper around 0.9
+plugins that adapts the changed plugin api calls. However, plugins
+that access nose internals are likely to fail, especially if they
+attempt to access test case or test suite classes. For example,
+plugins that try to determine if a test passed to startTest is an
+individual test or a suite will fail, partly because suites are no
+longer passed to startTest and partly because it\(aqs likely that the
+plugin is trying to find out if the test is an instance of a class
+that no longer exists.
+.SS 0.10 and 0.11 plugins
+.sp
+All plugins written for nose 0.10 and 0.11 should work with nose 1.0.
+.SS Options
+.INDENT 0.0
+.TP
+.B \-V, \-\-version
+Output nose version and exit
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-p, \-\-plugins
+Output list of available plugins and exit. Combine with higher verbosity for greater detail
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-v=DEFAULT, \-\-verbose=DEFAULT
+Be more verbose. [NOSE_VERBOSE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-verbosity=VERBOSITY
+Set verbosity; \-\-verbosity=2 is the same as \-v
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-q=DEFAULT, \-\-quiet=DEFAULT
+Be less verbose
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-c=FILES, \-\-config=FILES
+Load configuration from config file(s). May be specified multiple times; in that case, all config files will be loaded and combined
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-w=WHERE, \-\-where=WHERE
+Look for tests in this directory. May be specified multiple times. The first directory passed will be used as the working directory, in place of the current working directory, which is the default. Others will be added to the list of tests to execute. [NOSE_WHERE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-py3where=PY3WHERE
+Look for tests in this directory under Python 3.x. Functions the same as \(aqwhere\(aq, but only applies if running under Python 3.x or above. Note that, if present under 3.x, this option completely replaces any directories specified with \(aqwhere\(aq, so the \(aqwhere\(aq option becomes ineffective. [NOSE_PY3WHERE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-m=REGEX, \-\-match=REGEX, \-\-testmatch=REGEX
+Files, directories, function names, and class names that match this regular expression are considered tests. Default: (?:^|[b_./\-])[Tt]est [NOSE_TESTMATCH]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-tests=NAMES
+Run these tests (comma\-separated list). This argument is useful mainly from configuration files; on the command line, just pass the tests to run as additional arguments with no switch.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-l=DEFAULT, \-\-debug=DEFAULT
+Activate debug logging for one or more systems. Available debug loggers: nose, nose.importer, nose.inspector, nose.plugins, nose.result and nose.selector. Separate multiple names with a comma.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-debug\-log=FILE
+Log debug messages to this file (default: sys.stderr)
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-logging\-config=FILE, \-\-log\-config=FILE
+Load logging config from this file \-\- bypasses all other logging config settings.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-I=REGEX, \-\-ignore\-files=REGEX
+Completely ignore any file that matches this regular expression. Takes precedence over any other settings or plugins. Specifying this option will replace the default setting. Specify this option multiple times to add more regular expressions [NOSE_IGNORE_FILES]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-e=REGEX, \-\-exclude=REGEX
+Don\(aqt run tests that match regular expression [NOSE_EXCLUDE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-i=REGEX, \-\-include=REGEX
+This regular expression will be applied to files, directories, function names, and class names for a chance to include additional tests that do not match TESTMATCH. Specify this option multiple times to add more regular expressions [NOSE_INCLUDE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-x, \-\-stop
+Stop running tests after the first error or failure
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-P, \-\-no\-path\-adjustment
+Don\(aqt make any changes to sys.path when loading tests [NOSE_NOPATH]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-exe
+Look for tests in python modules that are executable. Normal behavior is to exclude executable modules, since they may not be import\-safe [NOSE_INCLUDE_EXE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-noexe
+DO NOT look for tests in python modules that are executable. (The default on the windows platform is to do so.)
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-traverse\-namespace
+Traverse through all path entries of a namespace package
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-first\-package\-wins, \-\-first\-pkg\-wins, \-\-1st\-pkg\-wins
+nose\(aqs importer will normally evict a package from sys.modules if it sees a package with the same name in a different location. Set this option to disable that behavior.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-no\-byte\-compile
+Prevent nose from byte\-compiling the source into .pyc files while nose is scanning for and running tests.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-a=ATTR, \-\-attr=ATTR
+Run only tests that have attributes specified by ATTR [NOSE_ATTR]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-A=EXPR, \-\-eval\-attr=EXPR
+Run only tests for whose attributes the Python expression EXPR evaluates to True [NOSE_EVAL_ATTR]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-s, \-\-nocapture
+Don\(aqt capture stdout (any stdout output will be printed immediately) [NOSE_NOCAPTURE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-nologcapture
+Disable logging capture plugin. Logging configuration will be left intact. [NOSE_NOLOGCAPTURE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-logging\-format=FORMAT
+Specify custom format to print statements. Uses the same format as used by standard logging handlers. [NOSE_LOGFORMAT]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-logging\-datefmt=FORMAT
+Specify custom date/time format to print statements. Uses the same format as used by standard logging handlers. [NOSE_LOGDATEFMT]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-logging\-filter=FILTER
+Specify which statements to filter in/out. By default, everything is captured. If the output is too verbose,
+use this option to filter out needless output.
+Example: filter=foo will capture statements issued ONLY to
+ foo or foo.what.ever.sub but not foobar or other logger.
+Specify multiple loggers with comma: filter=foo,bar,baz.
+If any logger name is prefixed with a minus, eg filter=\-foo,
+it will be excluded rather than included. Default: exclude logging messages from nose itself (\-nose). [NOSE_LOGFILTER]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-logging\-clear\-handlers
+Clear all other logging handlers
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-logging\-level=DEFAULT
+Set the log level to capture
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-with\-coverage
+Enable plugin Coverage:
+Activate a coverage report using Ned Batchelder\(aqs coverage module.
+ [NOSE_WITH_COVERAGE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-package=PACKAGE
+Restrict coverage output to selected packages [NOSE_COVER_PACKAGE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-erase
+Erase previously collected coverage statistics before run
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-tests
+Include test modules in coverage report [NOSE_COVER_TESTS]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-min\-percentage=DEFAULT
+Minimum percentage of coverage for tests to pass [NOSE_COVER_MIN_PERCENTAGE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-inclusive
+Include all python files under working directory in coverage report. Useful for discovering holes in test coverage if not all files are imported by the test suite. [NOSE_COVER_INCLUSIVE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-html
+Produce HTML coverage information
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-html\-dir=DIR
+Produce HTML coverage information in dir
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-branches
+Include branch coverage in coverage report [NOSE_COVER_BRANCHES]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-xml
+Produce XML coverage information
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-cover\-xml\-file=FILE
+Produce XML coverage information in file
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-pdb
+Drop into debugger on failures or errors
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-pdb\-failures
+Drop into debugger on failures
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-pdb\-errors
+Drop into debugger on errors
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-no\-deprecated
+Disable special handling of DeprecatedTest exceptions.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-with\-doctest
+Enable plugin Doctest:
+Activate doctest plugin to find and run doctests in non\-test modules.
+ [NOSE_WITH_DOCTEST]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-doctest\-tests
+Also look for doctests in test modules. Note that classes, methods and functions should have either doctests or non\-doctest tests, not both. [NOSE_DOCTEST_TESTS]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-doctest\-extension=EXT
+Also look for doctests in files with this extension [NOSE_DOCTEST_EXTENSION]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-doctest\-result\-variable=VAR
+Change the variable name set to the result of the last interpreter command from the default \(aq_\(aq. Can be used to avoid conflicts with the _() function used for text translation. [NOSE_DOCTEST_RESULT_VAR]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-doctest\-fixtures=SUFFIX
+Find fixtures for a doctest file in module with this name appended to the base name of the doctest file
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-doctest\-options=OPTIONS
+Specify options to pass to doctest. Eg. \(aq+ELLIPSIS,+NORMALIZE_WHITESPACE\(aq
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-with\-isolation
+Enable plugin IsolationPlugin:
+Activate the isolation plugin to isolate changes to external
+modules to a single test module or package. The isolation plugin
+resets the contents of sys.modules after each test module or
+package runs to its state before the test. PLEASE NOTE that this
+plugin should not be used with the coverage plugin, or in any other case
+where module reloading may produce undesirable side\-effects.
+ [NOSE_WITH_ISOLATION]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-d, \-\-detailed\-errors, \-\-failure\-detail
+Add detail to error output by attempting to evaluate failed asserts [NOSE_DETAILED_ERRORS]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-with\-profile
+Enable plugin Profile:
+Use this plugin to run tests using the hotshot profiler.
+ [NOSE_WITH_PROFILE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-profile\-sort=SORT
+Set sort order for profiler output
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-profile\-stats\-file=FILE
+Profiler stats file; default is a new temp file on each run
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-profile\-restrict=RESTRICT
+Restrict profiler output. See help for pstats.Stats for details
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-no\-skip
+Disable special handling of SkipTest exceptions.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-with\-id
+Enable plugin TestId:
+Activate to add a test id (like #1) to each test name output. Activate
+with \-\-failed to rerun failing tests only.
+ [NOSE_WITH_ID]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-id\-file=FILE
+Store test ids found in test runs in this file. Default is the file .noseids in the working directory.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-failed
+Run the tests that failed in the last test run.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-processes=NUM
+Spread test run among this many processes. Set a number equal to the number of processors or cores in your machine for best results. Pass a negative number to have the number of processes automatically set to the number of cores. Passing 0 means to disable parallel testing. Default is 0 unless NOSE_PROCESSES is set. [NOSE_PROCESSES]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-process\-timeout=SECONDS
+Set timeout for return of results from each test runner process. Default is 10. [NOSE_PROCESS_TIMEOUT]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-process\-restartworker
+If set, will restart each worker process once their tests are done, this helps control memory leaks from killing the system. [NOSE_PROCESS_RESTARTWORKER]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-with\-xunit
+Enable plugin Xunit: This plugin provides test results in the standard XUnit XML format. [NOSE_WITH_XUNIT]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-xunit\-file=FILE
+Path to xml file to store the xunit report in. Default is nosetests.xml in the working directory [NOSE_XUNIT_FILE]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-xunit\-testsuite\-name=PACKAGE
+Name of the testsuite in the xunit xml, generated by plugin. Default test suite name is nosetests.
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-all\-modules
+Enable plugin AllModules: Collect tests from all python modules.
+ [NOSE_ALL_MODULES]
+.UNINDENT
+.INDENT 0.0
+.TP
+.B \-\-collect\-only
+Enable collect\-only:
+Collect and output test names only, don\(aqt run any tests.
+ [COLLECT_ONLY]
+.UNINDENT
+.SH AUTHOR
+Nose developers
+.SH COPYRIGHT
+2009, Jason Pellerin
+.\" Generated by docutils manpage writer.
+.
diff --git a/egc/pyvenv.cfg b/egc/pyvenv.cfg
new file mode 100644
index 0000000000..8dbc2bab8f
--- /dev/null
+++ b/egc/pyvenv.cfg
@@ -0,0 +1,3 @@
+home = /usr/bin
+include-system-site-packages = false
+version = 3.8.5
diff --git a/egc/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl b/egc/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl
new file mode 100644
index 0000000000..bd54b9bf43
Binary files /dev/null and b/egc/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/appdirs-1.4.3-py2.py3-none-any.whl b/egc/share/python-wheels/appdirs-1.4.3-py2.py3-none-any.whl
new file mode 100644
index 0000000000..a52728b763
Binary files /dev/null and b/egc/share/python-wheels/appdirs-1.4.3-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/certifi-2019.11.28-py2.py3-none-any.whl b/egc/share/python-wheels/certifi-2019.11.28-py2.py3-none-any.whl
new file mode 100644
index 0000000000..77ec5fbdff
Binary files /dev/null and b/egc/share/python-wheels/certifi-2019.11.28-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/chardet-3.0.4-py2.py3-none-any.whl b/egc/share/python-wheels/chardet-3.0.4-py2.py3-none-any.whl
new file mode 100644
index 0000000000..09a68abf45
Binary files /dev/null and b/egc/share/python-wheels/chardet-3.0.4-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/colorama-0.4.3-py2.py3-none-any.whl b/egc/share/python-wheels/colorama-0.4.3-py2.py3-none-any.whl
new file mode 100644
index 0000000000..071333268b
Binary files /dev/null and b/egc/share/python-wheels/colorama-0.4.3-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/contextlib2-0.6.0-py2.py3-none-any.whl b/egc/share/python-wheels/contextlib2-0.6.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..c0e697532d
Binary files /dev/null and b/egc/share/python-wheels/contextlib2-0.6.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/distlib-0.3.0-py2.py3-none-any.whl b/egc/share/python-wheels/distlib-0.3.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..819ffabc93
Binary files /dev/null and b/egc/share/python-wheels/distlib-0.3.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/distro-1.4.0-py2.py3-none-any.whl b/egc/share/python-wheels/distro-1.4.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..78243cc0b9
Binary files /dev/null and b/egc/share/python-wheels/distro-1.4.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl b/egc/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl
new file mode 100644
index 0000000000..dd13596886
Binary files /dev/null and b/egc/share/python-wheels/html5lib-1.0.1-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/idna-2.8-py2.py3-none-any.whl b/egc/share/python-wheels/idna-2.8-py2.py3-none-any.whl
new file mode 100644
index 0000000000..ccd236bb6a
Binary files /dev/null and b/egc/share/python-wheels/idna-2.8-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl b/egc/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..d013ec5464
Binary files /dev/null and b/egc/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl b/egc/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl
new file mode 100644
index 0000000000..26ddc279bd
Binary files /dev/null and b/egc/share/python-wheels/lockfile-0.12.2-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/msgpack-0.6.2-py2.py3-none-any.whl b/egc/share/python-wheels/msgpack-0.6.2-py2.py3-none-any.whl
new file mode 100644
index 0000000000..12ecde9652
Binary files /dev/null and b/egc/share/python-wheels/msgpack-0.6.2-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/packaging-20.3-py2.py3-none-any.whl b/egc/share/python-wheels/packaging-20.3-py2.py3-none-any.whl
new file mode 100644
index 0000000000..6978f2aa58
Binary files /dev/null and b/egc/share/python-wheels/packaging-20.3-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl b/egc/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl
new file mode 100644
index 0000000000..3ce172aef8
Binary files /dev/null and b/egc/share/python-wheels/pep517-0.8.2-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/pip-20.0.2-py2.py3-none-any.whl b/egc/share/python-wheels/pip-20.0.2-py2.py3-none-any.whl
new file mode 100644
index 0000000000..17982ec22f
Binary files /dev/null and b/egc/share/python-wheels/pip-20.0.2-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl b/egc/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..4aa6219f42
Binary files /dev/null and b/egc/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/progress-1.5-py2.py3-none-any.whl b/egc/share/python-wheels/progress-1.5-py2.py3-none-any.whl
new file mode 100644
index 0000000000..dfa9185afb
Binary files /dev/null and b/egc/share/python-wheels/progress-1.5-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/pyparsing-2.4.6-py2.py3-none-any.whl b/egc/share/python-wheels/pyparsing-2.4.6-py2.py3-none-any.whl
new file mode 100644
index 0000000000..6ace191328
Binary files /dev/null and b/egc/share/python-wheels/pyparsing-2.4.6-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/pytoml-0.1.21-py2.py3-none-any.whl b/egc/share/python-wheels/pytoml-0.1.21-py2.py3-none-any.whl
new file mode 100644
index 0000000000..bf9e9b3633
Binary files /dev/null and b/egc/share/python-wheels/pytoml-0.1.21-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/requests-2.22.0-py2.py3-none-any.whl b/egc/share/python-wheels/requests-2.22.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..34e8c3e135
Binary files /dev/null and b/egc/share/python-wheels/requests-2.22.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl b/egc/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl
new file mode 100644
index 0000000000..bfbb61c836
Binary files /dev/null and b/egc/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/setuptools-44.0.0-py2.py3-none-any.whl b/egc/share/python-wheels/setuptools-44.0.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..a334395f4a
Binary files /dev/null and b/egc/share/python-wheels/setuptools-44.0.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/six-1.14.0-py2.py3-none-any.whl b/egc/share/python-wheels/six-1.14.0-py2.py3-none-any.whl
new file mode 100644
index 0000000000..d49a864672
Binary files /dev/null and b/egc/share/python-wheels/six-1.14.0-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl b/egc/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl
new file mode 100644
index 0000000000..00080d686f
Binary files /dev/null and b/egc/share/python-wheels/urllib3-1.25.8-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl b/egc/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl
new file mode 100644
index 0000000000..76ff3422b4
Binary files /dev/null and b/egc/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl differ
diff --git a/egc/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl b/egc/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl
new file mode 100644
index 0000000000..772e407fb6
Binary files /dev/null and b/egc/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl differ
diff --git a/requirements.txt b/requirements.txt
index d5860a1eb4..901ca8dece 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,7 +4,7 @@ djangorestframework==3.7.7
django-cors-headers==2.1.0
requests==2.18.4
django-filter==1.1.0
-psycopg2==2.7.4
+psycopg2-binary==2.8.4
django-rest-swagger==2.2.0
coverage==4.5.2
django-nose==1.4.6