~craftyguy/ridecasa

ca96418bdfd2c649553878fc8f0bd77d1097b88c — Clayton Craft 2 years ago cd272d7 equipment_tracker
DO NOT MERGE: equipment mockups
M ridecasa/__init__.py => ridecasa/__init__.py +2 -0
@@ 106,6 106,7 @@ def register_blueprints(app):
        from ridecasa.user.views import user_blueprint
        from ridecasa.activity.views import activity_blueprint
        from ridecasa.settings.views import settings_blueprint
        from ridecasa.equipment.views import equipment_blueprint

        # Register Blueprints
        app.register_blueprint(home_blueprint)


@@ 113,5 114,6 @@ def register_blueprints(app):
        app.register_blueprint(user_blueprint)
        app.register_blueprint(activity_blueprint)
        app.register_blueprint(settings_blueprint, url_prefix='/settings')
        app.register_blueprint(equipment_blueprint)

        return app

A ridecasa/equipment/forms.py => ridecasa/equipment/forms.py +36 -0
@@ 0,0 1,36 @@
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.fields import BooleanField, SelectField
from wtforms.validators import AnyOf, EqualTo, Optional, DataRequired


class SelectEquipmentForm(FlaskForm):
    current_equipment = SelectField()
    show_part_active = BooleanField('Show active parts', default=True)
    show_part_retired = BooleanField('Show retired parts', default=False)


class NewEquipmentForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    type = SelectField('Type of equipment', choices=['bicycle'])
    description = StringField('Description', maxlength=50)


class NewPartForm(FlaskForm):
    name = StringField('Name', validators=[DataRequired()])
    type = SelectField('Type of part', choices=[
        'brake cable',
        'brake lever',
        'brake pads/shoes',
        'cassette',
        'chain',
        'chainring'
        'cranks',
        'derailleur',
        'handlebar',
        'pedals',
        'saddle',
        'shifter cable',
        'shifter',
    ], validators=[DataRequired()])
    description = StringField('Description', maxlength=50)

A ridecasa/equipment/views.py => ridecasa/equipment/views.py +42 -0
@@ 0,0 1,42 @@
from datetime import datetime
from dateutil.relativedelta import relativedelta
from flask import (Blueprint, make_response, redirect, render_template,
                   request)
from flask_login import current_user, login_required
from pony.orm import db_session
from pony import orm
from ridecasa.equipment.forms import SelectEquipmentForm
from ridecasa.models import db
from ridecasa.util import get_user_prefs

equipment_blueprint = Blueprint('equipment', __name__)


@equipment_blueprint.route('/equipment', methods=['GET', 'POST'])
@db_session
def main():
    if not current_user.is_authenticated:
        return redirect('/login')

    prefs = get_user_prefs(current_user.id)
    equipment_form = SelectEquipmentForm()

    # TODO: add equipment modal

    if request.method == 'POST':
        # TODO: buttons should have part ID to remove or replace
        # TODO: handle remove part and replace part submissions
        if 'deletePartButton' in request.form:
            token = request.form['shareButton']
            # if part.user != current_user:
            #    abort(500)
    user_equipment = orm.select((e.id, e.name, e.description, e.type,
                                e.added_date, e.distanceMeters) for e in
                                db.Equipment if e.user.id ==
                                current_user.id)[:]
    # TODO: set selectequipmentform select field
    # TODO: set bool fields for selectequipmentform


    resp = make_response(render_template('equipment/main.html'))
    return resp

M ridecasa/models/models.py => ridecasa/models/models.py +24 -0
@@ 21,6 21,8 @@ class User(db.Entity, UserMixin):
    activities = orm.Set('Activity')
    prefs = orm.Optional('UserPref', cascade_delete=True)
    msgs = orm.Set('UserMessage')
    equipment = orm.Set('Equipment')
    equipment_parts = orm.Set('EquipmentPart')


class UserPref(db.Entity):


@@ 76,3 78,25 @@ class TrackPoint(db.Entity):
    speed = orm.Required(float)
    startOffsetSeconds = orm.Required(int)
    activity = orm.Required(Activity, index=True)


class Equipment(db.Entity):
    name = orm.Required(str)
    type = orm.Required(str)
    added_date = orm.Required(datetime.datetime)
    description = orm.Optional(str)
    user = orm.Required(User)
    distanceMeters = orm.Required(float, default=0.0)
    parts = orm.Set('EquipmentPart')


