Beispiel #1
0
class ActivityVersion(EventBaseModel, Period):
    backref_base_name = "activity_revisions"

    activity_id = db.Column(
        db.Integer, db.ForeignKey("activity.id"), index=True, nullable=False
    )
    activity = db.relationship("Activity", backref=backref("revisions"))

    version = db.Column(db.Integer, nullable=False)
    context = db.Column(JSONB(none_as_null=True), nullable=True)

    @property
    def type(self):
        return self.activity.type

    __table_args__ = (
        db.UniqueConstraint(
            "version",
            "activity_id",
            name="unique_version_among_same_activity_versions",
        ),
        db.Constraint(
            name="activity_version_start_time_before_reception_time"
        ),
        db.Constraint(name="activity_version_end_time_before_reception_time"),
        db.Constraint(name="activity_version_start_time_before_end_time"),
    )

    def __repr__(self):
        return f"<Revision [{self.id}] of {self.activity}>"
Beispiel #2
0
class Expenditure(UserEventBaseModel, Dismissable):
    backref_base_name = "expenditures"

    mission_id = db.Column(db.Integer,
                           db.ForeignKey("mission.id"),
                           index=True,
                           nullable=False)
    mission = db.relationship("Mission", backref=backref("expenditures"))

    type = enum_column(ExpenditureType, nullable=False)

    __table_args__ = (
        db.Constraint("no_duplicate_expenditures_per_user_and_mission"), )

    def __repr__(self):
        return f"<Expenditure [{self.id}] : {self.type.value}>"
Beispiel #3
0
class OAuth2Token(BaseModel, TokenMixin):
    __tablename__ = "oauth2_token"

    client_id = db.Column(
        db.Integer,
        db.ForeignKey("oauth2_client.id"),
        index=True,
        nullable=False,
    )
    token = db.Column(db.String(255), unique=True, nullable=False)
    user_id = db.Column(db.Integer,
                        db.ForeignKey("user.id"),
                        index=True,
                        nullable=False)
    user = db.relationship("User", backref="oauth_tokens")

    revoked_at = db.Column(DateTimeStoredAsUTC)

    __table_args__ = (db.Constraint(
        name="only_one_active_token_per_user_and_client"), )

    def get_client_id(self):
        return self.client_id

    def get_scope(self):
        return ""

    def get_expires_in(self):
        return 86400

    def expires_at(self):
        return time.time() + 86400

    @property
    def revoked(self):
        return self.revoked_at is not None
