diff --git a/dds_web/scheduled_tasks.py b/dds_web/scheduled_tasks.py index a6724c486..34d544e1b 100644 --- a/dds_web/scheduled_tasks.py +++ b/dds_web/scheduled_tasks.py @@ -1,24 +1,96 @@ import flask_apscheduler import flask -# Apscheduler +## Apscheduler scheduler = flask_apscheduler.APScheduler() -@scheduler.task("interval", id="available_to_expired", seconds=10, misfire_grace_time=1) -def change_status_to_expired(): - # print("Task: Change project status from Available to expired.", flush=True) - from dds_web.database import models +@scheduler.task("cron", id="available_to_expired", hour=0, minute=1, misfire_grace_time=3600) +# @scheduler.task("interval", id="available_to_expired", seconds=15, misfire_grace_time=1) +def set_available_to_expired(): + scheduler.app.logger.debug("Task: Checking for Expiring projects.") + import sqlalchemy - # from dds_web.utils import current_time, page_query + from dds_web import db + from dds_web.database import models + from dds_web.errors import DatabaseError + from dds_web.api.project import ProjectStatus + from dds_web.utils import current_time, page_query with scheduler.app.app_context(): - # scheduler.app.logger.debug("This means that the app context works!") - test = models.Project.query.all() - # print(test, flush=True) + expire = ProjectStatus() + + errors = {} + + try: + for unit in db.session.query(models.Unit).with_for_update().all(): + errors[unit.name] = {} + + days_in_expired = unit.days_in_expired + + for project in page_query( + db.session.query(models.Project) + .filter( + sqlalchemy.and_( + models.Project.is_active == 1, models.Project.unit_id == unit.id + ) + ) + .with_for_update() + ): + + if ( + project.current_status == "Available" + and project.current_deadline <= current_time() + ): + scheduler.app.logger.debug("Handling expiring project") + scheduler.app.logger.debug( + "Project: %s has status %s and expires on: %s", + project.id, + project.current_status, + project.current_deadline, + ) + new_status_row = expire.expire_project( + project=project, + current_time=current_time(), + deadline_in=days_in_expired, + ) + + project.project_statuses.append(new_status_row) - # for project in page_query( - # models.ProjectStatuses.query.filter(models.ProjectStatuses.deadline <= current_time()) - # ): + try: + db.session.commit() + scheduler.app.logger.debug( + "Project: %s has status Archived now!", project.public_id + ) + except ( + sqlalchemy.exc.OperationalError, + sqlalchemy.exc.SQLAlchemyError, + ) as err: + flask.current_app.logger.exception(err) + db.session.rollback() + errors[unit.name][project.public_id] = str(err) + continue + else: + scheduler.app.logger.debug( + "Nothing to do for Project: %s", project.public_id + ) + except (sqlalchemy.exc.OperationalError, sqlalchemy.exc.SQLAlchemyError) as err: + flask.current_app.logger.exception(err) + db.session.rollback() + raise DatabaseError( + message=str(err), + alt_message=f"Status update failed" + + ( + ": Database malfunction." + if isinstance(err, sqlalchemy.exc.OperationalError) + else "." + ), + ) from err - # flask.current_app.logger.debug("Project: %s - Expires: %s", project, project.expires) + for unit, projects in errors.items(): + if projects: + scheduler.app.logger.error( + f"Following projects of Unit '{unit}' encountered issues during expiration process:" + ) + for proj in errors[unit].keys(): + scheduler.app.logger.error(f"Error for project '{proj}': {errors[unit][proj]} ")