diff --git a/zou/app/blueprints/crud/entity.py b/zou/app/blueprints/crud/entity.py index b420049893..464633d537 100644 --- a/zou/app/blueprints/crud/entity.py +++ b/zou/app/blueprints/crud/entity.py @@ -11,6 +11,7 @@ EntityLink, EntityConceptLink, ) +from zou.app.models.project import Project from zou.app.models.subscription import Subscription from zou.app.services import ( assets_service, @@ -23,7 +24,7 @@ user_service, concepts_service, ) -from zou.app.utils import events, date_helpers +from zou.app.utils import date_helpers, events, permissions from werkzeug.exceptions import NotFound @@ -56,6 +57,16 @@ def check_create_permissions(self, entity): def emit_create_event(self, entity_dict): self.emit_event("new", entity_dict) + def check_read_permissions(self): + return not permissions.has_vendor_permissions() + + def add_project_permission_filter(self, query): + if not permissions.has_admin_permissions(): + query = query.join(Project).filter( + user_service.build_related_projects_filter() + ) + return query + def update_data(self, data): data = super().update_data(data) data["created_by"] = persons_service.get_current_user()["id"] diff --git a/zou/app/blueprints/crud/task.py b/zou/app/blueprints/crud/task.py index 59db9b53f3..41780f965d 100644 --- a/zou/app/blueprints/crud/task.py +++ b/zou/app/blueprints/crud/task.py @@ -1,8 +1,11 @@ from flask import request, current_app from flask_jwt_extended import jwt_required +from sqlalchemy.orm import aliased from sqlalchemy.exc import IntegrityError + from zou.app.mixin import ArgsMixin +from zou.app.models.entity import Entity from zou.app.models.person import Person from zou.app.models.project import Project from zou.app.models.task import Task @@ -21,7 +24,7 @@ from zou.app.blueprints.crud.base import BaseModelsResource, BaseModelResource -class TasksResource(BaseModelsResource): +class TasksResource(BaseModelsResource, ArgsMixin): def __init__(self): BaseModelsResource.__init__(self, Task) @@ -37,6 +40,46 @@ def add_project_permission_filter(self, query): ) return query + def build_filters(self, options): + ( + many_join_filter, + in_filter, + name_filter, + criterions, + ) = super().build_filters(options) + if "project_id" in criterions: + del criterions["project_id"] + if "episode_id" in criterions: + del criterions["episode_id"] + return ( + many_join_filter, + in_filter, + name_filter, + criterions, + ) + + def apply_filters(self, query, options): + query = super().apply_filters(query, options) + + project_id = options.get("project_id", None) + episode_id = options.get("episode_id", None) + if episode_id is not None: + Sequence = aliased(Entity) + query = ( + query + .join(Entity, Task.entity_id == Entity.id) + .join(Sequence, Entity.parent_id == Sequence.id) + .filter(Sequence.parent_id == episode_id) + ) + elif project_id is not None: + query = ( + query + .join(Entity, Task.entity_id == Entity.id) + .filter(Entity.project_id == project_id) + ) + + return query + def post(self): """ Create a task with data given in the request body. JSON format is diff --git a/zou/app/blueprints/previews/resources.py b/zou/app/blueprints/previews/resources.py index 0bb1738205..d8a525f3dd 100644 --- a/zou/app/blueprints/previews/resources.py +++ b/zou/app/blueprints/previews/resources.py @@ -76,6 +76,7 @@ "sbbkp", "svg", "swf", + "tvpp", "wav", "zip", ] diff --git a/zou/app/blueprints/shots/resources.py b/zou/app/blueprints/shots/resources.py index d130aea686..e55ee4c236 100644 --- a/zou/app/blueprints/shots/resources.py +++ b/zou/app/blueprints/shots/resources.py @@ -48,6 +48,52 @@ def get(self, shot_id): user_service.check_entity_access(shot["id"]) return shot + @jwt_required() + def put(self, shot_id): + """ + Update given shot. + --- + tags: + - Shots + parameters: + - in: path + name: shot_id + required: True + type: string + format: UUID + x-example: a24a6ea4-ce75-4665-a070-57453082c25 + - in: body + name: data + required: True + type: object + responses: + 200: + description: Update given shot + """ + shot = shots_service.get_shot(shot_id) + user_service.check_manager_project_access(shot["project_id"]) + data = request.json + if data is None: + raise ArgumentsException( + "Data are empty. Please verify that you sent JSON data and" + " that you set the right headers." + ) + for field in [ + "id", + "created_at", + "updated_at", + "instance_casting", + "project_id", + "entities_in", + "entities_out", + "type", + "shotgun_id", + "created_by" + ]: + data.pop(field, None) + + return shots_service.update_shot(shot_id, data) + @jwt_required() def delete(self, shot_id): """ diff --git a/zou/app/blueprints/tasks/resources.py b/zou/app/blueprints/tasks/resources.py index b81f9e9baf..de0c1b5d9b 100644 --- a/zou/app/blueprints/tasks/resources.py +++ b/zou/app/blueprints/tasks/resources.py @@ -1463,7 +1463,6 @@ class ProjectTasksResource(Resource, ArgsMixin): """ @jwt_required() - @permissions.require_admin def get(self, project_id): """ Retrieve all tasks related to given project. @@ -1478,13 +1477,37 @@ def get(self, project_id): type: string format: UUID x-example: a24a6ea4-ce75-4665-a070-57453082c25 + - in: query + name: page + required: False + type: integer + x-example: 1 + - in: query + name: task_type_id + required: False + type: string + format: UUID + x-example: a24a6ea4-ce75-4665-a070-57453082c25 + - in: query + name: episode_id + required: False + type: string + format: UUID + x-example: a24a6ea4-ce75-4665-a070-57453082c25 responses: 200: description: All tasks related to given project """ projects_service.get_project(project_id) page = self.get_page() - return tasks_service.get_tasks_for_project(project_id, page) + task_type_id = self.get_task_type_id() + episode_id = self.get_episode_id() + return tasks_service.get_tasks_for_project( + project_id, + page, + task_type_id=task_type_id, + episode_id=episode_id + ) class ProjectCommentsResource(Resource, ArgsMixin): diff --git a/zou/app/services/tasks_service.py b/zou/app/services/tasks_service.py index a4e8004c9f..83e88f5d9f 100644 --- a/zou/app/services/tasks_service.py +++ b/zou/app/services/tasks_service.py @@ -1698,13 +1698,38 @@ def get_time_spents_for_project(project_id, page=0): return query_utils.get_paginated_results(query, page) -def get_tasks_for_project(project_id, page=0): +def get_tasks_for_project( + project_id, + page=0, + task_type_id=None, + episode_id=None +): """ Return all tasks for given project. """ query = Task.query.filter(Task.project_id == project_id).order_by( Task.updated_at.desc() ) + if task_type_id is not None: + query = query.filter(Task.task_type_id == task_type_id) + if episode_id is not None: + Sequence = aliased(Entity, name="sequence") + query = ( + query + .join(Entity, Entity.id == Task.entity_id) + .join(Sequence, Sequence.id == Entity.parent_id) + .filter(Sequence.parent_id == episode_id) + ) + + if permissions.has_vendor_permissions(): + query = query.filter(user_service.build_assignee_filter()) + elif not permissions.has_admin_permissions(): + query = query.join(Project).filter( + user_service.build_related_projects_filter() + ) + return query + + return query_utils.get_paginated_results(query, page, relations=True) diff --git a/zou/app/utils/dbhelpers.py b/zou/app/utils/dbhelpers.py index 8d32ee7ef1..c5837f4e7f 100644 --- a/zou/app/utils/dbhelpers.py +++ b/zou/app/utils/dbhelpers.py @@ -1,4 +1,4 @@ -from sqlalchemy import create_engine +from sqlalchemy import create_engine, inspect from sqlalchemy_utils import database_exists, create_database from sqlalchemy.engine.url import URL from sqlalchemy.orm import close_all_sessions @@ -39,3 +39,15 @@ def drop_all(): db.session.flush() close_all_sessions() return db.drop_all() + + +def is_init(): + """ + Check if database is initialized. + """ + from zou.app import db + from zou.app.models.project_status import ProjectStatus + return ( + inspect(db.engine).has_table("person") + and db.session.query(ProjectStatus).count() == 2 + ) diff --git a/zou/cli.py b/zou/cli.py index 4fdbfe3d5f..8dc7f17491 100755 --- a/zou/cli.py +++ b/zou/cli.py @@ -47,6 +47,21 @@ def init_db(): print("Database and tables created.") +@cli.command() +def is_db_ready(): + """ + Return a message telling if the database wheter the database is + initiliazed or not." + """ + with app.app_context(): + is_init = dbhelpers.is_init() + if is_init: + print("Database is initiliazed.") + else: + print("Database is not initiliazed. " + "Run 'zou init-db' and 'init-data'.") + + @cli.command() @click.option("--message", default="") def migrate_db(message):