Beispiel #4
0
class Activity(UserEventBaseModel, Dismissable, Period):
    backref_base_name = "activities"

    mission_id = db.Column(db.Integer,
                           db.ForeignKey("mission.id"),
                           index=True,
                           nullable=False)
    mission = db.relationship("Mission", backref=backref("activities"))

    type = enum_column(ActivityType, nullable=False)

    last_update_time = db.Column(DateTimeStoredAsUTC, nullable=False)

    editable_fields = {"start_time", "end_time"}

    __table_args__ = (
        db.Constraint(name="no_overlapping_acknowledged_activities"),
        db.Constraint(name="activity_start_time_before_end_time"),
        db.Constraint(name="activity_start_time_before_update_time"),
        db.Constraint(name="activity_end_time_before_update_time"),
        db.Constraint(name="no_successive_activities_with_same_type"),
    )

    # TODO : add (maybe)
    # - validator
    # - version (each version represents a set of changes to the day activities)
    # OR revises (indicates which prior activity the current one revises)

    def __repr__(self):
        return f"<Activity [{self.id}] : {self.type.value}>"

    def latest_version_number(self):
        return (max([r.version
                     for r in self.revisions]) if self.revisions else None)

    def version_at(self, at_time):
        if self.reception_time > at_time:
            return None
        if self.dismissed_at and self.dismissed_at <= at_time:
            return None
        return max(
            [r for r in self.revisions if r.reception_time <= at_time],
            key=lambda r: r.version,
        )

    def revise(
        self,
        revision_time,
        revision_context=None,
        bypass_check=False,
        **updated_props,
    ):
        from app.domain.log_activities import handle_activities_update

        if self.is_dismissed:
            raise ResourceAlreadyDismissedError("Activity already dismissed")

        if not set(updated_props.keys()) <= Activity.editable_fields:
            raise ValueError("Bad arguments to revise method")

        new = {
            field: updated_props.get(field, getattr(self, field))
            for field in Activity.editable_fields
        }
        old = {
            field: getattr(self, field)
            for field in Activity.editable_fields
        }

        if new == old:
            app.logger.warning(
                "No changes detected for the activity",
                extra={"to_secondary_slack_channel": True},
            )
            return None

        with handle_activities_update(
                submitter=current_user,
                user=self.user,
                mission=self.mission,
                reception_time=revision_time,
                start_time=new["start_time"],
                end_time=new["end_time"],
                bypass_check=bypass_check,
        ):
            revision = ActivityVersion(
                activity=self,
                reception_time=revision_time,
                start_time=new["start_time"],
                end_time=new["end_time"],
                context=revision_context,
                version=(self.latest_version_number() or 0) + 1,
                submitter=current_user,
            )
            db.session.add(revision)

            for field, value in updated_props.items():
                setattr(self, field, value)

            self.last_update_time = revision_time
            db.session.add(self)

            return revision

    def dismiss(self, dismiss_time=None, context=None):
        from app.domain.log_activities import handle_activities_update

        if not dismiss_time:
            dismiss_time = datetime.now()

        with handle_activities_update(
                submitter=current_user,
                user=self.user,
                mission=self.mission,
                reception_time=dismiss_time,
                start_time=self.start_time,
                end_time=None,
                bypass_check=True,
                reopen_mission_if_needed=False,
        ):
            super().dismiss(dismiss_time, context)
            self.last_update_time = self.dismissed_at
Beispiel #5
0
class Employment(UserEventBaseModel, Dismissable):
    backref_base_name = "employments"

    is_primary = db.Column(db.Boolean, nullable=True)

    validation_time = db.Column(DateTimeStoredAsUTC, nullable=True)

    validation_status = enum_column(EmploymentRequestValidationStatus,
                                    nullable=False)

    start_date = db.Column(db.Date, nullable=False)
    end_date = db.Column(db.Date, nullable=True)

    company_id = db.Column(db.Integer,
                           db.ForeignKey("company.id"),
                           index=True,
                           nullable=False)
    company = db.relationship("Company", backref="employments")

    has_admin_rights = db.Column(db.Boolean, nullable=True)

    user_id = db.Column(db.Integer,
                        db.ForeignKey("user.id"),
                        index=True,
                        nullable=True)
    email = db.Column(db.String(255), nullable=True)
    invite_token = db.Column(db.String(255), nullable=True, unique=True)

    __table_args__ = (
        db.Constraint(name="only_one_current_primary_enrollment_per_user"),
        db.Constraint(name="no_simultaneous_enrollments_for_the_same_company"),
        db.Constraint(name="no_undefined_employment_type_for_user"),
    )

    def __repr__(self):
        return f"<Employment [{self.id}] of User {self.user_id} in Company {self.company_id}>"

    @property
    def is_not_rejected(self):
        return (self.validation_status !=
                EmploymentRequestValidationStatus.REJECTED)

    @property
    def is_acknowledged(self):
        return (self.validation_status
                == EmploymentRequestValidationStatus.APPROVED
                and not self.is_dismissed)

    def bind(self, user):
        self.user_id = user.id
        for email in self.invite_emails:
            email.user_id = user.id

    def validate_by(self, user, time=None, reject=False):
        if not self.user_id == user.id:
            raise AuthorizationError(
                "Actor is not authorized to review the employment")

        if (not self.validation_status
                == EmploymentRequestValidationStatus.PENDING
                or self.is_dismissed):
            raise InvalidResourceError(
                f"Employment is already {'validated' if self.is_acknowledged else 'dismissed' if self.is_dismissed else 'rejected'}"
            )

        self.validation_status = (EmploymentRequestValidationStatus.APPROVED
                                  if not reject else
                                  EmploymentRequestValidationStatus.REJECTED)
        self.validation_time = time if time else datetime.now()