Giter VIP home page Giter VIP logo

roche's Introduction

roche

Web app for asynchronous roching

Start from the beginning

  • Set up DigitalOcean server
  • Create user
  • Set up ssh keys
  • Install Python + PIP

Booting up

  • If it doesn't exist already: pyvenv roche_env
  • source roche_env/bin/active
  • (deactivate to escape)

Adding packages

  • Note: install these while in the venv!
  • python-decouple
  • django-sendgrid-v5
  • django-twilio
  • phonenumbers (dependency for django-twilio that doesn't automatically come with it)

Starting Django

  • Make sure to add 0.0.0.0 and your domain to ALLOWED_HOSTS in settings.py
  • `python manage.py runserver 0:8000

Editing Django settings

  • Set time zone in roche/settings.py
  • Create admin (python manage.py createsuperuser)
  • Add environment variables to .env in root directory
  • Edit settings.py to change or add environment variables: LOGIN_REDIRECT_URL, EMAIL_BACKEND, SENDGRID_API_KEY, DEFAULT_FROM_EMAIL

Adding apps

  • Add to INSTALLED_APPS in the settings.py file for the project (not any one of the apps)
  • Add appname.apps.AppnameConfig (substitute in the real app's name for appname/Appname)
  • Add django_twilio

django-twilio

  • Need to pip install two other packages: phonenumbers and wheel
  • Add export TWILIO_ACCOUNT_SID=xxx and export TWILIO_AUTH_TOKEN=yyy to /bin/activate.sh of the virtual environment (while in the virtual environment)

roche's People

Contributors

tdingman avatar

Watchers

 avatar

roche's Issues

[3] Set up core

  • python manage.py startapp core
  • Add core.apps.CoreConfig to INSTALLED_APPS
  • Add 1 to roche/urls.py
  • Add 2 to roche/core/urls.py

1:

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('core.urls')),
]

2:

from django.urls import path
from . import views

urlpatterns = [
]

[8] Set up infrastructure

  • pyvenv roche_env
  • source roche_env/bin/activate
  • django-admin startproject roche
  • Add 0.0.0.0 and justroche.com to ALLOWED_HOSTS in settings.py
  • Set time zone to America/New_York on TIME_ZONE in settings.py
  • python manage.py makemigrations --> python manage.py migrate
  • python manage.py createsuperuser

[8] Update roche status

Roches need to change status somehow

  • Detect when there are at least two participants who have accepted
  • Allow creator to move from open to in-progress
  • Allow create to move from open to expired
  • Detect when there is only one participant remaining
  • Update status to complete
  • Update status to fulfilled

This had a lot of extra work about updating rounds!

  • Determine wash or winners/losers
  • Eliminate winners/losers
  • Create new round if more than one participant remaining

[13] Set up user creation + authentication

  • Add path('accounts/', include('django.contrib.auth.urls')), to roche/urls.py
  • Create roche/templates
  • Create roche/templates/registration
  • Add [os.path.join(BASE_DIR, 'templates')], to TEMPLATES > DIRS in roche/roche/settings.py
  • Create login.html in templates/registration like 1
  • Change login redirect to / in roche/settings.py with LOGIN_REDIRECT_URL = '/'
  • Create logged_out.html in templates/registration like 2
  • Create password_reset_form.html in templates/registration like 3
  • Create password_reset_done.html in templates/registration like 4
  • Create password_reset_email in templates/registration like 5
  • Create password_reset_confirm in templates/registration like 6
  • Create password_reset_complete in templates/registration like 7
  • Add email backend with EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' to roche/settings.py
  • Add login/logout link to nav (probably in roche/templates/base_generic.html) like 8

1:

{% extends "base_generic.html" %}

{% block content %}

{% if form.errors %}
  <p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
  {% if user.is_authenticated %}
    <p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
  {% else %}
    <p>Please login to see this page.</p>
  {% endif %}
{% endif %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>

<tr>
  <td>{{ form.username.label_tag }}</td>
  <td>{{ form.username }}</td>
</tr>

<tr>
  <td>{{ form.password.label_tag }}</td>
  <td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>

{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>

{% endblock %}

2:

{% extends "base_generic.html" %}

{% block content %}
  <p>Logged out!</p>  
  <a href="{% url 'login'%}">Click here to login again.</a>
{% endblock %}

3

{% extends "base_generic.html" %}

{% block content %}
  <form action="" method="post">
  {% csrf_token %}
  {% if form.email.errors %}
    {{ form.email.errors }}
  {% endif %}
      <p>{{ form.email }}</p> 
    <input type="submit" class="btn btn-default btn-lg" value="Reset password">
  </form>
{% endblock %}

4:

{% extends "base_generic.html" %}

{% block content %}
  <p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
{% endblock %}

5:

Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

6:

{% extends "base_generic.html" %}

{% block content %}
    {% if validlink %}
        <p>Please enter (and confirm) your new password.</p>
        <form action="" method="post">
        {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">New password:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirm password:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Change my password" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Password reset failed</h1>
        <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
    {% endif %}
{% endblock %}

7:

{% extends "base_generic.html" %}

{% block content %}
  <h1>The password has been changed!</h1>
  <p><a href="{% url 'login' %}">log in again?</a></p>
{% endblock %}

8:

<ul class="sidebar-nav">
   {% if user.is_authenticated %}
     <li>User: {{ user.get_username }}</li>
     <li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>   
   {% else %}
     <li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>   
   {% endif %} 
  </ul>

[8] Add forms to create roche and invite friends

SubmitRoche should be a CreateView, whereas InviteParticipantForm should be a regular ol' Form

For InviteParticipantForm:

  • Create core/forms.py like 1 and add InviteParticipantForm
  • Add validation like 2
  • Add link to form in core/urls.py
  • Add view to core/views.py like 3
  • Add template core/templates/core/invite_participant.html like 4

For SubmitRoche:

  • Create SubmitRoche in core/views.py
  • Add url to core/urls.py
  • Create template in core/templates/core/roche_form.html

1:

from django import forms
    
class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")

2:

import datetime

from django import forms
from django.core.exceptions import ValidationError
# The method below isn't necessary, just for later internationalization
from django.utils.translation import ugettext_lazy as _

class RenewBookForm(forms.Form):
    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")

    def clean_renewal_date(self):
        data = self.cleaned_data['renewal_date']
        
        # Check if a date is not in the past. 
        if data < datetime.date.today():
            raise ValidationError(_('Invalid date - renewal in past'))

        # Check if a date is in the allowed range (+4 weeks from today).
        if data > datetime.date.today() + datetime.timedelta(weeks=4):
            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))

        # Remember to always return the cleaned data.
        return data

3:

import datetime

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse

from catalog.forms import RenewBookForm

def renew_book_librarian(request, pk):
    book_instance = get_object_or_404(BookInstance, pk=pk)

    # If this is a POST request then process the Form data
    if request.method == 'POST':

        # Create a form instance and populate it with data from the request (binding):
        form = RenewBookForm(request.POST)

        # Check if the form is valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
            book_instance.due_back = form.cleaned_data['renewal_date']
            book_instance.save()

            # redirect to a new URL:
            return HttpResponseRedirect(reverse('all-borrowed') )

    # If this is a GET (or any other method) create the default form.
    else:
        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date})

    context = {
        'form': form,
        'book_instance': book_instance,
    }

    return render(request, 'catalog/book_renew_librarian.html', context)

4:

{% extends "base_generic.html" %}

{% block content %}
  <h1>Renew: {{ book_instance.book.title }}</h1>
  <p>Borrower: {{ book_instance.borrower }}</p>
  <p{% if book_instance.is_overdue %} class="text-danger"{% endif %}>Due date: {{ book_instance.due_back }}</p>
    
  <form action="" method="post">
    {% csrf_token %}
    <table>
    {{ form.as_table }}
    </table>
    <input type="submit" value="Submit">
  </form>
{% endblock %}

[8] Create templates + urls

Sample templates for generic_base.html and index.html

Sample template for user.html

  • generic_base.html
  • `` (index) using list view (2) and core/templates/roche_list.html
  • <int:pk> using detail view (3) and core/templates/roche_detail.html
  • users/<int:pk> using list view (4) and core/templates/user.html
  • Add to core/urls.py (1)
  • Add templates directory to TEMPLATES > DIRS in settings.py: os.path.join(BASE_DIR, 'templates'),

1:

urlpatterns = [
    path('', views.RocheListView.as_view(), name='index'),
    path('<int:pk>', views.RocheDetailView.as_view(), name='roche'),
]

2

from django.views import generic

class RocheListView(generic.ListView):
    model = Roche

3:

class RocheDetailView(generic.DetailView):
    model = Roche

4:

class LoanedBooksByUserListView(generic.ListView):
    model = BookInstance
    template_name ='catalog/bookinstance_list_borrowed_user.html'
    
    def get_queryset(self):
        return BookInstance.objects.filter(borrower=id).filter(status__exact='o').order_by('due_back')

^ I'm actually not sure about that borrower=id piece

[8] Create models

  • Add 1 to roche/core/models.py for Roche (reference db model for details)
  • Repeat step above but for Round
  • Repeat step above but for Participant
  • Repeat step above but for Consequence
  • Re-run db migrations: python manage.py makemigrations --> python manage.py migrate
  • Register models in core/admin.py (2)

1:

from django.db import models

class MyModelName(models.Model):

    # Fields
    my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
    # Metadata
    class Meta:
        ordering = ['-my_field_name']
    # Methods
    def get_absolute_url(self):
        return reverse('model-detail-view', args=[str(self.id)])
    def __str__(self):
        return self.my_field_name

2:

from django.contrib import admin
from catalog.models import MyModelName

admin.site.register(MyModelName)

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.