diff --git a/hiboo/cli.py b/hiboo/cli.py index 678496bc4e1c89e680c79dc8d0709b786510f994..9a0e4de9664bb9f5f64130ea6aeac84ba62a3be9 100644 --- a/hiboo/cli.py +++ b/hiboo/cli.py @@ -12,17 +12,7 @@ tasks = cli.AppGroup("tasks") def run_transitions(): - # Handle profile transitions - profiles = models.Profile.query.filter( - models.Profile.transition_step.in_ ( - [models.Profile.INIT, models.Profile.START, models.Profile.DONE] - ) - ).all() - for profile in profiles: - _, _, delay, _ = models.Profile.TRANSITIONS[profile.transition] - trigger = profile.updated_at + datetime.timedelta(seconds=delay) - if profile.transition_step == models.Profile.INIT and trigger > datetime.datetime.now(): - continue + for profile in models.Profile.transition_ready().all(): print("Applying {}/{} to profile {}@{}".format( profile.transition, profile.transition_step, profile.username, profile.service.name)) common.apply_transition(profile) diff --git a/hiboo/models.py b/hiboo/models.py index ec22bacd0304276a071fe5ea8d1403bfda42d756..1190488f161c7517a209dbd571c1d6c34e40affe 100644 --- a/hiboo/models.py +++ b/hiboo/models.py @@ -1,12 +1,12 @@ from passlib import context, hash from flask import current_app as app from sqlalchemy.ext import declarative, mutable -from datetime import datetime from flask_babel import lazy_gettext as _ import flask_sqlalchemy import flask_babel import sqlalchemy +import datetime import json import uuid @@ -44,11 +44,11 @@ class Base(flask_sqlalchemy.Model): @declarative.declared_attr def created_at(cls): - return sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, default=datetime.now) + return sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, default=datetime.datetime.now) @declarative.declared_attr def updated_at(cls): - return sqlalchemy.Column(sqlalchemy.DateTime, nullable=True, onupdate=datetime.now) + return sqlalchemy.Column(sqlalchemy.DateTime, nullable=True, onupdate=datetime.datetime.now) @declarative.declared_attr def comment(cls): @@ -221,6 +221,7 @@ class Profile(db.Model): server_status = db.Column(db.String(25)) transition = db.Column(db.String(25)) transition_step = db.Column(db.String(25)) + transition_time = db.Column(db.DateTime()) extra = db.Column(mutable.MutableDict.as_mutable(JSONEncoded)) @property @@ -234,6 +235,16 @@ class Profile(db.Model): user_uuid=user.uuid, ).filter(cls.status.in_((cls.ACTIVE, cls.BLOCKED, cls.REQUEST))) + @classmethod + def transition_ready(cls): + return cls.query.filter( + cls.transition_step.in_ ([cls.START, cls.DONE]) or + ( + cls.transition_step == cls.INIT and + datetime.datetime.now() > cls.transition_time + ) + ) + def transitions(self, actor): return { name: transition for name, transition in Profile.TRANSITIONS.items() @@ -242,15 +253,16 @@ class Profile(db.Model): } def transition_delta(self, formatted=False): - delta = datetime.now() - self.updated_at + delta = self.transition_time - datetime.datetime.now() if self.transition_time else 0 return flask_babel.format_timedelta(delta) if formatted else delta - def set_transition(self, transition, actor): """ Prepare the profile for transition """ + _, _, delta, _, _ = Profile.TRANSITIONS[transition] self.transition = transition self.transition_step = Profile.INIT + self.transition_time = datetime.datetime.now() + datetime.timedelta(seconds=delta) log( category=History.TRANSITION, value=transition, @@ -282,7 +294,7 @@ class ResetToken(db.Model): user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid)) user = db.relationship(User) - expired_at = db.Column(db.DateTime, nullable=False) + expired_at = db.Column(sqlalchemy.DateTime, nullable=False) class History(db.Model): @@ -303,7 +315,7 @@ class History(db.Model): CREATE: _("created the profile {this.profile.username} on {this.service.name}"), PASSWORD: _("changed this account password"), STATUS: _("set the {this.service.name} profile {this.profile.username} as {this.value}"), - TRANSITION: _("did {this.transition[3]} the profile {this.profile.username} on {this.service.name}") + TRANSITION: _("did {this.transition[4]} the profile {this.profile.username} on {this.service.name}") } user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid)) diff --git a/hiboo/profile/common.py b/hiboo/profile/common.py index 51f3d700601d9b67e19851660704894ef1041f07..e2928360bd74d8245cc6692e5311ec866a4c8eb7 100644 --- a/hiboo/profile/common.py +++ b/hiboo/profile/common.py @@ -15,7 +15,7 @@ def apply_transition(profile): """ app = profile.service.application transition = profile.transition - _, target, _, _ = models.Profile.TRANSITIONS[transition] + _, target, _, _, _ = models.Profile.TRANSITIONS[transition] step = profile.transition_step manual = app.apply_hooks(profile, transition, step) profile.transition_step = { diff --git a/hiboo/profile/templates/profile_list.html b/hiboo/profile/templates/profile_list.html index ed5bad882a145819f7e0c1a9c432e8d5f5dca026..9058bdb852f0c15c83ffe95a3ab810617c9ec2dc 100644 --- a/hiboo/profile/templates/profile_list.html +++ b/hiboo/profile/templates/profile_list.html @@ -44,7 +44,7 @@ <td>{{ macros.profile_status(profile) }}</td> <td>{{ profile.created_at.date() }}</td> <td> - {% for transition, (_, _, _, _, label) in profile.transitions(current_user.is_admin).items() %} + {% for transition, (_, _, _, _, label) in profile.transitions(current_user).items() %} <a href="{{ url_for("profile.start_transition", profile_uuid=profile.uuid, transition=transition) }}">{{ label | capitalize }}</a> {% endfor %} </td> diff --git a/hiboo/templates/macros.html b/hiboo/templates/macros.html index 8efe7fd6b6be899adbf0f37a9aa21b9973f0f27e..f9d62b237a36d95269d0a379a04a34d6d8d45563 100644 --- a/hiboo/templates/macros.html +++ b/hiboo/templates/macros.html @@ -39,7 +39,7 @@ <i class="fa fa-arrow-right"></i> <span class="badge bg-{{ target[0] }}">{{ target[1] }}</span> {% if profile.transition_step == profile.INIT %} - <i class="fa fa-clock-o"></i> + <i class="fa fa-clock-o"></i> {{ profile.transition_delta(True) }} {% elif profile.transition_step == profile.START %} <i class="fa fa-rocket"></i> {% elif profile.transition_step == profile.DONE %} diff --git a/migrations/versions/445033285d55_add_transition_time.py b/migrations/versions/445033285d55_add_transition_time.py new file mode 100644 index 0000000000000000000000000000000000000000..faf29d90bd74be651cde03d979b515e395849b2b --- /dev/null +++ b/migrations/versions/445033285d55_add_transition_time.py @@ -0,0 +1,26 @@ +""" Add a timestamp for profile transitions + +Revision ID: 445033285d55 +Revises: 99634916f37f +Create Date: 2020-12-20 11:58:00.642289 +""" + +from alembic import op +import sqlalchemy as sa +import hiboo + + +revision = '445033285d55' +down_revision = '99634916f37f' +branch_labels = None +depends_on = None + + +def upgrade(): + with op.batch_alter_table('profile') as batch_op: + batch_op.add_column(sa.Column('transition_time', sa.DateTime(), nullable=True)) + + +def downgrade(): + with op.batch_alter_table('profile') as batch_op: + batch_op.drop_column('transition_time')