class EquipmentPart(db.Entity):
    name = orm.Required(str)
    type = orm.Required(str)
    added_date = orm.Required(datetime.datetime)
    retired_date = orm.Optional(datetime.datetime)
    description = orm.Optional(str)
    user = orm.Required(User)
    retired = orm.Required(bool)
    distanceMeters = orm.Required(float, default=0.0)
    equipment = orm.Required(Equipment, index=True)

A ridecasa/templates/equipment/main.html => ridecasa/templates/equipment/main.html +56 -0
@@ 0,0 1,56 @@
{% extends "layout.html" %}
{% block head %}
    <script nonce="{{ csp_nonce() }}" type="text/javascript" src="/static/js/moment-with-locales.min.js"></script>
{% endblock %}

{% block content %}
<section class="section">
  <div class="field">
    <div class="field" id="chart_section">
      <table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
        <thead>
          <tr>
            <th>Name</th>
            <th>Description</th>
            <th>Type</th>
            <th>Distance (km)</th>
            <th>Age</th>
            <th/>
          </tr>
        </thead>
        {% for part in parts %}
        <tr>
          <th>{{part.name}}</th>
          <th>{{part.description}}</th>
          <th>{{part.type}}</th>
          <th>{{part.distance}}</th>
          <th>{{part.age}}</th>
          <th>
            <button name="removePartButton" class="button is-danger is-small" type="submit" value="{{ part.id }}">Remove</button>
          </th>
        </tr>
        {% endfor %}
      </table>
    </div>
  </div>
  <div class="field">
    <div class="container">
    </div>
  </div>
</section>

<div class="modal" id="add_equipment_modal">
  <div class="modal-background"></div>
  <div class="modal-card">
    <form id="add_equipment_form">
      <header class="modal-card-head">
        <p class="modal-card-title">Add New Equipment</p>
        <button class="delete toggle-modal" aria-label="close"></button>
      </header>
      <section class="modal-card-body" style="overflow:hidden">
        <!-- TODO: add equipment form here-->
      </section>
    </form>
  </div>
</div>
{% endblock %}

M ridecasa/templates/home/summary.html => ridecasa/templates/home/summary.html +14 -0
@@ 87,6 87,20 @@
      {% endfor %}
    </table>
  </div>
      <nav class="pagination is-centered" role="navigation" aria-label="pagination">
  <a class="pagination-previous">Previous</a>
  <a class="pagination-next">Next page</a>
  <ul class="pagination-list">
    <li><a class="pagination-link" aria-label="Goto page 1">1</a></li>
    <li><span class="pagination-ellipsis">&hellip;</span></li>
    <li><a class="pagination-link" aria-label="Goto page 45">45</a></li>
    <li><a class="pagination-link is-current" aria-label="Page 46" aria-current="page">46</a></li>
    <li><a class="pagination-link" aria-label="Goto page 47">47</a></li>
    <li><span class="pagination-ellipsis">&hellip;</span></li>
    <li><a class="pagination-link" aria-label="Goto page 86">86</a></li>
  </ul>
</nav>

  {% else %}
  <div class="label is-medium" style="text-align:center">No activies here! Use the upload button above to start uploading activities!</div>
  {% endif %}

M ridecasa/templates/layout.html => ridecasa/templates/layout.html +1 -1
@@ 33,6 33,7 @@
          </div>
          <div class="navbar-end">
            {% if current_user.is_authenticated %}
            <a class="button is-primary" href="{{ url_for('equipment.main') }}"><strong>Equipment</strong></a>
            <div class="navbar-item has-dropdown is-hoverable has-text-dark">
              <a class="navbar-link has-text-dark">
                <strong>{{ current_user.username }}</strong>


@@ 105,7 106,6 @@
                      Overwrite existing activities?</input>
                    </label>
                  </p>

                </div>
              </section>
            </footer>

M tests/conftest.py => tests/conftest.py +11 -0
@@ 5,6 5,17 @@ import ridecasa.models

@pytest.fixture
def client():
    # db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp()
    # flaskr.app.config['TESTING'] = True

    # with flaskr.app.test_client() as client:
    #     with flaskr.app.app_context():
    #         flaskr.init_db()
    #     yield client

    # os.close(db_fd)
    # os.unlink(flaskr.app.config['DATABASE'])

    flask_app = ridecasa.create_testing_app('ridecasa.config.TestConfig')
    with flask_app.test_client() as client:
        yield client