Skip to content
Snippets Groups Projects
Commit 75aacd27 authored by kaiyou's avatar kaiyou
Browse files

Add an event history and display it on the home page

parent 76cb3b97
No related branches found
No related tags found
No related merge requests found
""" empty message
Revision ID: 3fe66efb6088
Revision ID: b2fe21a1da94
Revises:
Create Date: 2019-09-14 12:13:37.847931
Create Date: 2019-09-14 13:27:34.971323
"""
from alembic import op
import sqlalchemy as sa
revision = '3fe66efb6088'
revision = 'b2fe21a1da94'
down_revision = None
branch_labels = None
depends_on = None
......@@ -23,16 +23,16 @@ def upgrade():
sa.Column('max_profiles', sa.Integer(), nullable=True),
sa.Column('config', sa.String(), nullable=True),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.Date(), nullable=False),
sa.Column('updated_at', sa.Date(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('comment', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('uuid')
)
op.create_table('user',
sa.Column('username', sa.String(length=255), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.Date(), nullable=False),
sa.Column('updated_at', sa.Date(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('comment', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('uuid'),
sa.UniqueConstraint('username')
......@@ -42,8 +42,8 @@ def upgrade():
sa.Column('value', sa.String(), nullable=True),
sa.Column('extra', sa.String(), nullable=True),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.Date(), nullable=False),
sa.Column('updated_at', sa.Date(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('comment', sa.String(length=255), nullable=True),
sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], ),
sa.PrimaryKeyConstraint('uuid')
......@@ -54,18 +54,36 @@ def upgrade():
sa.Column('username', sa.String(length=255), nullable=False),
sa.Column('status', sa.String(length=25), nullable=False),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.Date(), nullable=False),
sa.Column('updated_at', sa.Date(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('comment', sa.String(length=255), nullable=True),
sa.ForeignKeyConstraint(['service_uuid'], ['service.uuid'], ),
sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], ),
sa.PrimaryKeyConstraint('uuid')
)
op.create_table('history',
sa.Column('user_uuid', sa.String(length=36), nullable=True),
sa.Column('profile_uuid', sa.String(length=36), nullable=True),
sa.Column('service_uuid', sa.String(length=36), nullable=True),
sa.Column('actor_uuid', sa.String(length=36), nullable=True),
sa.Column('category', sa.String(length=25), nullable=True),
sa.Column('value', sa.String(), nullable=True),
sa.Column('uuid', sa.String(length=36), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('comment', sa.String(length=255), nullable=True),
sa.ForeignKeyConstraint(['actor_uuid'], ['user.uuid'], ),
sa.ForeignKeyConstraint(['profile_uuid'], ['profile.uuid'], ),
sa.ForeignKeyConstraint(['service_uuid'], ['service.uuid'], ),
sa.ForeignKeyConstraint(['user_uuid'], ['user.uuid'], ),
sa.PrimaryKeyConstraint('uuid')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('history')
op.drop_table('profile')
op.drop_table('auth')
op.drop_table('user')
......
from trurt.account import blueprint
from trurt.sso import forms as sso_forms
from trurt import models, utils
import flask_login
import flask
......@@ -13,13 +14,13 @@ def profiles():
@blueprint.route("/pick")
@flask_login.login_required
def pick():
service_spn = flask.request.args.get("service_spn") or flask.abort(404)
service = models.Service.query.filter_by(spn=service_spn).first_or_404()
service_uuid = flask.request.args.get("service_uuid") or flask.abort(404)
service = models.Service.query.get(service_uuid) or flask.arort(404)
profiles = models.Profile.query.filter_by(
service_id=service.id,
user_id=flask_login.current_user.id
service_uuid=service.uuid,
user_uuid=flask_login.current_user.uuid
)
form = sso_forms.SSOValidateForm()
return flask.render_template("sso_pick.html",
return flask.render_template("account_pick.html",
service=service, profiles=profiles, form=form,
action=utils.url_or_intent("account.status"))
......@@ -7,7 +7,8 @@ import flask
@blueprint.route("/home")
@flask_login.login_required
def home():
return flask.render_template("account_home.html")
history = flask_login.current_user.history
return flask.render_template("account_home.html", history=history)
@blueprint.route("/password")
......
......@@ -2,3 +2,31 @@
{% block title %}My account{% endblock %}
{% block subtitle %}overview of {{ current_user.username }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6 col-s-12">
<section class="content">
<div class="row">
<div class="col-md-6 col-xs-12">
{{ macros.infobox("Account age", "{} days".format((current_user.created_at.today() - current_user.created_at).total_seconds() // 86400), "aqua", "calendar") }}
</div>
<div class="col-md-6 col-xs-12">
{{ macros.infobox("Profile count", current_user.profiles.__len__(), "red", "users") }}
</div>
</div>
<div class="row">
<div class="col-md-6 col-xs-12">
{{ macros.infobox("Pending requests", "0", "green", "hourglass") }}
</div>
<div class="col-md-6 col-xs-12">
{{ macros.infobox("Role", "registered user", "yellow", "lock") }}
</div>
</div>
</section>
</div>
<div class="col-md-6 col-s-12">
{{ macros.timeline(history) }}
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% block title %}Pick a profile{% endblock %}
{% block subtitle %}for the service {{ service.name }}{% endblock %}
{% block content %}
{% for profile in profiles %}
<form method="POST" action="{{ action }}">
{{ form.hidden_tag() }}
<input type="hidden" name="profile_uuid" value="{{ profile.uuid }}">
<input type="submit" value="{{ profile.username }}">
</form>
{% endfor %}
{% endblock %}
......@@ -9,7 +9,8 @@ DEFAULT_CONFIG = {
'SQLALCHEMY_DATABASE_URI': 'sqlite:////tmp/trurt.db',
'SQLALCHEMY_TRACK_MODIFICATIONS': False,
'SECRET_KEY': 'changeMe',
'TEMPLATES_AUTO_RELOAD': False
'TEMPLATES_AUTO_RELOAD': False,
'MAIL_DOMAIN': 'tedomum.net'
}
class ConfigManager(dict):
......
......@@ -23,6 +23,11 @@ def create_user(username, password):
auth = models.Auth()
auth.set_password(password)
user.auths.append(auth)
event = models.History()
event.user = user
event.category = models.History.REGISTER
event.comment = "Created from the command line"
models.db.session.add(event)
models.db.session.add(user)
models.db.session.add(auth)
models.db.session.commit()
......@@ -40,5 +45,13 @@ def create_profile(username, service_uuid, profile_username):
profile.user = user
profile.service = service
profile.username = profile_username
event = models.History()
event.user = user
event.service = service
event.profile = profile
event.category = models.History.CREATE
event.value = profile.username
event.comment = "Created from the command line"
models.db.session.add(event)
models.db.session.add(profile)
models.db.session.commit()
from passlib import context, hash
from flask import current_app as app
from sqlalchemy.ext import declarative
from datetime import date
from datetime import datetime
import flask_sqlalchemy
import sqlalchemy
......@@ -26,11 +26,11 @@ class Base(flask_sqlalchemy.Model):
@declarative.declared_attr
def created_at(cls):
return sqlalchemy.Column(sqlalchemy.Date, nullable=False, default=date.today)
return sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, default=datetime.now)
@declarative.declared_attr
def updated_at(cls):
return sqlalchemy.Column(sqlalchemy.Date, nullable=True, onupdate=date.today)
return sqlalchemy.Column(sqlalchemy.DateTime, nullable=True, onupdate=datetime.today)
@declarative.declared_attr
def comment(cls):
......@@ -132,6 +132,7 @@ class Profile(db.Model):
__tablename__ = "profile"
ACTIVE = "active"
DELETED = "deleted"
user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid))
service_uuid = db.Column(db.String(36), db.ForeignKey(Service.uuid))
......@@ -145,4 +146,42 @@ class Profile(db.Model):
@property
def email(self):
return self.uuid + "@kaiyou.fr"
return "{}@{}".format(self.uuid, app.config.get("MAIL_DOMAIN"))
class History(db.Model):
""" Records an even in an account's or profile's lifetime.
"""
__tablename__ = "history"
REGISTER = "register"
CREATE = "create"
DELETE = "delete"
NOTE = "note"
STATUS = "status"
DESCRIPTION = {
REGISTER: "registered this account",
CREATE: "created the profile {this.profile.username} on {this.service.name}",
}
user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid))
profile_uuid = db.Column(db.String(36), db.ForeignKey(Profile.uuid))
service_uuid = db.Column(db.String(36), db.ForeignKey(Service.uuid))
actor_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid))
user = db.relationship(User, foreign_keys=[user_uuid],
backref=db.backref('history', cascade='all, delete-orphan'))
profile = db.relationship(Profile,
backref=db.backref('history', cascade='all, delete-orphan'))
service = db.relationship(Service,
backref=db.backref('history', cascade='all, delete-orphan'))
actor = db.relationship(User, foreign_keys=[actor_uuid],
backref=db.backref('actions', cascade='all, delete-orphan'))
category = db.Column(db.String(25))
value = db.Column(db.String())
@property
def description(self):
print(History.DESCRIPTION.get(self.category, "").format(this=self))
return History.DESCRIPTION.get(self.category, "").format(this=self)
{% import "macros.html" as macros %}
<!doctype html>
<html>
<head>
......
{% macro timeline(events) %}
<ul class="timeline">
{% set dates = [] %}
{% for event in events | reverse %}
{% if not event.created_at.date() == dates[-1] %}
{% set _ = dates.append(event.created_at.date()) %}
<li class="time-label">
<span class="bg-red">{{ event.created_at.date() }}</span>
</li>
{% endif %}
<li>
<i class"fa fa-enveloppe bg-blue"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> {{ event.created_at.time().strftime("%H:%m") }}</span>
<h3 class="timeline-header">
<strong>{{ event.actor.username or "You" }}</strong>
{{ event.description }}
</h3>
<div class="timeline-body">
{{ event.comment }}
</div>
</div>
</li>
{% endfor %}
</ul>
{% endmacro %}
{% macro infobox(title, text, color, icon) %}
<div class="info-box">
<span class="info-box-icon bg-{{ color }}"><i class="fa fa-{{ icon }}"></i></span>
<div class="info-box-content">
<span class="info-box-text">{{ title }}</span>
<span class="info-box-number">{{ text }}</span>
</div>
</div>
{% endmacro %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment