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

Properly handle transition time for profiles

parent a41edc14
No related branches found
No related tags found
No related merge requests found
...@@ -12,17 +12,7 @@ tasks = cli.AppGroup("tasks") ...@@ -12,17 +12,7 @@ tasks = cli.AppGroup("tasks")
def run_transitions(): def run_transitions():
# Handle profile transitions for profile in models.Profile.transition_ready().all():
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
print("Applying {}/{} to profile {}@{}".format( print("Applying {}/{} to profile {}@{}".format(
profile.transition, profile.transition_step, profile.username, profile.service.name)) profile.transition, profile.transition_step, profile.username, profile.service.name))
common.apply_transition(profile) common.apply_transition(profile)
......
from passlib import context, hash from passlib import context, hash
from flask import current_app as app from flask import current_app as app
from sqlalchemy.ext import declarative, mutable from sqlalchemy.ext import declarative, mutable
from datetime import datetime
from flask_babel import lazy_gettext as _ from flask_babel import lazy_gettext as _
import flask_sqlalchemy import flask_sqlalchemy
import flask_babel import flask_babel
import sqlalchemy import sqlalchemy
import datetime
import json import json
import uuid import uuid
...@@ -44,11 +44,11 @@ class Base(flask_sqlalchemy.Model): ...@@ -44,11 +44,11 @@ class Base(flask_sqlalchemy.Model):
@declarative.declared_attr @declarative.declared_attr
def created_at(cls): 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 @declarative.declared_attr
def updated_at(cls): 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 @declarative.declared_attr
def comment(cls): def comment(cls):
...@@ -221,6 +221,7 @@ class Profile(db.Model): ...@@ -221,6 +221,7 @@ class Profile(db.Model):
server_status = db.Column(db.String(25)) server_status = db.Column(db.String(25))
transition = db.Column(db.String(25)) transition = db.Column(db.String(25))
transition_step = 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)) extra = db.Column(mutable.MutableDict.as_mutable(JSONEncoded))
@property @property
...@@ -234,6 +235,16 @@ class Profile(db.Model): ...@@ -234,6 +235,16 @@ class Profile(db.Model):
user_uuid=user.uuid, user_uuid=user.uuid,
).filter(cls.status.in_((cls.ACTIVE, cls.BLOCKED, cls.REQUEST))) ).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): def transitions(self, actor):
return { return {
name: transition for name, transition in Profile.TRANSITIONS.items() name: transition for name, transition in Profile.TRANSITIONS.items()
...@@ -242,15 +253,16 @@ class Profile(db.Model): ...@@ -242,15 +253,16 @@ class Profile(db.Model):
} }
def transition_delta(self, formatted=False): 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 return flask_babel.format_timedelta(delta) if formatted else delta
def set_transition(self, transition, actor): def set_transition(self, transition, actor):
""" Prepare the profile for transition """ Prepare the profile for transition
""" """
_, _, delta, _, _ = Profile.TRANSITIONS[transition]
self.transition = transition self.transition = transition
self.transition_step = Profile.INIT self.transition_step = Profile.INIT
self.transition_time = datetime.datetime.now() + datetime.timedelta(seconds=delta)
log( log(
category=History.TRANSITION, category=History.TRANSITION,
value=transition, value=transition,
...@@ -282,7 +294,7 @@ class ResetToken(db.Model): ...@@ -282,7 +294,7 @@ class ResetToken(db.Model):
user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid)) user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid))
user = db.relationship(User) user = db.relationship(User)
expired_at = db.Column(db.DateTime, nullable=False) expired_at = db.Column(sqlalchemy.DateTime, nullable=False)
class History(db.Model): class History(db.Model):
...@@ -303,7 +315,7 @@ class History(db.Model): ...@@ -303,7 +315,7 @@ class History(db.Model):
CREATE: _("created the profile {this.profile.username} on {this.service.name}"), CREATE: _("created the profile {this.profile.username} on {this.service.name}"),
PASSWORD: _("changed this account password"), PASSWORD: _("changed this account password"),
STATUS: _("set the {this.service.name} profile {this.profile.username} as {this.value}"), 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)) user_uuid = db.Column(db.String(36), db.ForeignKey(User.uuid))
......
...@@ -15,7 +15,7 @@ def apply_transition(profile): ...@@ -15,7 +15,7 @@ def apply_transition(profile):
""" """
app = profile.service.application app = profile.service.application
transition = profile.transition transition = profile.transition
_, target, _, _ = models.Profile.TRANSITIONS[transition] _, target, _, _, _ = models.Profile.TRANSITIONS[transition]
step = profile.transition_step step = profile.transition_step
manual = app.apply_hooks(profile, transition, step) manual = app.apply_hooks(profile, transition, step)
profile.transition_step = { profile.transition_step = {
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<td>{{ macros.profile_status(profile) }}</td> <td>{{ macros.profile_status(profile) }}</td>
<td>{{ profile.created_at.date() }}</td> <td>{{ profile.created_at.date() }}</td>
<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> <a href="{{ url_for("profile.start_transition", profile_uuid=profile.uuid, transition=transition) }}">{{ label | capitalize }}</a>
{% endfor %} {% endfor %}
</td> </td>
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
<i class="fa fa-arrow-right"></i> <i class="fa fa-arrow-right"></i>
<span class="badge bg-{{ target[0] }}">{{ target[1] }}</span> <span class="badge bg-{{ target[0] }}">{{ target[1] }}</span>
{% if profile.transition_step == profile.INIT %} {% 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 %} {% elif profile.transition_step == profile.START %}
<i class="fa fa-rocket"></i> <i class="fa fa-rocket"></i>
{% elif profile.transition_step == profile.DONE %} {% elif profile.transition_step == profile.DONE %}
......
""" 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')
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