├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── main.yml │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .pre-commit-hooks.yaml ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.rst ├── RELEASE.md ├── pyproject.toml ├── requirements.txt ├── scripts └── release.sh ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── assets │ ├── __init__.py │ ├── test_asset_tasks.py │ ├── test_asset_types.py │ └── test_assets.py ├── auth │ ├── __init__.py │ ├── test_auth_route.py │ └── test_permission.py ├── base.py ├── breakdown │ ├── __init__.py │ └── test_routes.py ├── chats │ ├── __init__.py │ ├── test_chat_routes.py │ └── test_chat_service.py ├── edits │ ├── __init__.py │ ├── base.py │ ├── test_edit_tasks.py │ └── test_edits.py ├── events │ ├── __init__.py │ └── test_event_routes.py ├── export │ ├── __init__.py │ ├── test_assets_to_csv.py │ ├── test_casting_to_csv.py │ ├── test_edits_to_csv.py │ ├── test_persons_to_csv.py │ ├── test_playlist_to_csv.py │ ├── test_projects_to_csv.py │ ├── test_shots_to_csv.py │ ├── test_task_types_to_csv.py │ ├── test_tasks_to_csv.py │ └── test_time_spents_to_csv.py ├── files │ ├── __init__.py │ ├── test_preview_files.py │ ├── test_route_file_tree.py │ ├── test_route_output_files.py │ └── test_route_working_files.py ├── fixtures │ ├── csv │ │ ├── assets.csv │ │ ├── assets_broken_01.csv │ │ ├── assets_broken_02.csv │ │ ├── assets_broken_03.csv │ │ ├── assets_no_metadata.csv │ │ ├── assets_other_delimiter.csv │ │ ├── casting.csv │ │ ├── casting_02.csv │ │ ├── persons.csv │ │ ├── projects.csv │ │ ├── shots.csv │ │ ├── shots_no_metadata.csv │ │ └── tasks.csv │ ├── playlist.csv │ ├── plugins │ │ ├── __init__.py │ │ └── hello │ │ │ ├── __init__.py │ │ │ └── resources.py │ ├── shotgun │ │ ├── assets.json │ │ ├── episodes.json │ │ ├── persons.json │ │ ├── projectconnections.json │ │ ├── projects.json │ │ ├── scenes.json │ │ ├── sequences.json │ │ ├── shots.json │ │ ├── status.json │ │ ├── steps.json │ │ ├── tasks.json │ │ └── versions.json │ ├── thumbnails │ │ ├── sample.hdr │ │ ├── th01.png │ │ ├── th02.png │ │ ├── th03.png │ │ └── th04.jpg │ └── videos │ │ ├── test_preview_tiles.mp4 │ │ └── tile01.png ├── misc │ ├── __init__.py │ ├── test_base_model.py │ ├── test_commands.py │ ├── test_date.py │ ├── test_index.py │ ├── test_paginate.py │ ├── test_plugins.py │ ├── test_query.py │ └── test_special_chars.py ├── models │ ├── __init__.py │ ├── test_comments.py │ ├── test_custom_actions.py │ ├── test_department.py │ ├── test_entity.py │ ├── test_entity_type.py │ ├── test_file_status.py │ ├── test_metadata_descriptor.py │ ├── test_output_file.py │ ├── test_output_type.py │ ├── test_person.py │ ├── test_preview_background_file.py │ ├── test_preview_file.py │ ├── test_project.py │ ├── test_project_status.py │ ├── test_schedule_item.py │ ├── test_software.py │ ├── test_status_automation.py │ ├── test_task.py │ ├── test_task_status.py │ ├── test_task_type.py │ ├── test_time_spent.py │ └── test_working_file.py ├── news │ ├── __init__.py │ └── test_route_news.py ├── persons │ ├── __init__.py │ ├── test_department.py │ ├── test_desktop_login_logs.py │ ├── test_person_cache_invalidation.py │ └── test_time_spents.py ├── playlists │ ├── __init__.py │ ├── test_annotations.py │ └── test_playlists.py ├── projects │ ├── __init__.py │ ├── test_route_all_projects.py │ ├── test_route_metadata.py │ ├── test_route_open_projects.py │ ├── test_route_schedule_items.py │ └── test_route_sync.py ├── search │ ├── __init__.py │ └── test_search.py ├── services │ ├── __init__.py │ ├── test_assets_service.py │ ├── test_auth_service.py │ ├── test_base_service.py │ ├── test_breakdown_service.py │ ├── test_budget_service.py │ ├── test_comments_service.py │ ├── test_edits_service.py │ ├── test_emails_service.py │ ├── test_entity_service.py │ ├── test_events_service.py │ ├── test_file_tree_service.py │ ├── test_files_service.py │ ├── test_index_service.py │ ├── test_names_service.py │ ├── test_news_service.py │ ├── test_notifications_service.py │ ├── test_persons_service.py │ ├── test_playlists_service.py │ ├── test_preview_files_service.py │ ├── test_projects_service.py │ ├── test_scenes_service.py │ ├── test_schedule_service.py │ ├── test_shots_service.py │ ├── test_status_automation_service.py │ ├── test_sync_service.py │ ├── test_tasks_service.py │ ├── test_time_spents_service.py │ └── test_user_service.py ├── shots │ ├── __init__.py │ ├── test_asset_instances.py │ ├── test_breakdown.py │ ├── test_episode_stats.py │ ├── test_episode_tasks.py │ ├── test_episodes.py │ ├── test_scene_tasks.py │ ├── test_scenes.py │ ├── test_sequence_tasks.py │ ├── test_sequences.py │ ├── test_shot_tasks.py │ └── test_shots.py ├── source │ ├── __init__.py │ ├── csv │ │ ├── __init__.py │ │ ├── test_import_assets.py │ │ ├── test_import_casting.py │ │ ├── test_import_persons.csv │ │ ├── test_import_persons.py │ │ └── test_import_shots.py │ └── shotgun │ │ ├── __init__.py │ │ ├── base.py │ │ ├── test_shotgun_import_assets.py │ │ ├── test_shotgun_import_episode.py │ │ ├── test_shotgun_import_errors.py │ │ ├── test_shotgun_import_notes.py │ │ ├── test_shotgun_import_persons.py │ │ ├── test_shotgun_import_project.py │ │ ├── test_shotgun_import_scene.py │ │ ├── test_shotgun_import_sequence.py │ │ ├── test_shotgun_import_shot.py │ │ ├── test_shotgun_import_status.py │ │ ├── test_shotgun_import_steps.py │ │ ├── test_shotgun_import_tasks.py │ │ ├── test_shotgun_import_teams.py │ │ └── test_shotgun_import_versions.py ├── stores │ ├── test_auth_tokens_store.py │ └── test_file_store.py ├── sync │ ├── __init__.py │ └── test_create_from_import.py ├── tasks │ ├── __init__.py │ ├── test_publish.py │ ├── test_route_task_change.py │ ├── test_route_tasks.py │ ├── test_route_time_spent.py │ └── test_subscription.py ├── thumbnails │ ├── __init__.py │ └── test_route_thumbnail.py ├── tiles │ ├── __init__.py │ └── test_route_tiles.py ├── user │ ├── __init__.py │ └── test_route_context.py └── utils │ ├── __init__.py │ ├── preview01_tile.png │ ├── test_cache.py │ ├── test_events.py │ ├── test_movie.py │ ├── test_thumbnail.py │ ├── test_tile.py │ └── test_utils.py └── zou ├── __init__.py ├── app ├── __init__.py ├── api.py ├── blueprints │ ├── __init__.py │ ├── assets │ │ ├── __init__.py │ │ └── resources.py │ ├── auth │ │ ├── __init__.py │ │ └── resources.py │ ├── breakdown │ │ ├── __init__.py │ │ └── resources.py │ ├── chats │ │ ├── __init__.py │ │ └── resources.py │ ├── comments │ │ ├── __init__.py │ │ └── resources.py │ ├── concepts │ │ ├── __init__.py │ │ └── resources.py │ ├── crud │ │ ├── __init__.py │ │ ├── asset_instance.py │ │ ├── attachment_file.py │ │ ├── base.py │ │ ├── budget.py │ │ ├── budget_entry.py │ │ ├── chat.py │ │ ├── chat_message.py │ │ ├── comments.py │ │ ├── custom_action.py │ │ ├── day_off.py │ │ ├── department.py │ │ ├── entity.py │ │ ├── entity_link.py │ │ ├── entity_type.py │ │ ├── event.py │ │ ├── file_status.py │ │ ├── metadata_descriptor.py │ │ ├── milestone.py │ │ ├── news.py │ │ ├── notification.py │ │ ├── organisation.py │ │ ├── output_file.py │ │ ├── output_type.py │ │ ├── person.py │ │ ├── playlist.py │ │ ├── plugin.py │ │ ├── preview_background_file.py │ │ ├── preview_file.py │ │ ├── project.py │ │ ├── project_status.py │ │ ├── salary_scale.py │ │ ├── schedule_item.py │ │ ├── search_filter.py │ │ ├── search_filter_group.py │ │ ├── software.py │ │ ├── status_automation.py │ │ ├── studio.py │ │ ├── subscription.py │ │ ├── task.py │ │ ├── task_status.py │ │ ├── task_type.py │ │ ├── time_spent.py │ │ └── working_file.py │ ├── edits │ │ ├── __init__.py │ │ └── resources.py │ ├── entities │ │ ├── __init__.py │ │ └── resources.py │ ├── events │ │ ├── __init__.py │ │ └── resources.py │ ├── export │ │ ├── __init__.py │ │ └── csv │ │ │ ├── __init__.py │ │ │ ├── assets.py │ │ │ ├── base.py │ │ │ ├── casting.py │ │ │ ├── edits.py │ │ │ ├── persons.py │ │ │ ├── playlists.py │ │ │ ├── projects.py │ │ │ ├── shots.py │ │ │ ├── task_types.py │ │ │ ├── tasks.py │ │ │ └── time_spents.py │ ├── files │ │ ├── __init__.py │ │ └── resources.py │ ├── index │ │ ├── __init__.py │ │ └── resources.py │ ├── news │ │ ├── __init__.py │ │ └── resources.py │ ├── persons │ │ ├── __init__.py │ │ └── resources.py │ ├── playlists │ │ ├── __init__.py │ │ └── resources.py │ ├── previews │ │ ├── __init__.py │ │ └── resources.py │ ├── projects │ │ ├── __init__.py │ │ └── resources.py │ ├── search │ │ ├── __init__.py │ │ └── resources.py │ ├── shots │ │ ├── __init__.py │ │ └── resources.py │ ├── source │ │ ├── __init__.py │ │ ├── csv │ │ │ ├── __init__.py │ │ │ ├── assets.py │ │ │ ├── base.py │ │ │ ├── casting.py │ │ │ ├── edits.py │ │ │ ├── persons.py │ │ │ ├── shots.py │ │ │ └── task_type_estimations.py │ │ ├── kitsu.py │ │ ├── otio.py │ │ └── shotgun │ │ │ ├── __init__.py │ │ │ ├── assets.py │ │ │ ├── base.py │ │ │ ├── episode.py │ │ │ ├── exception.py │ │ │ ├── import_errors.py │ │ │ ├── notes.py │ │ │ ├── person.py │ │ │ ├── project.py │ │ │ ├── scene.py │ │ │ ├── sequence.py │ │ │ ├── shot.py │ │ │ ├── status.py │ │ │ ├── steps.py │ │ │ ├── tasks.py │ │ │ ├── team.py │ │ │ └── versions.py │ ├── tasks │ │ ├── __init__.py │ │ └── resources.py │ └── user │ │ ├── __init__.py │ │ └── resources.py ├── config.py ├── file_trees │ ├── default.json │ └── simple.json ├── indexer │ ├── __init__.py │ └── indexing.py ├── mixin.py ├── models │ ├── __init__.py │ ├── asset_instance.py │ ├── attachment_file.py │ ├── base.py │ ├── budget.py │ ├── budget_entry.py │ ├── build_job.py │ ├── chat.py │ ├── chat_message.py │ ├── comment.py │ ├── custom_action.py │ ├── data_import_error.py │ ├── day_off.py │ ├── department.py │ ├── desktop_login_log.py │ ├── entity.py │ ├── entity_type.py │ ├── event.py │ ├── file_status.py │ ├── login_log.py │ ├── metadata_descriptor.py │ ├── milestone.py │ ├── news.py │ ├── notification.py │ ├── organisation.py │ ├── output_file.py │ ├── output_type.py │ ├── person.py │ ├── playlist.py │ ├── plugin.py │ ├── preview_background_file.py │ ├── preview_file.py │ ├── project.py │ ├── project_status.py │ ├── salary_scale.py │ ├── schedule_item.py │ ├── search_filter.py │ ├── search_filter_group.py │ ├── serializer.py │ ├── software.py │ ├── status_automation.py │ ├── studio.py │ ├── subscription.py │ ├── task.py │ ├── task_status.py │ ├── task_type.py │ ├── time_spent.py │ └── working_file.py ├── services │ ├── __init__.py │ ├── assets_service.py │ ├── auth_service.py │ ├── backup_service.py │ ├── base_service.py │ ├── breakdown_service.py │ ├── budget_service.py │ ├── chats_service.py │ ├── comments_service.py │ ├── concepts_service.py │ ├── custom_actions_service.py │ ├── deletion_service.py │ ├── edits_service.py │ ├── emails_service.py │ ├── entities_service.py │ ├── events_service.py │ ├── exception.py │ ├── file_tree_service.py │ ├── files_service.py │ ├── index_service.py │ ├── names_service.py │ ├── news_service.py │ ├── notifications_service.py │ ├── persons_service.py │ ├── playlists_service.py │ ├── plugins_service.py │ ├── preview_files_service.py │ ├── projects_service.py │ ├── scenes_service.py │ ├── schedule_service.py │ ├── shots_service.py │ ├── stats_service.py │ ├── status_automations_service.py │ ├── sync_service.py │ ├── tasks_service.py │ ├── telemetry_services.py │ ├── time_spents_service.py │ └── user_service.py ├── stores │ ├── __init__.py │ ├── auth_tokens_store.py │ ├── file_store.py │ ├── publisher_store.py │ └── queue_store.py ├── swagger.py └── utils │ ├── __init__.py │ ├── api.py │ ├── auth.py │ ├── cache.py │ ├── chats.py │ ├── colors.py │ ├── commands.py │ ├── csv_utils.py │ ├── date_helpers.py │ ├── dbhelpers.py │ ├── emails.py │ ├── env.py │ ├── events.py │ ├── fido.py │ ├── fields.py │ ├── flask.py │ ├── fs.py │ ├── git.py │ ├── logs.py │ ├── monitoring.py │ ├── permissions.py │ ├── plugins.py │ ├── query.py │ ├── redis.py │ ├── remote_job.py │ ├── saml.py │ ├── shell.py │ ├── string.py │ └── thumbnail.py ├── cli.py ├── debug.py ├── event_stream.py ├── job_settings.py ├── migrations ├── README ├── __init__.py ├── alembic.ini ├── env.py ├── script.py.mako ├── utils │ ├── __init__.py │ └── base.py └── versions │ ├── 003be8a91001_add_start_and_end_dates_to_projects.py │ ├── 0596674df51d_add_department_mentions_to_comments.py │ ├── 05ac7e8caa21_remove_unique_constraint_for_taskstatus_.py │ ├── 05b7dc79a416_add_archived_fields_to_main_tables.py │ ├── 06552e22f9e7_add_comment_updated_by.py │ ├── 0cf5e0e035fa_drop_column_tasktype_for_shots.py │ ├── 0ec3762a745d_add_attachment_table.py │ ├── 0ef6416a507b_.py │ ├── 10cf267d95c9_fix_schedule_item.py │ ├── 16328eae4b5f_add_new_constraint_for_timespent.py │ ├── 16df47d76c64_add_some_indexes.py │ ├── 17ef8f7be758_disallow_null_choicetype.py │ ├── 1bb55759146f_add_table_studio.py │ ├── 1cb44194db49_add_file_size_field_to_preview_file.py │ ├── 1e150c2cea4d_add_nb_entities_out.py │ ├── 1e2d77a2f0c4_add_hd_by_default_column.py │ ├── 1fab8c420678_add_attachments_to_message_chats.py │ ├── 20a8ad264659_tasktypeassettypelink_.py │ ├── 20dfeb36142b_add_projecttaskstatuslink_roles_for_.py │ ├── 23122f290ca2_add_entity_chat_models.py │ ├── 269d41bfb73f_add_entity_entity_links.py │ ├── 2762a797f1f9_add_people_salary_information.py │ ├── 29df910f04a4_create_unique_constraint_project_id_.py │ ├── 29fe01a6c9eb_add_status_automation_field.py │ ├── 2adc020885fa_.py │ ├── 2baede80b111_add_entity_status_field.py │ ├── 306266361f4f_.py │ ├── 307edd8c639d_change_comment_updated_by_in_comment_.py │ ├── 328fd44c6347_add_entity_created_by.py │ ├── 32f134ff1201_add_is_shared_flag_to_filters.py │ ├── 346250b5304c_add_position_to_preview_files.py │ ├── 3476e147e632_add_acks_to_comments.py │ ├── 389cfb9de776_.py │ ├── 38baa9a23b3d_.py │ ├── 398150912a3f_validation_status_preview.py │ ├── 3b0d1321079e_.py │ ├── 3d5c93bafb9d_.py │ ├── 3e0538ddf80f_.py │ ├── 3fee3bd10f9d_.py │ ├── 4095103c7d01_add_is_clients_isolated_flag_to_projects.py │ ├── 40dea9555940_.py │ ├── 42ec83db6a01_change_person_preferred_two_factor_.py │ ├── 43d0cf0ed5e7_.py │ ├── 443d1e78a932_.py │ ├── 45c2de366e66_.py │ ├── 45dafbb3f4e1_for_person_contract_type_disallow_null.py │ ├── 45f739ef962a_add_people_salary_scale_table.py │ ├── 4715c2586036_add_last_preview_file_fields.py │ ├── 4aab1f84ad72_introduce_plugin_table.py │ ├── 4e3738cdc34c_.py │ ├── 4f2398ebcd49_.py │ ├── 523ee9647bee_.py │ ├── 539a3a00c417_for_departmentlink_index_person_id_.py │ ├── 54ee0d1d60ba_add_build_job_model.py │ ├── 556526e47daa_.py │ ├── 57222395f2be_add_statusautomation_import_last_revision.py │ ├── 5798d2c9020b_change_person_role_to_choicetype.py │ ├── 590aa1ffe731_add_notifications_enabled_flag.py │ ├── 59a7445a966c_add_entity_is_shared.py │ ├── 5a291251823c_add_max_retake_parameter.py │ ├── 5ab9d7a75887_.py │ ├── 5b0fcbb94f24_.py │ ├── 5b7fa3e51701_add_is_client_allowed_flag_to_task_.py │ ├── 5b980f0dc365_add_comment_links.py │ ├── 5b9fd9ddfe43_add_homepage_and_contract_fields.py │ ├── 5c0498e264bc_add_slack_fields.py │ ├── 5e2ce62632a6_add_workflow_to_project.py │ ├── 680c64565f9d_for_searchfiltergroup_is_shared.py │ ├── 693cc511d28d_add_taskstatus_priority.py │ ├── 6aa446ee4072_add_is_for_all_flage_to_playlists.py │ ├── 6bd3b102d61b_.py │ ├── 6c597e842afa_add_task_type_field_to_playlists.py │ ├── 6d1b2c60f58b_add_milestone_model.py │ ├── 6d7fa5a8e9a5_add_timesheets_locked_field_to_org.py │ ├── 6eeaff945706_add_data_field_on_entity_links.py │ ├── 6f6049877105_.py │ ├── 7417c8eb70d8_add_for_entity_field_to_playlists.py │ ├── 772a5e43f05b_.py │ ├── 7748d3d22925_add_columns_to_searchfilter_table_to_.py │ ├── 77d6820f494f_add_reply_to_notif.py │ ├── 7a16258f2fab_add_currency_field_to_budgets.py │ ├── 7b1f765677d8_.py │ ├── 7bc746997e8d_add_slack_token_field_to_organisation.py │ ├── 7dc79d4ed7cd_.py │ ├── 818f7bda2528_.py │ ├── 82e7f7a95e84_add_project_id_to_events.py │ ├── 82ee682204ab_add_is_generated_from_ldap_column_to_.py │ ├── 83e2f33a9b14_add_project_bugdet_table.py │ ├── 8588f254d6b8_add_archived_fields.py │ ├── 8739ae9fa28b_add_for_client_field.py │ ├── 87efceb6745b_add_ready_for_to_entity.py │ ├── 892b264937ec_.py │ ├── 8a1b4a1b7f4a_add_totp_columns_for_person.py │ ├── 8ab98c178903_add_budget_entry_table.py │ ├── 8e4f39e321f4_add_day_off_table.py │ ├── 8e67c183bed7_add_preference_fields.py │ ├── 8fbd40afbe5f_allow_to_set_float_values_for_.py │ ├── 9010a64e5a2d_add_indices.py │ ├── 9060dd4f6116_notification_uc.py │ ├── 925771029620_.py │ ├── 92b40d79ad3f_allow_message_attachments.py │ ├── 92bdfe07e5f5_discord_integration.py │ ├── 956659992419_add_columns_for_person_to_store_fido_.py │ ├── 96c79d31e648_add_mail_otp_columns_for_person.py │ ├── 96f58a4a2a58_person_partial_index_for_email_only_.py │ ├── 971dbf5a0faf_add_short_name_for_asset_type_entity_.py │ ├── 98c90621cf58_add_for_client_flag_to_playlist.py │ ├── 99825b9cc778_.py │ ├── 9a09467f9b2c_.py │ ├── 9b85c14fa8a7_add_day_off_new_columns.py │ ├── 9bd17364fc18_.py │ ├── 9d3bb33c6fc6_add_department_keys_to_filter_models.py │ ├── 9e5b3a9b0cee_add_new_column_metadatadescriptor_data_type_.py │ ├── 9f8445f9b42c_add_man_days_fields.py │ ├── a23682ccc1f1_.py │ ├── a252a094e977_add_descriptions_for_entities_tasks_and_.py │ ├── a519c710877c_.py │ ├── a65bdadbae2f_.py │ ├── a66508788c53_add_nb_assets_ready.py │ ├── a6c25eed3ea1_add_login_failed_attemps_and_last_login_.py │ ├── a7c43f3fbc76_add_duration_column_to_the_preview_file.py │ ├── aa0a60033106_feedback_request.py │ ├── addbad59c706_allow_to_manage_drawings_instead_of_.py │ ├── addbbefa7028_add_departments_link_to_metadata_.py │ ├── ae0127f2fc56_add_previewfile_width_and_previewfile_.py │ ├── af1790868e2c_.py │ ├── b45cb782bb9c_.py │ ├── b4dd0add5f79_.py │ ├── b8c0a0f9d054_drop_task_status_is_reviewable.py │ ├── b8ed0fb263f8_add_person_jti_and_jti_expiration_date.py │ ├── b97a71306fc8_add_is_casting_standby_column_to_entity.py │ ├── be56dc0fb760_for_is_shared_disallow_nullable.py │ ├── bf1347acdee2_.py │ ├── c49e41f1298b_add_previewbackground.py │ ├── c68c2a62cfac_add_mimetype_column_to_attachment.py │ ├── c726b98be194_.py │ ├── c81f3e83bdb5_.py │ ├── ca28796a2a62_add_is_done_field_to_the_task_model.py │ ├── cf3d365de164_add_entity_version_model.py │ ├── cf6cec6d6bf5_add_status_field_to_preview_file.py │ ├── d25118cddcaa_modify_salary_scale_model.py │ ├── d80267806131_task_status_new_column_is_default.py │ ├── d80f02824047_add_plugin_revision.py │ ├── d8dcd5196d57_add_casting_label.py │ ├── de8a3de227ef_.py │ ├── deeacd38d373_for_projecttaskstatuslink_set_default_.py │ ├── df1834485f57_.py │ ├── df9f8a147e80_change_file_size_to_big_integer.py │ ├── e1ef93f40d3d_.py │ ├── e29638428dfd_add_schedule_item_table.py │ ├── e3f6db74cc1e_.py │ ├── e7e633bd6fa2_add_exceptions_to_budget_entries.py │ ├── e839d6603c09_add_person_id_to_shot_history.py │ ├── ee2373fbe3a4_.py │ ├── f0567e8d0c62_.py │ ├── f0c6cbb61869_add_production_style_field.py │ ├── f344b867a911_for_description_of_entity_task_working_.py │ ├── f4ff5a73d283_add_person_ldap_uid.py │ ├── f5b113876a49_add_preferred_two_factor_authentication_.py │ ├── f5bdca075cdc_add_preview_download_flag_to_projects.py │ ├── f874ad5e898a_add_link_entity_type_task_type.py │ ├── f995b28fb749_.py │ ├── fb6b6f188497_.py │ ├── fb87feaaa094_add_missing_unique_constraints.py │ ├── fba149993140_add_missing_index.py │ ├── fc322f908695_.py │ ├── fee7c696166e_.py │ ├── feffd3c5b806_introduce_concepts.py │ └── ffeed4956ab1_add_more_details_to_projects.py ├── plugin_template ├── __init__.py ├── logo.png ├── manifest.toml ├── migrations │ ├── alembic.ini │ ├── env.py │ └── script.py.mako ├── models.py └── routes.py ├── remote ├── __init__.py ├── config_payload.py ├── normalize_movie.py └── playlist.py └── utils ├── __init__.py └── movie.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: EvanBldy 7 | 8 | --- 9 | 10 | **Context** 11 | 12 | Studio name: 13 | Zou version: 14 | Zou installation type: (for example: hosted by CGWire, self-hosted, docker etc) 15 | 16 | **Describe the bug** 17 | A clear and concise description of what the bug is. 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: EvanBldy 7 | 8 | --- 9 | 10 | Please use https://cgwire.canny.io for any big feature request. 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Problem** 2 | 3 | 4 | **Solution** 5 | 6 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build package and deploy on PyPi 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-n-publish: 7 | name: Build and publish Python 🐍 distributions 📦 to PyPI 8 | runs-on: ubuntu-latest 9 | permissions: 10 | id-token: write 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Set up Python 14 | uses: actions/setup-python@v5 15 | with: 16 | python-version: "3.x" 17 | - name: Install pypa/build 18 | run: >- 19 | python3 -m 20 | pip install 21 | build 22 | --user 23 | - name: Build a binary wheel 24 | run: >- 25 | python3 -m 26 | build 27 | - name: Publish distribution 📦 to PyPI 28 | if: startsWith(github.ref, 'refs/tags') 29 | uses: pypa/gh-action-pypi-publish@release/v1 30 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: end-of-file-fixer 6 | - id: trailing-whitespace 7 | - repo: https://github.com/psf/black 8 | rev: 25.1.0 9 | hooks: 10 | - id: black 11 | - repo: https://github.com/PyCQA/autoflake 12 | rev: v2.3.1 13 | hooks: 14 | - id: autoflake 15 | args: 16 | [--remove-all-unused-imports, --remove-unused-variables, --in-place] 17 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: black 2 | name: black 3 | description: "Black: The uncompromising Python code formatter" 4 | entry: black 5 | language: python 6 | minimum_pre_commit_version: 3.8.0 7 | require_serial: true 8 | types_or: [python, pyi] 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[python]": { 3 | "editor.defaultFormatter": "ms-python.black-formatter" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # How to create a new release for Zou 2 | 3 | We release Zou versions through Github Actions on Pypi. Every time a new version is ready, we 4 | follow this process: 5 | 6 | 1. Rebase on the main branch. 7 | 2. Up the version number located the `zou/__version__` file. 8 | 3. Push changes to `main` branch. 9 | 4. Tag the commit and push the changes to Github. 10 | 5. Github Actions will build the package from the sources and publish the package on Pypi 11 | 12 | You can run a script to perform these commands at once, he is located in scripts/release.sh. 13 | 14 | # Deployment 15 | 16 | Please see the Zou documentation for the update instructions. 17 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 79 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -e .[dev,test] 2 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | git pull --rebase origin main 3 | last_release_number=$(python -c "from zou import __version__; print(__version__)") 4 | release_number=$(echo ${last_release_number} | awk -F. -v OFS=. '{$NF += 1 ; print}') 5 | echo "__version__ = \"$release_number\"" > zou/__init__.py 6 | git commit zou/__init__.py -m $release_number 7 | git tag v$release_number 8 | git push origin main --follow-tags 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup(python_requires=">=3.10, <3.14") 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/__init__.py -------------------------------------------------------------------------------- /tests/assets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/assets/__init__.py -------------------------------------------------------------------------------- /tests/auth/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/auth/__init__.py -------------------------------------------------------------------------------- /tests/breakdown/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/breakdown/__init__.py -------------------------------------------------------------------------------- /tests/chats/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/chats/__init__.py -------------------------------------------------------------------------------- /tests/edits/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/edits/__init__.py -------------------------------------------------------------------------------- /tests/events/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/events/__init__.py -------------------------------------------------------------------------------- /tests/export/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/export/__init__.py -------------------------------------------------------------------------------- /tests/export/test_persons_to_csv.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class PersonsCsvExportTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(PersonsCsvExportTestCase, self).setUp() 7 | 8 | self.generate_fixture_person() 9 | 10 | def test_export(self): 11 | csv_persons = self.get_raw("/export/csv/persons.csv") 12 | expected_result = """Last Name;First Name;Email;Phone;Role;Contract Type;Active\r 13 | Did;John;john.did@gmail.com;;admin;open-ended;yes\r 14 | Doe;John;john.doe@gmail.com;;user;open-ended;yes\r\n""" 15 | self.assertEqual(csv_persons, expected_result) 16 | -------------------------------------------------------------------------------- /tests/export/test_playlist_to_csv.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class PlaylistCsvExportTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(PlaylistCsvExportTestCase, self).setUp() 7 | self.generate_fixture_project_status() 8 | self.generate_fixture_project() 9 | self.generate_fixture_asset_type() 10 | self.generate_fixture_episode("E01") 11 | self.project_id = str(self.project.id) 12 | self.serialized_episode = self.episode.serialize(obj_type="Episode") 13 | self.episode_id = str(self.episode.id) 14 | self.generate_fixture_sequence("SE01") 15 | self.serialized_sequence = self.sequence.serialize(obj_type="Sequence") 16 | self.generate_fixture_shot("SE01") 17 | self.generate_fixture_shot("SE02") 18 | self.generate_fixture_shot("SE03") 19 | self.generate_fixture_playlist("Playlist 1") 20 | 21 | def test_get_playlist(self): 22 | csv_playlist = self.get_raw( 23 | "/export/csv/playlists/%s" % self.playlist.id 24 | ) 25 | expected_result = "Playlist;Cosmos Landromat | for shots;Playlist 1;" 26 | self.assertTrue(csv_playlist.startswith(expected_result)) 27 | -------------------------------------------------------------------------------- /tests/export/test_projects_to_csv.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class OutputFileTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(OutputFileTestCase, self).setUp() 7 | 8 | self.generate_fixture_project_status() 9 | self.generate_fixture_project() 10 | 11 | def test_export(self): 12 | csv_projects = self.get_raw("/export/csv/projects.csv") 13 | expected_result = """Name;Status\r 14 | Cosmos Landromat;Open\r\n""" 15 | self.assertEqual(csv_projects, expected_result) 16 | -------------------------------------------------------------------------------- /tests/export/test_task_types_to_csv.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class TasksCsvExportTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(TasksCsvExportTestCase, self).setUp() 7 | 8 | self.generate_fixture_project_status() 9 | self.generate_fixture_project() 10 | self.generate_fixture_asset_type() 11 | self.generate_fixture_department() 12 | self.generate_fixture_task_type() 13 | 14 | def test_export(self): 15 | csv_task_types = self.get_raw("export/csv/task-types.csv") 16 | expected_result = """Department;Name\r 17 | Animation;Animation\r 18 | Animation;Layout\r 19 | Modeling;Concept\r 20 | Modeling;Modeling\r 21 | Modeling;Shaders\r 22 | """ 23 | self.assertEqual(csv_task_types, expected_result) 24 | -------------------------------------------------------------------------------- /tests/export/test_tasks_to_csv.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class TasksCsvExportTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(TasksCsvExportTestCase, self).setUp() 7 | 8 | self.generate_fixture_project_status() 9 | self.generate_fixture_project() 10 | self.generate_fixture_asset_type() 11 | self.generate_fixture_department() 12 | self.generate_fixture_task_type() 13 | self.generate_fixture_task_status() 14 | self.generate_fixture_asset() 15 | self.generate_fixture_sequence() 16 | self.generate_fixture_shot() 17 | self.generate_fixture_person() 18 | self.generate_fixture_assigner() 19 | self.generate_fixture_task() 20 | self.maxDiff = None 21 | 22 | def test_export(self): 23 | csv_tasks = self.get_raw("/export/csv/tasks.csv") 24 | expected_result = """Project;Task Type;Episode;Sequence;Entity Type;Entity;Assigner;Assignees;Duration;Estimation;Start date;Due date;WIP date;Validation date;Task Status\r 25 | Cosmos Landromat;Shaders;;;Props;Tree;Ema Peel;John Doe;50.0;40.0;2017-02-20;2017-02-28;2017-02-22;;Open\r 26 | """ 27 | self.assertEqual(csv_tasks, expected_result) 28 | -------------------------------------------------------------------------------- /tests/files/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/files/__init__.py -------------------------------------------------------------------------------- /tests/files/test_preview_files.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class PreviewFilesTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(PreviewFilesTestCase, self).setUp() 7 | self.generate_base_context() 8 | self.generate_fixture_asset() 9 | self.generate_fixture_assigner() 10 | self.generate_fixture_person() 11 | self.generate_fixture_task() 12 | 13 | def test_get_running_preview_filless(self): 14 | self.generate_fixture_preview_file().serialize() 15 | preview_file_broken = self.generate_fixture_preview_file( 16 | revision=2, status="broken" 17 | ).serialize() 18 | preview_file_processing = self.generate_fixture_preview_file( 19 | revision=3, status="processing" 20 | ).serialize() 21 | preview_files = self.get("data/playlists/preview-files/running") 22 | self.assertEqual(len(preview_files), 2) 23 | self.assertEqual(preview_files[0]["id"], preview_file_processing["id"]) 24 | self.assertEqual(preview_files[1]["id"], preview_file_broken["id"]) 25 | -------------------------------------------------------------------------------- /tests/fixtures/csv/assets.csv: -------------------------------------------------------------------------------- 1 | Type,Name,Description,Contractor 2 | Prop,Cassette Player,Description 01,contractor 1 3 | Prop,Wood Stick,Description 02,contractor 2 4 | Character,Victor,Description 03,contractor 2 5 | -------------------------------------------------------------------------------- /tests/fixtures/csv/assets_broken_01.csv: -------------------------------------------------------------------------------- 1 | Type,Name,Description,Contractor 2 | Prop,Cassette Player,Description 01,contractor 1 3 | Prop,Wood Stick,Description 02,contractor 2 4 | 5 | Character,Victor,Description 03,contractor 2 6 | -------------------------------------------------------------------------------- /tests/fixtures/csv/assets_broken_02.csv: -------------------------------------------------------------------------------- 1 | Type,Name,Description,Contractor 2 | Prop,Cassette Player,Description 01,contractor 1 3 | Prop 4 | Character,Victor,Description 03,contractor 2 5 | -------------------------------------------------------------------------------- /tests/fixtures/csv/assets_broken_03.csv: -------------------------------------------------------------------------------- 1 | Type,Description,Contractor 2 | Prop,Description 01,contractor 1 3 | Prop,Description 02,contractor 2 4 | Character,Description 03,contractor 2 5 | -------------------------------------------------------------------------------- /tests/fixtures/csv/assets_no_metadata.csv: -------------------------------------------------------------------------------- 1 | Type,Name,Description 2 | Prop,Cassette Player,Description 01 3 | Prop,Wood Stick,Description 02 4 | Character,Victor,Description 03 5 | -------------------------------------------------------------------------------- /tests/fixtures/csv/assets_other_delimiter.csv: -------------------------------------------------------------------------------- 1 | Type;Name;Description;Contractor 2 | Prop;Cassette Player;Description 01;contractor 1 3 | Prop;Wood Stick;Description 02;contractor 2 4 | Character;Victor;Description 03;contractor 2 5 | -------------------------------------------------------------------------------- /tests/fixtures/csv/casting.csv: -------------------------------------------------------------------------------- 1 | Episode,Parent,Name,Asset Type,Asset,Occurences,Label 2 | MP,Environment,Lake,Props,Boat,1,setdress 3 | MP,Environment,Lake,Props,Pool,1,setdress 4 | ,Environment,Lake,Props,Flowers,12,setdress 5 | ,Environment,Mine,Props,Block,2,setdress 6 | ,Environment,Mine,Props,Wagon,3,setdress 7 | E01,SEQ01,SH01,Character,Victor,1,animate 8 | E01,SEQ01,SH01,Character,John,1,animate 9 | E01,SEQ02,SH01,Environment,Lake,1,layout 10 | E01,SEQ01,SH02,Character,Victor,1,animate 11 | E01,SEQ01,SH02,Environment,Mine,1,layout 12 | E02,SEQ02,SH01,Character,John,1,animate 13 | E02,SEQ02,SH01,Environment,Lake,1,layout 14 | E02,SEQ01,SH01,Character,Victor,1,animate 15 | E02,SEQ01,SH01,Environment,Mine,1,layout 16 | -------------------------------------------------------------------------------- /tests/fixtures/csv/casting_02.csv: -------------------------------------------------------------------------------- 1 | Episode,Parent,Name,Asset Type,Asset,Occurences,Label 2 | MP,Environment,Lake,Props,Boat,1,fixed 3 | MP,Environment,Lake,Props,Pool,1,fixed 4 | ,Environment,Lake,Props,Flowers,12,fixed 5 | ,Environment,Mine,Props,Block,2,fixed 6 | ,Environment,Mine,Props,Wagon,3,fixed 7 | E01,SEQ01,SH01,Character,Victor,1,fixed 8 | E01,SEQ01,SH01,Character,John,1,fixed 9 | E01,SEQ02,SH01,Environment,Lake,1,fixed 10 | E01,SEQ01,SH02,Character,Victor,1,fixed 11 | E01,SEQ01,SH02,Environment,Mine,1,fixed 12 | E02,SEQ02,SH01,Character,John,1,fixed 13 | E02,SEQ02,SH01,Environment,Lake,1,fixed 14 | E02,SEQ01,SH01,Character,Victor,1,fixed 15 | E02,SEQ01,SH01,Environment,Mine,1,fixed 16 | -------------------------------------------------------------------------------- /tests/fixtures/csv/persons.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Email,Phone 2 | John,Doe,john.doe@gmail.com,+33 6 08 08 08 08 3 | Ema,Doe,ema.doe@gmail.com, +33 6 08 08 08 09 4 | -------------------------------------------------------------------------------- /tests/fixtures/csv/projects.csv: -------------------------------------------------------------------------------- 1 | Name,Status 2 | Cosmos Landraumat,Open 3 | Caminandes: Lamigos,Open 4 | Big Buck Bunny,Closed 5 | -------------------------------------------------------------------------------- /tests/fixtures/csv/shots.csv: -------------------------------------------------------------------------------- 1 | Episode,Sequence,Name,Description,FPS,Frame In,Frame Out,Contractor 2 | E01,SE01,S01,Description 01,25,0,100,contractor 1 3 | E01,SE01,S02,Description 02,25,100,200,contractor 2 4 | E01,SE02,S01,Description 03,25,200,300,contractor 2 5 | E02,SE01,S01,Description 01,25,300,400,contractor 1 6 | -------------------------------------------------------------------------------- /tests/fixtures/csv/shots_no_metadata.csv: -------------------------------------------------------------------------------- 1 | Episode,Sequence,Name,Description,FPS,Frame In,Frame Out 2 | E01,SE01,S01,Description 01,25,0,100 3 | E01,SE01,S02,Description 02,25,100,200 4 | E01,SE02,S01,Description 03,25,200,300 5 | E02,SE01,S01,Description 01,25,300,400 6 | -------------------------------------------------------------------------------- /tests/fixtures/csv/tasks.csv: -------------------------------------------------------------------------------- 1 | Project,Department,Task Type,Episode,Sequence,Shot,Asset Type,Asset,Name,Assigner,Assignee,Duration,Estimation,Start Date,Due date,WIP date,Validation Date,Task Status 2 | Cosmos Landraumat,Animation,Facial,E01,SE01,S01,,,Master,John Doe,John Doe,40,30,2017-03-19,2017-03-20,2017-03-19,2017-03-21,WIP 3 | Cosmos Landraumat,Compositing,Compositing,E01,SE01,S01,,,Master,John Doe,,50,20,2017-03-19,,2017-03-19,2017-03-21,TODO 4 | Cosmos Landraumat,Modeling,Texture,,,,Prop,Cassette Player,Master,John Doe,,40,30,2017-03-19,2017-03-20,2017-03-19,2017-03-21,WIP 5 | -------------------------------------------------------------------------------- /tests/fixtures/playlist.csv: -------------------------------------------------------------------------------- 1 | Playlist;Les doudous | for shots;Animation Check;2021-02-02T10:09:44;;; 2 | ;;;;;; 3 | Entity name;Task Type;Revision;Task Status;Last comment author;Last comment date;Last comment 4 | SQ01 / SH01;Compositing;4;Todo;Frank Rousseau;2021-02-33T12:59:43; 5 | SQ01 / SH02;Animation;2;Todo;Frank Rousseau;2021-02-33T11:09:28; 6 | SQ01 / SH03;Animation;1;Todo;Frank Rousseau;2021-02-33T11:32:34; 7 | -------------------------------------------------------------------------------- /tests/fixtures/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/plugins/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/plugins/hello/__init__.py: -------------------------------------------------------------------------------- 1 | from .resources import HelloResource 2 | 3 | routes = [ 4 | ("/hello", HelloResource), 5 | ] 6 | 7 | name = __name__ 8 | -------------------------------------------------------------------------------- /tests/fixtures/plugins/hello/resources.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource 2 | 3 | 4 | class HelloResource(Resource): 5 | def get(self): 6 | return {"Hello": "world"} 7 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/assets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code": "Sheep", 4 | "description": "Black sheep", 5 | "project": { 6 | "type": "Project", 7 | "id": 1, 8 | "name": "Cosmos Landraumat" 9 | }, 10 | "sg_asset_type": "Character", 11 | "type": "Asset", 12 | "id": 1 13 | }, 14 | { 15 | "code": "Tom", 16 | "description": null, 17 | "project": { 18 | "type": "Project", 19 | "id": 1, 20 | "name": "Cosmos Landraumat" 21 | }, 22 | "sg_asset_type": "Character", 23 | "type": "Asset", 24 | "id": 2 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/episodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "project": { 4 | "type": "Project", 5 | "id": 1, 6 | "name": "Agent327" 7 | }, 8 | "code": "E01", 9 | "type": "Episode", 10 | "id": 1, 11 | "description": null 12 | }, 13 | { 14 | "project": { 15 | "type": "Project", 16 | "id": 1, 17 | "name": "Agent327" 18 | }, 19 | "code": "E02", 20 | "type": "Episode", 21 | "id": 2, 22 | "description": null 23 | }, 24 | { 25 | "project": { 26 | "type": "Project", 27 | "id": 1, 28 | "name": "Agent327" 29 | }, 30 | "code": "E03", 31 | "type": "Episode", 32 | "id": 3, 33 | "description": null 34 | } 35 | ] 36 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/persons.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "email": "johndoe@gmail.com", 4 | "firstname": "John", 5 | "lastname": "Doe", 6 | "id": 1, 7 | "login": "john.doe", 8 | "type": "HumanUser" 9 | }, 10 | { 11 | "email": "ema.peel@gmail.com", 12 | "firstname": "Ema", 13 | "lastname": "Peel", 14 | "id": 2, 15 | "login": "ema.peel", 16 | "type": "HumanUser" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/projectconnections.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "project": { 5 | "type": "Project", 6 | "id": 1, 7 | "name": "Agent327" 8 | }, 9 | "user": { 10 | "type": "HumanUser", 11 | "id": 1, 12 | "name": "Jhon Doe" 13 | }, 14 | "type": "ProjectUserConnection" 15 | }, 16 | { 17 | "id": 1, 18 | "project": { 19 | "type": "Project", 20 | "id": 1, 21 | "name": "Agent327" 22 | }, 23 | "user": { 24 | "type": "HumanUser", 25 | "id": 2, 26 | "name": "Jane Doe" 27 | }, 28 | "type": "ProjectUserConnection" 29 | }, 30 | { 31 | "id": 2, 32 | "project": { 33 | "type": "Project", 34 | "id": 2, 35 | "name": "Big Buck Bunny" 36 | }, 37 | "user": { 38 | "type": "HumanUser", 39 | "id": 1, 40 | "name": "Jhon Doe" 41 | }, 42 | "type": "ProjectUserConnection" 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/projects.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "name": "Agent327", 5 | "sg_status": "Active", 6 | "sg_fps": "25", 7 | "sg_width___height": "1920 / 1080", 8 | "type": "Project" 9 | }, 10 | { 11 | "id": 2, 12 | "name": "Big Buck Bunny", 13 | "sg_status": "On Hold", 14 | "type": "Project" 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/scenes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "sg_cut_in": null, 4 | "code": "SC01", 5 | "name": "SC01", 6 | "project": { 7 | "type": "Project", 8 | "id": 1, 9 | "name": "Agent327" 10 | }, 11 | "sequence_sg_scenes_1_sequences": [{ 12 | "type": "Sequence", 13 | "id": 1, 14 | "name": "S01" 15 | }], 16 | "sg_cut_duration": null, 17 | "type": "Scene", 18 | "id": 1 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/sequences.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "project": { 4 | "type": "Project", 5 | "id": 1, 6 | "name": "Agent327" 7 | }, 8 | "episode": { 9 | "type": "Episode", 10 | "id": 1, 11 | "name": "E01" 12 | }, 13 | "code": "S01", 14 | "type": "Sequence", 15 | "id": 1, 16 | "description": null 17 | }, 18 | { 19 | "project": { 20 | "type": "Project", 21 | "id": 1, 22 | "name": "Agent327" 23 | }, 24 | "code": "S02", 25 | "type": "Sequence", 26 | "id": 2, 27 | "description": null 28 | }, 29 | { 30 | "project": { 31 | "type": "Project", 32 | "id": 1, 33 | "name": "Agent327" 34 | }, 35 | "code": "S03", 36 | "type": "Sequence", 37 | "id": 3, 38 | "description": null 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/shots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "sg_cut_in": null, 4 | "code": "SH01", 5 | "assets": [ 6 | { 7 | "type": "Character", 8 | "id": 1, 9 | "name": "Black Sheep" 10 | } 11 | ], 12 | "project": { 13 | "type": "Project", 14 | "id": 1, 15 | "name": "Agent327" 16 | }, 17 | "sg_sequence": { 18 | "type": "Project", 19 | "id": 1, 20 | "name": "S01" 21 | }, 22 | "sg_cut_duration": null, 23 | "type": "Shot", 24 | "id": 1 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/steps.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code": "Modeling Shading", 4 | "color": "50,149,253", 5 | "id": 1, 6 | "type": "Step" 7 | }, 8 | { 9 | "code": "Animation", 10 | "color": "183,0,188", 11 | "id": 2, 12 | "type": "Step" 13 | }, 14 | { 15 | "code": "Layout", 16 | "color": "173,240,108", 17 | "id": 3, 18 | "type": "Step" 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /tests/fixtures/shotgun/versions.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "code": "S01_animation", 4 | "description": "First scene", 5 | "entity": { 6 | "id": 1, 7 | "name": "S01", 8 | "type": "Shot" 9 | }, 10 | "frame_range": null, 11 | "id": 1, 12 | "project": { 13 | "id": 1, 14 | "name": "Cosmos Landromat", 15 | "type": "Project" 16 | }, 17 | "sg_task": { 18 | "id": 1, 19 | "name": "Animation", 20 | "type": "Task" 21 | }, 22 | "sg_uploaded_movie": { 23 | "content_type": "video/avi", 24 | "id": 1, 25 | "link_type": "upload", 26 | "name": "movie.avi", 27 | "type": "Attachment", 28 | "url": "https://sg-media.amazonaws.com/movie.avi" 29 | }, 30 | "type": "Version" 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /tests/fixtures/thumbnails/sample.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/thumbnails/sample.hdr -------------------------------------------------------------------------------- /tests/fixtures/thumbnails/th01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/thumbnails/th01.png -------------------------------------------------------------------------------- /tests/fixtures/thumbnails/th02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/thumbnails/th02.png -------------------------------------------------------------------------------- /tests/fixtures/thumbnails/th03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/thumbnails/th03.png -------------------------------------------------------------------------------- /tests/fixtures/thumbnails/th04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/thumbnails/th04.jpg -------------------------------------------------------------------------------- /tests/fixtures/videos/test_preview_tiles.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/videos/test_preview_tiles.mp4 -------------------------------------------------------------------------------- /tests/fixtures/videos/tile01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/fixtures/videos/tile01.png -------------------------------------------------------------------------------- /tests/misc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/misc/__init__.py -------------------------------------------------------------------------------- /tests/misc/test_commands.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | from tests.base import ApiDBTestCase 4 | 5 | from zou.app.utils import commands 6 | from zou.app.stores import auth_tokens_store 7 | from zou.app.models.entity_type import EntityType 8 | from zou.app.models.task_type import TaskType 9 | 10 | 11 | def totimestamp(dt, epoch=datetime.datetime(1970, 1, 1)): 12 | td = dt - epoch 13 | return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6 14 | 15 | 16 | class CommandsTestCase(ApiDBTestCase): 17 | def setUp(self): 18 | super(CommandsTestCase, self).setUp() 19 | self.store = auth_tokens_store 20 | self.store.clear() 21 | 22 | def test_clean_auth_tokens_revoked(self): 23 | self.store.add("testkey", "false") 24 | self.store.add("testkey2", "false") 25 | self.assertEqual(len(self.store.keys()), 2) 26 | self.store.add("testkey2", "true") 27 | commands.clean_auth_tokens() 28 | self.assertEqual(len(self.store.keys()), 1) 29 | self.assertEqual(self.store.keys()[0], "testkey") 30 | 31 | def test_init_data(self): 32 | commands.init_data() 33 | task_types = TaskType.get_all() 34 | entity_types = EntityType.get_all() 35 | self.assertEqual(len(task_types), 12) 36 | self.assertEqual(len(entity_types), 8) 37 | -------------------------------------------------------------------------------- /tests/misc/test_date.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | from mixer.backend.flask import mixer 3 | from zou.app.utils import date_helpers 4 | 5 | 6 | class DateTestCase(ApiDBTestCase): 7 | def setUp(self): 8 | super(DateTestCase, self).setUp() 9 | mixer.init_app(self.flask_app) 10 | self.now = date_helpers.get_utc_now_datetime() 11 | self.generate_fixture_person() 12 | 13 | def test_create_date(self): 14 | self.assertIsNotNone(self.person.created_at) 15 | self.assertGreater(self.person.created_at, self.now) 16 | 17 | def test_update_date(self): 18 | self.person.last_name = "Doe" 19 | self.person.save() 20 | self.assertIsNotNone(self.person.updated_at) 21 | self.assertGreater(self.person.updated_at, self.person.created_at) 22 | -------------------------------------------------------------------------------- /tests/misc/test_index.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiTestCase 2 | 3 | from zou import __version__ 4 | from zou.app import app 5 | 6 | 7 | class VersionTestCase(ApiTestCase): 8 | def test_version_route(self): 9 | data = self.get("/") 10 | self.assertEqual( 11 | data, {"api": app.config["APP_NAME"], "version": __version__} 12 | ) 13 | 14 | def test_status_route(self): 15 | data = self.get("/status") 16 | self.assertTrue("database-up" in data) 17 | self.assertTrue("event-stream-up" in data) 18 | self.assertTrue("key-value-store-up" in data) 19 | -------------------------------------------------------------------------------- /tests/misc/test_paginate.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | from zou.app.models.person import Person 4 | 5 | 6 | class PaginationTestCase(ApiDBTestCase): 7 | def setUp(self): 8 | super(PaginationTestCase, self).setUp() 9 | self.generate_data(Person, 250, departments=[]) 10 | 11 | def test_paginate(self): 12 | persons = self.get("data/persons?page=1")["data"] 13 | self.assertEqual(len(persons), 100) 14 | persons = self.get("data/persons?page=2")["data"] 15 | self.assertEqual(len(persons), 100) 16 | persons = self.get("data/persons?page=3")["data"] 17 | self.assertEqual(len(persons), 51) 18 | 19 | def test_404(self): 20 | persons = self.get("data/persons?page=4")["data"] 21 | self.assertEqual(len(persons), 0) 22 | persons = self.get("data/persons?page=0")["data"] 23 | self.assertEqual(len(persons), 0) 24 | 25 | def test_metadata(self): 26 | pagination_infos = self.get("data/persons?page=2") 27 | self.assertEqual(pagination_infos["total"], 251) 28 | self.assertEqual(pagination_infos["nb_pages"], 3) 29 | self.assertEqual(pagination_infos["page"], 2) 30 | self.assertEqual(pagination_infos["offset"], 100) 31 | self.assertEqual(pagination_infos["limit"], 100) 32 | -------------------------------------------------------------------------------- /tests/misc/test_plugins.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from tests.base import ApiTestCase 3 | 4 | 5 | class PluginTestCase(ApiTestCase): 6 | """ 7 | def test__plugin_modules(self): 8 | plugins = api.load_plugin_modules("tests/fixtures/plugins") 9 | self.assertEqual(len(plugins), 1) 10 | 11 | plugin = plugins[0] 12 | self.assertTrue(hasattr(plugin, "routes")) 13 | """ 14 | 15 | """ 16 | def test_load_plugin(self): 17 | plugins = api.load_plugin_modules("tests/fixtures/plugins") 18 | plugin = plugins[0] 19 | api.load_plugin(app, plugin) 20 | self.get("/plugins/hello") 21 | """ 22 | -------------------------------------------------------------------------------- /tests/misc/test_special_chars.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from tests.base import ApiDBTestCase 3 | 4 | 5 | class SpecialCharTestCase(ApiDBTestCase): 6 | def setUp(self): 7 | super(SpecialCharTestCase, self).setUp() 8 | self.generate_fixture_project_status() 9 | self.generate_fixture_project() 10 | 11 | def test_repr(self): 12 | self.project.name = "Battle 360°" 13 | self.project.save() 14 | self.assertEqual(self.project.name, "Battle 360°") 15 | 16 | def test_get_special_char(self): 17 | self.project.name = "Battle 360°" 18 | self.project.save() 19 | projects = self.get("data/projects") 20 | self.assertEqual(projects[0]["name"], "Battle 360°") 21 | -------------------------------------------------------------------------------- /tests/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/models/__init__.py -------------------------------------------------------------------------------- /tests/news/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/news/__init__.py -------------------------------------------------------------------------------- /tests/persons/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/persons/__init__.py -------------------------------------------------------------------------------- /tests/persons/test_department.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | from zou.app.services import persons_service 4 | 5 | 6 | class PersonDepartmentTestCase(ApiDBTestCase): 7 | def setUp(self): 8 | super(PersonDepartmentTestCase, self).setUp() 9 | self.person = self.generate_fixture_person().serialize() 10 | self.department = self.generate_fixture_department().serialize() 11 | self.path = "/actions/persons/%s/departments" % self.person["id"] 12 | 13 | def test_add_to_department(self): 14 | person = self.person 15 | department = self.department 16 | persons_service.add_to_department(department["id"], person["id"]) 17 | self.post(self.path + "/add", {"department_id": department["id"]}) 18 | person = persons_service.get_person(person["id"]) 19 | self.assertEqual(person["departments"][0], department["id"]) 20 | 21 | def test_remove_from_department(self): 22 | person = self.person 23 | department = self.department 24 | persons_service.add_to_department(department["id"], person["id"]) 25 | self.delete(self.path + "/" + department["id"]) 26 | person = persons_service.get_person(person["id"]) 27 | self.assertEqual(len(person["departments"]), 0) 28 | -------------------------------------------------------------------------------- /tests/persons/test_person_cache_invalidation.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.base import ApiDBTestCase 4 | 5 | from zou.app.models.person import Person 6 | from zou.app.services import persons_service 7 | from zou.app.services.exception import PersonNotFoundException 8 | 9 | 10 | class PersonCacheInvalidationTestCase(ApiDBTestCase): 11 | def setUp(self): 12 | super(PersonCacheInvalidationTestCase, self).setUp() 13 | self.generate_fixture_person() 14 | 15 | def test_add_logs(self): 16 | person = persons_service.get_person_by_email("john.doe@gmail.com") 17 | person_raw = Person.get(person["id"]) 18 | person_raw.update({"email": "jhon.doe.bis@gmail.com"}) 19 | person = persons_service.get_person_by_email("john.doe@gmail.com") 20 | 21 | person = persons_service.update_person( 22 | person["id"], {"email": "john.doe2@gmail.com"} 23 | ) 24 | with pytest.raises(PersonNotFoundException): 25 | persons_service.get_person_by_email("john.doe@gmail.com") 26 | person = persons_service.get_person_by_email("john.doe2@gmail.com") 27 | -------------------------------------------------------------------------------- /tests/playlists/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/playlists/__init__.py -------------------------------------------------------------------------------- /tests/projects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/projects/__init__.py -------------------------------------------------------------------------------- /tests/projects/test_route_all_projects.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class OpenProjectRouteTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(OpenProjectRouteTestCase, self).setUp() 7 | 8 | self.generate_fixture_project_status() 9 | self.generate_fixture_project_closed_status() 10 | self.generate_fixture_project() 11 | self.generate_fixture_project_closed() 12 | 13 | def test_all_projects(self): 14 | projects = self.get("data/projects/all/") 15 | 16 | self.assertEqual(len(projects), 2) 17 | self.assertEqual(projects[0]["name"], self.project.name) 18 | self.assertEqual(projects[0]["project_status_name"], "Open") 19 | self.assertEqual(projects[1]["project_status_name"], "closed") 20 | 21 | def test_get_project_by_name(self): 22 | project = self.get_first( 23 | "data/projects/all?name=%s" % self.project.name.lower() 24 | ) 25 | self.assertEqual(project["id"], str(self.project.id)) 26 | -------------------------------------------------------------------------------- /tests/search/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/search/__init__.py -------------------------------------------------------------------------------- /tests/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/services/__init__.py -------------------------------------------------------------------------------- /tests/shots/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/shots/__init__.py -------------------------------------------------------------------------------- /tests/source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/source/__init__.py -------------------------------------------------------------------------------- /tests/source/csv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/source/csv/__init__.py -------------------------------------------------------------------------------- /tests/source/csv/test_import_persons.csv: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from test.base import ApiDBTestCase, TestHelpers 4 | 5 | from app.models.project import Project 6 | from app.models.project_status import ProjectStatus 7 | 8 | 9 | class ImportCsvProjectsTestCase(ApiDBTestCase, TestHelpers): 10 | 11 | def setUp(self): 12 | super(ImportCsvProjectsTestCase, self).setUp() 13 | 14 | def tearDown(self): 15 | super(ImportCsvProjectsTestCase, self).tearDown() 16 | 17 | def test_import_projects(self): 18 | path = "/data/import/csv/projects" 19 | 20 | file_path_fixture = self.get_fixture_file_path( 21 | os.path.join("csv", "projects.csv") 22 | ) 23 | self.upload_file(path, file_path_fixture) 24 | 25 | projects = Project.query.all() 26 | self.assertEqual(len(projects), 3) 27 | -------------------------------------------------------------------------------- /tests/source/csv/test_import_persons.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from tests.base import ApiDBTestCase 4 | 5 | from zou.app.models.person import Person 6 | 7 | 8 | class ImportCsvPersonsTestCase(ApiDBTestCase): 9 | def setUp(self): 10 | super(ImportCsvPersonsTestCase, self).setUp() 11 | 12 | def tearDown(self): 13 | super(ImportCsvPersonsTestCase, self).tearDown() 14 | 15 | def test_import_persons(self): 16 | path = "/import/csv/persons" 17 | 18 | file_path_fixture = self.get_fixture_file_path( 19 | os.path.join("csv", "persons.csv") 20 | ) 21 | self.upload_file(path, file_path_fixture) 22 | 23 | persons = Person.query.all() 24 | self.assertEqual(len(persons), 3) 25 | -------------------------------------------------------------------------------- /tests/source/shotgun/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/source/shotgun/__init__.py -------------------------------------------------------------------------------- /tests/source/shotgun/base.py: -------------------------------------------------------------------------------- 1 | import orjson as json 2 | 3 | from tests.base import ApiDBTestCase 4 | 5 | 6 | class ShotgunTestCase(ApiDBTestCase): 7 | def setUp(self): 8 | super(ShotgunTestCase, self).setUp() 9 | 10 | def load_fixture(self, data_type): 11 | file_path = "./tests/fixtures/shotgun/%s.json" % data_type 12 | api_path = "/import/shotgun/%s" % data_type 13 | data = json.loads(open(file_path).read()) 14 | return self.post(api_path, data, 200) 15 | -------------------------------------------------------------------------------- /tests/sync/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/sync/__init__.py -------------------------------------------------------------------------------- /tests/tasks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/tasks/__init__.py -------------------------------------------------------------------------------- /tests/tasks/test_subscription.py: -------------------------------------------------------------------------------- 1 | from tests.base import ApiDBTestCase 2 | 3 | 4 | class SubscriptionRouteTestCase(ApiDBTestCase): 5 | def setUp(self): 6 | super(SubscriptionRouteTestCase, self).setUp() 7 | 8 | self.generate_fixture_project_status() 9 | self.generate_fixture_project() 10 | self.generate_fixture_asset_type() 11 | self.generate_fixture_asset() 12 | self.generate_fixture_sequence() 13 | self.generate_fixture_shot() 14 | self.generate_fixture_department() 15 | self.generate_fixture_task_type() 16 | self.generate_fixture_task_status() 17 | self.generate_fixture_person() 18 | self.generate_fixture_assigner() 19 | self.generate_fixture_task_status_wip() 20 | self.user_id = str(self.user.id) 21 | -------------------------------------------------------------------------------- /tests/thumbnails/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/thumbnails/__init__.py -------------------------------------------------------------------------------- /tests/tiles/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/tiles/__init__.py -------------------------------------------------------------------------------- /tests/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/user/__init__.py -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/utils/__init__.py -------------------------------------------------------------------------------- /tests/utils/preview01_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/utils/preview01_tile.png -------------------------------------------------------------------------------- /tests/utils/test_cache.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from zou.app.utils import cache 4 | 5 | 6 | class CacheTestCase(unittest.TestCase): 7 | __name__ = "test_handler" 8 | 9 | def setUp(self): 10 | super(CacheTestCase, self).setUp() 11 | global called 12 | self.called = 0 13 | 14 | @cache.memoize_function(50) 15 | def memoized_function(self, parameter): 16 | self.called = self.called + 1 17 | 18 | @cache.memoize_function(50) 19 | def memoized_function2(self, parameter): 20 | import random 21 | 22 | return parameter + str(random.randrange(1, 50)) 23 | 24 | def test_memoize(self): 25 | result = self.memoized_function2("param1") 26 | result2 = self.memoized_function2("param1") 27 | result3 = self.memoized_function2("param2") 28 | 29 | self.assertEqual(result, result2) 30 | self.assertNotEqual(result, result3) 31 | -------------------------------------------------------------------------------- /tests/utils/test_tile.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/tests/utils/test_tile.py -------------------------------------------------------------------------------- /zou/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.20.52" 2 | -------------------------------------------------------------------------------- /zou/app/blueprints/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/blueprints/__init__.py -------------------------------------------------------------------------------- /zou/app/blueprints/auth/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from zou.app.utils.api import configure_api_from_blueprint 3 | 4 | from zou.app.blueprints.auth.resources import ( 5 | AuthenticatedResource, 6 | ChangePasswordResource, 7 | EmailOTPResource, 8 | FIDOResource, 9 | LoginResource, 10 | LogoutResource, 11 | RecoveryCodesResource, 12 | RefreshTokenResource, 13 | RegistrationResource, 14 | ResetPasswordResource, 15 | TOTPResource, 16 | SAMLSSOResource, 17 | SAMLLoginResource, 18 | ) 19 | 20 | routes = [ 21 | ("/auth/login", LoginResource), 22 | ("/auth/logout", LogoutResource), 23 | ("/auth/authenticated", AuthenticatedResource), 24 | ("/auth/register", RegistrationResource), 25 | ("/auth/change-password", ChangePasswordResource), 26 | ("/auth/reset-password", ResetPasswordResource), 27 | ("/auth/refresh-token", RefreshTokenResource), 28 | ("/auth/totp", TOTPResource), 29 | ("/auth/email-otp", EmailOTPResource), 30 | ("/auth/recovery-codes", RecoveryCodesResource), 31 | ("/auth/fido", FIDOResource), 32 | ("/auth/saml/sso", SAMLSSOResource), 33 | ("/auth/saml/login", SAMLLoginResource), 34 | ] 35 | 36 | blueprint = Blueprint("auth", "auth") 37 | api = configure_api_from_blueprint(blueprint, routes) 38 | -------------------------------------------------------------------------------- /zou/app/blueprints/chats/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | from zou.app.utils.api import configure_api_from_blueprint 4 | 5 | from zou.app.blueprints.chats.resources import ( 6 | ChatResource, 7 | ChatMessagesResource, 8 | ChatMessageResource, 9 | ) 10 | 11 | 12 | routes = [ 13 | ("/data/entities//chat", ChatResource), 14 | ("/data/entities//chat/messages", ChatMessagesResource), 15 | ( 16 | "/data/entities//chat/messages/", 17 | ChatMessageResource, 18 | ), 19 | ] 20 | 21 | blueprint = Blueprint("chats", "chats") 22 | api = configure_api_from_blueprint(blueprint, routes) 23 | -------------------------------------------------------------------------------- /zou/app/blueprints/concepts/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from zou.app.utils.api import configure_api_from_blueprint 3 | 4 | from zou.app.blueprints.concepts.resources import ( 5 | ConceptResource, 6 | AllConceptsResource, 7 | ConceptsAndTasksResource, 8 | ConceptPreviewsResource, 9 | ConceptTaskTypesResource, 10 | ConceptTasksResource, 11 | ProjectConceptsResource, 12 | ) 13 | 14 | routes = [ 15 | ("/data/concepts", AllConceptsResource), 16 | ("/data/concepts/with-tasks", ConceptsAndTasksResource), 17 | ("/data/concepts/", ConceptResource), 18 | ("/data/concepts//task-types", ConceptTaskTypesResource), 19 | ("/data/concepts//tasks", ConceptTasksResource), 20 | ("/data/concepts//preview-files", ConceptPreviewsResource), 21 | ("/data/projects//concepts", ProjectConceptsResource), 22 | ] 23 | 24 | 25 | blueprint = Blueprint("concepts", "concepts") 26 | api = configure_api_from_blueprint(blueprint, routes) 27 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/attachment_file.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.attachment_file import AttachmentFile 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | from zou.app.services import chats_service, tasks_service, user_service 6 | 7 | from zou.app.utils.permissions import PermissionDenied 8 | 9 | 10 | class AttachmentFilesResource(BaseModelsResource): 11 | def __init__(self): 12 | BaseModelsResource.__init__(self, AttachmentFile) 13 | 14 | 15 | class AttachmentFileResource(BaseModelResource): 16 | def __init__(self): 17 | BaseModelResource.__init__(self, AttachmentFile) 18 | 19 | def check_read_permissions(self, instance): 20 | attachment_file = instance 21 | if attachment_file["comment_id"] is not None: 22 | comment = tasks_service.get_comment(attachment_file["comment_id"]) 23 | user_service.check_task_access(comment["object_id"]) 24 | elif attachment_file["chat_message_id"] is not None: 25 | message = chats_service.get_chat_message( 26 | attachment_file["chat_message_id"] 27 | ) 28 | chat = chats_service.get_chat(message["chat_id"]) 29 | user_service.check_entity_access(chat["object_id"]) 30 | else: 31 | raise PermissionDenied() 32 | return True 33 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/budget.py: -------------------------------------------------------------------------------- 1 | from zou.app.blueprints.crud.base import BaseModelsResource, BaseModelResource 2 | 3 | from zou.app.models.salary_scale import Budget 4 | 5 | 6 | class BudgetsResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, Budget) 9 | 10 | 11 | class BudgetResource(BaseModelResource): 12 | protected_fields = [ 13 | "id", 14 | "created_at", 15 | "updated_at", 16 | "project_id", 17 | "revision", 18 | ] 19 | 20 | def __init__(self): 21 | BaseModelResource.__init__(self, Budget) 22 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/budget_entry.py: -------------------------------------------------------------------------------- 1 | from zou.app.blueprints.crud.base import BaseModelsResource, BaseModelResource 2 | 3 | from zou.app.models.salary_scale import BudgetEntry 4 | 5 | 6 | class BudgetEntriesResource(BaseModelsResource): 7 | 8 | def __init__(self): 9 | BaseModelsResource.__init__(self, BudgetEntry) 10 | 11 | 12 | class BudgetEntryResource(BaseModelResource): 13 | 14 | def __init__(self): 15 | BaseModelResource.__init__(self, BudgetEntry) 16 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/chat.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.chat import Chat 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class ChatsResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, Chat) 9 | 10 | 11 | class ChatResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, Chat) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/chat_message.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.chat_message import ChatMessage 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class ChatMessagesResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, ChatMessage) 9 | 10 | 11 | class ChatMessageResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, ChatMessage) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/custom_action.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.custom_action import CustomAction 2 | 3 | from zou.app.blueprints.crud.base import BaseModelsResource, BaseModelResource 4 | 5 | from zou.app.services import custom_actions_service, user_service 6 | 7 | 8 | class CustomActionsResource(BaseModelsResource): 9 | def __init__(self): 10 | BaseModelsResource.__init__(self, CustomAction) 11 | 12 | def check_read_permissions(self, options=None): 13 | user_service.block_access_to_vendor() 14 | return True 15 | 16 | def post_creation(self, custom_action): 17 | custom_actions_service.clear_custom_action_cache() 18 | return custom_action.serialize() 19 | 20 | 21 | class CustomActionResource(BaseModelResource): 22 | def __init__(self): 23 | BaseModelResource.__init__(self, CustomAction) 24 | 25 | def post_update(self, custom_action, data): 26 | custom_actions_service.clear_custom_action_cache() 27 | return custom_action 28 | 29 | def post_delete(self, custom_action): 30 | custom_actions_service.clear_custom_action_cache() 31 | return custom_action 32 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/department.py: -------------------------------------------------------------------------------- 1 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 2 | 3 | from zou.app.models.department import Department 4 | 5 | from zou.app.services import tasks_service 6 | 7 | 8 | class DepartmentsResource(BaseModelsResource): 9 | def __init__(self): 10 | BaseModelsResource.__init__(self, Department) 11 | 12 | def check_read_permissions(self, options=None): 13 | return True 14 | 15 | def post_creation(self, instance): 16 | tasks_service.clear_department_cache(str(instance.id)) 17 | return instance.serialize() 18 | 19 | 20 | class DepartmentResource(BaseModelResource): 21 | def __init__(self): 22 | BaseModelResource.__init__(self, Department) 23 | 24 | def check_read_permissions(self, instance): 25 | return True 26 | 27 | def post_update(self, instance_dict, data): 28 | tasks_service.clear_department_cache(instance_dict["id"]) 29 | return instance_dict 30 | 31 | def post_delete(self, instance_dict): 32 | tasks_service.clear_department_cache(instance_dict["id"]) 33 | return instance_dict 34 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/entity_link.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.entity import EntityLink 2 | from zou.app.utils import fields 3 | 4 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 5 | from zou.app.services.exception import ( 6 | EntityLinkNotFoundException, 7 | WrongParameterException, 8 | ) 9 | 10 | 11 | class EntityLinksResource(BaseModelsResource): 12 | def __init__(self): 13 | BaseModelsResource.__init__(self, EntityLink) 14 | 15 | 16 | class EntityLinkResource(BaseModelResource): 17 | def __init__(self): 18 | BaseModelResource.__init__(self, EntityLink) 19 | 20 | def get_model_or_404(self, instance_id): 21 | if not fields.is_valid_id(instance_id): 22 | raise WrongParameterException("Malformed ID.") 23 | instance = self.model.get_by(id=instance_id) 24 | if instance is None: 25 | raise EntityLinkNotFoundException 26 | return instance 27 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/event.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.event import ApiEvent 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class EventsResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, ApiEvent) 9 | 10 | def all_entries(self, query=None, relations=False): 11 | if query is None: 12 | query = self.model.query 13 | 14 | return self.serialize_list( 15 | query.limit(1000).all(), relations=relations 16 | ) 17 | 18 | 19 | class EventResource(BaseModelResource): 20 | def __init__(self): 21 | BaseModelResource.__init__(self, ApiEvent) 22 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/file_status.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.file_status import FileStatus 2 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 3 | 4 | 5 | class FileStatusesResource(BaseModelsResource): 6 | def __init__(self): 7 | BaseModelsResource.__init__(self, FileStatus) 8 | 9 | def check_read_permissions(self, options=None): 10 | return True 11 | 12 | 13 | class FileStatusResource(BaseModelResource): 14 | def __init__(self): 15 | BaseModelResource.__init__(self, FileStatus) 16 | 17 | def check_read_permissions(self, instance): 18 | return True 19 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/milestone.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.milestone import Milestone 2 | from zou.app.services import user_service 3 | 4 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 5 | 6 | 7 | class MilestonesResource(BaseModelsResource): 8 | def __init__(self): 9 | BaseModelsResource.__init__(self, Milestone) 10 | 11 | def check_create_permissions(self, milestone): 12 | user_service.check_manager_project_access(milestone["project_id"]) 13 | 14 | 15 | class MilestoneResource(BaseModelResource): 16 | def __init__(self): 17 | BaseModelResource.__init__(self, Milestone) 18 | 19 | def check_read_permissions(self, milestone): 20 | user_service.check_project_access(milestone["project_id"]) 21 | user_service.block_access_to_vendor() 22 | 23 | def check_update_permissions(self, milestone, data): 24 | user_service.check_manager_project_access(milestone["project_id"]) 25 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/news.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.news import News 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class NewssResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, News) 9 | 10 | 11 | class NewsResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, News) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/notification.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.notification import Notification 2 | from zou.app.utils import permissions 3 | 4 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 5 | 6 | 7 | class NotificationsResource(BaseModelsResource): 8 | def __init__(self): 9 | BaseModelsResource.__init__(self, Notification) 10 | 11 | def check_create_permissions(self, data): 12 | return permissions.check_admin_permissions() 13 | 14 | 15 | class NotificationResource(BaseModelResource): 16 | def __init__(self): 17 | BaseModelResource.__init__(self, Notification) 18 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/plugin.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.plugin import Plugin 2 | 3 | from zou.app.blueprints.crud.base import BaseModelsResource, BaseModelResource 4 | 5 | 6 | class PluginsResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, Plugin) 9 | 10 | 11 | class PluginResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, Plugin) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/project_status.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.project_status import ProjectStatus 2 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 3 | 4 | 5 | class ProjectStatussResource(BaseModelsResource): 6 | def __init__(self): 7 | BaseModelsResource.__init__(self, ProjectStatus) 8 | 9 | def check_read_permissions(self, options=None): 10 | return True 11 | 12 | 13 | class ProjectStatusResource(BaseModelResource): 14 | def __init__(self): 15 | BaseModelResource.__init__(self, ProjectStatus) 16 | 17 | def check_read_permissions(self, instance): 18 | return True 19 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/search_filter.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.search_filter import SearchFilter 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class SearchFiltersResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, SearchFilter) 9 | 10 | 11 | class SearchFilterResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, SearchFilter) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/search_filter_group.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.search_filter_group import SearchFilterGroup 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class SearchFilterGroupsResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, SearchFilterGroup) 9 | 10 | 11 | class SearchFilterGroupResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, SearchFilterGroup) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/studio.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.studio import Studio 2 | 3 | from zou.app.blueprints.crud.base import BaseModelsResource, BaseModelResource 4 | 5 | from zou.app.services import tasks_service 6 | 7 | 8 | class StudiosResource(BaseModelsResource): 9 | def __init__(self): 10 | BaseModelsResource.__init__(self, Studio) 11 | 12 | def check_read_permissions(self, options=None): 13 | return True 14 | 15 | def post_creation(self, instance): 16 | tasks_service.clear_studio_cache(str(instance.id)) 17 | return instance.serialize() 18 | 19 | 20 | class StudioResource(BaseModelResource): 21 | def __init__(self): 22 | BaseModelResource.__init__(self, Studio) 23 | 24 | def check_read_permissions(self, instance): 25 | return True 26 | 27 | def post_update(self, instance_dict, data): 28 | tasks_service.clear_studio_cache(instance_dict["id"]) 29 | return instance_dict 30 | 31 | def post_delete(self, instance_dict): 32 | tasks_service.clear_studio_cache(instance_dict["id"]) 33 | return instance_dict 34 | -------------------------------------------------------------------------------- /zou/app/blueprints/crud/subscription.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.subscription import Subscription 2 | 3 | from zou.app.blueprints.crud.base import BaseModelResource, BaseModelsResource 4 | 5 | 6 | class SubscriptionsResource(BaseModelsResource): 7 | def __init__(self): 8 | BaseModelsResource.__init__(self, Subscription) 9 | 10 | 11 | class SubscriptionResource(BaseModelResource): 12 | def __init__(self): 13 | BaseModelResource.__init__(self, Subscription) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/edits/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from zou.app.utils.api import configure_api_from_blueprint 3 | 4 | from zou.app.blueprints.edits.resources import ( 5 | EditResource, 6 | EditsResource, 7 | AllEditsResource, 8 | EditsAndTasksResource, 9 | EditPreviewsResource, 10 | EditTaskTypesResource, 11 | EditTasksResource, 12 | EditVersionsResource, 13 | ProjectEditsResource, 14 | EpisodeEditsResource, 15 | EpisodeEditTasksResource, 16 | ) 17 | 18 | routes = [ 19 | ("/data/edits", AllEditsResource), 20 | ("/data/edits/all", EditsResource), 21 | ("/data/edits/with-tasks", EditsAndTasksResource), 22 | ("/data/edits/", EditResource), 23 | ("/data/edits//task-types", EditTaskTypesResource), 24 | ("/data/edits//tasks", EditTasksResource), 25 | ("/data/edits//preview-files", EditPreviewsResource), 26 | ("/data/edits//versions", EditVersionsResource), 27 | ("/data/episodes//edits", EpisodeEditsResource), 28 | ("/data/episodes//edit-tasks", EpisodeEditTasksResource), 29 | ("/data/projects//edits", ProjectEditsResource), 30 | ] 31 | 32 | 33 | blueprint = Blueprint("edits", "edits") 34 | api = configure_api_from_blueprint(blueprint, routes) 35 | -------------------------------------------------------------------------------- /zou/app/blueprints/entities/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from zou.app.utils.api import configure_api_from_blueprint 3 | 4 | from zou.app.blueprints.entities.resources import ( 5 | EntityPreviewFilesResource, 6 | EntityNewsResource, 7 | EntityTimeSpentsResource, 8 | EntitiesLinkedWithTasksResource, 9 | ) 10 | 11 | 12 | routes = [ 13 | ("/data/entities//news", EntityNewsResource), 14 | ("/data/entities//preview-files", EntityPreviewFilesResource), 15 | ("/data/entities//time-spents", EntityTimeSpentsResource), 16 | ( 17 | "/data/entities//entities-linked/with-tasks", 18 | EntitiesLinkedWithTasksResource, 19 | ), 20 | ] 21 | 22 | blueprint = Blueprint("entities", "entities") 23 | api = configure_api_from_blueprint(blueprint, routes) 24 | -------------------------------------------------------------------------------- /zou/app/blueprints/events/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from zou.app.utils.api import configure_api_from_blueprint 3 | 4 | from zou.app.blueprints.events.resources import ( 5 | EventsResource, 6 | LoginLogsResource, 7 | ) 8 | 9 | routes = [ 10 | ("/data/events/last", EventsResource), 11 | ("/data/events/login-logs/last", LoginLogsResource), 12 | ] 13 | 14 | blueprint = Blueprint("events", "events") 15 | api = configure_api_from_blueprint(blueprint, routes) 16 | -------------------------------------------------------------------------------- /zou/app/blueprints/export/csv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/blueprints/export/csv/__init__.py -------------------------------------------------------------------------------- /zou/app/blueprints/export/csv/base.py: -------------------------------------------------------------------------------- 1 | from flask import abort 2 | from flask_jwt_extended import jwt_required 3 | 4 | from flask_restful import Resource 5 | from zou.app.utils import csv_utils, permissions 6 | 7 | 8 | class BaseCsvExport(Resource): 9 | def __init__(self): 10 | Resource.__init__(self) 11 | self.file_name = "export" 12 | 13 | def check_permissions(self): 14 | permissions.check_manager_permissions() 15 | return True 16 | 17 | def prepare_import(self): 18 | pass 19 | 20 | @jwt_required() 21 | def get(self): 22 | """ 23 | Export as csv. 24 | --- 25 | tags: 26 | - Export 27 | responses: 28 | 200: 29 | description: Exported as csv 30 | """ 31 | self.prepare_import() 32 | try: 33 | self.check_permissions() 34 | csv_content = [] 35 | csv_content.append(self.build_headers()) 36 | results = self.build_query().all() 37 | for result in results: 38 | csv_content.append(self.build_row(result)) 39 | except permissions.PermissionDenied: 40 | abort(403) 41 | 42 | return csv_utils.build_csv_response( 43 | csv_content, file_name=self.file_name 44 | ) 45 | -------------------------------------------------------------------------------- /zou/app/blueprints/export/csv/persons.py: -------------------------------------------------------------------------------- 1 | from zou.app.blueprints.export.csv.base import BaseCsvExport 2 | 3 | from zou.app.models.person import Person 4 | 5 | 6 | class PersonsCsvExport(BaseCsvExport): 7 | def __init__(self): 8 | BaseCsvExport.__init__(self) 9 | self.file_name = "people_export" 10 | 11 | def build_headers(self): 12 | return [ 13 | "Last Name", 14 | "First Name", 15 | "Email", 16 | "Phone", 17 | "Role", 18 | "Contract Type", 19 | "Active", 20 | ] 21 | 22 | def build_query(self): 23 | return Person.query.filter(Person.is_bot == False).order_by( 24 | Person.last_name, Person.first_name 25 | ) 26 | 27 | def build_row(self, person): 28 | active = "yes" 29 | if not person.active: 30 | active = "no" 31 | return [ 32 | person.last_name, 33 | person.first_name, 34 | person.email, 35 | person.phone, 36 | person.role.code, 37 | person.contract_type.code, 38 | active, 39 | ] 40 | -------------------------------------------------------------------------------- /zou/app/blueprints/export/csv/projects.py: -------------------------------------------------------------------------------- 1 | from zou.app.blueprints.export.csv.base import BaseCsvExport 2 | 3 | from zou.app.models.project_status import ProjectStatus 4 | from zou.app.models.project import Project 5 | 6 | 7 | class ProjectsCsvExport(BaseCsvExport): 8 | def __init__(self): 9 | BaseCsvExport.__init__(self) 10 | 11 | def build_headers(self): 12 | return ["Name", "Status"] 13 | 14 | def build_query(self): 15 | query = Project.query.join( 16 | ProjectStatus, Project.project_status_id == ProjectStatus.id 17 | ) 18 | query = query.add_columns(ProjectStatus.name) 19 | query = query.order_by(Project.name) 20 | return query 21 | 22 | def build_row(self, project_data): 23 | (project, project_status_name) = project_data 24 | return [project.name, project_status_name] 25 | -------------------------------------------------------------------------------- /zou/app/blueprints/export/csv/task_types.py: -------------------------------------------------------------------------------- 1 | from zou.app.blueprints.export.csv.base import BaseCsvExport 2 | 3 | from zou.app.models.department import Department 4 | from zou.app.models.task_type import TaskType 5 | 6 | 7 | class TaskTypesCsvExport(BaseCsvExport): 8 | def __init__(self): 9 | BaseCsvExport.__init__(self) 10 | 11 | self.name = "task_types_export" 12 | 13 | def build_headers(self): 14 | return ["Department", "Name"] 15 | 16 | def build_query(self): 17 | query = TaskType.query.order_by(Department.name, TaskType.name) 18 | query = query.join(Department, TaskType.department_id == Department.id) 19 | query = query.add_columns(Department.name) 20 | return query 21 | 22 | def build_row(self, task_type_row): 23 | (task_type, department_name) = task_type_row 24 | return [department_name, task_type.name] 25 | -------------------------------------------------------------------------------- /zou/app/blueprints/index/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module is named source instead of import because import is a Python 3 | keyword. 4 | """ 5 | 6 | from flask import Blueprint 7 | from zou.app.utils.api import configure_api_from_blueprint 8 | 9 | from zou.app.blueprints.index.resources import ( 10 | ConfigResource, 11 | IndexResource, 12 | InfluxStatusResource, 13 | StatusResource, 14 | StatusResourcesResource, 15 | StatsResource, 16 | TestEventsResource, 17 | TxtStatusResource, 18 | ) 19 | 20 | routes = [ 21 | ("/", IndexResource), 22 | ("/status", StatusResource), 23 | ("/status/influx", InfluxStatusResource), 24 | ("/status/resources", StatusResourcesResource), 25 | ("/status.txt", TxtStatusResource), 26 | ("/status/test-event", TestEventsResource), 27 | ("/stats", StatsResource), 28 | ("/config", ConfigResource), 29 | ] 30 | 31 | blueprint = Blueprint("index", "index") 32 | api = configure_api_from_blueprint(blueprint, routes) 33 | -------------------------------------------------------------------------------- /zou/app/blueprints/news/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | from zou.app.utils.api import configure_api_from_blueprint 3 | 4 | from zou.app.blueprints.news.resources import ( 5 | NewsResource, 6 | ProjectNewsResource, 7 | ProjectSingleNewsResource, 8 | ) 9 | 10 | 11 | routes = [ 12 | ("/data/projects/news", NewsResource), 13 | ("/data/projects//news", ProjectNewsResource), 14 | ("/data/projects//news/", ProjectSingleNewsResource), 15 | ] 16 | 17 | blueprint = Blueprint("news", "news") 18 | api = configure_api_from_blueprint(blueprint, routes) 19 | -------------------------------------------------------------------------------- /zou/app/blueprints/search/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility routes to run full text search on Kitsu data. 3 | """ 4 | 5 | from flask import Blueprint 6 | from zou.app.utils.api import configure_api_from_blueprint 7 | 8 | from zou.app.blueprints.search.resources import SearchResource 9 | 10 | routes = [("/data/search", SearchResource)] 11 | 12 | blueprint = Blueprint("search", "search") 13 | api = configure_api_from_blueprint(blueprint, routes) 14 | -------------------------------------------------------------------------------- /zou/app/blueprints/source/csv/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/blueprints/source/csv/__init__.py -------------------------------------------------------------------------------- /zou/app/blueprints/source/shotgun/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/blueprints/source/shotgun/__init__.py -------------------------------------------------------------------------------- /zou/app/blueprints/source/shotgun/exception.py: -------------------------------------------------------------------------------- 1 | class ShotgunEntryImportFailed(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /zou/app/indexer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/indexer/__init__.py -------------------------------------------------------------------------------- /zou/app/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/models/__init__.py -------------------------------------------------------------------------------- /zou/app/models/budget.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | from zou.app.utils import fields 7 | 8 | 9 | class Budget(db.Model, BaseMixin, SerializerMixin): 10 | """ 11 | Budget quote for a project. It's a base object where budget entries 12 | are linked to. 13 | """ 14 | 15 | project_id = db.Column( 16 | UUIDType(binary=False), db.ForeignKey("project.id"), index=True 17 | ) 18 | revision = db.Column(db.Integer, nullable=False, default=1) 19 | name = db.Column(db.String(255), nullable=False) 20 | currency = db.Column(db.String(3)) 21 | 22 | def __repr__(self): 23 | return "" % ( 24 | self.project_id, 25 | self.revision, 26 | self.name, 27 | self.id, 28 | ) 29 | 30 | def present(self): 31 | return fields.serialize_dict( 32 | { 33 | "id": self.id, 34 | "project_id": self.project_id, 35 | "revision": self.revision, 36 | "name": self.name, 37 | "currency": self.currency, 38 | } 39 | ) 40 | -------------------------------------------------------------------------------- /zou/app/models/chat_message.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | 8 | class ChatMessage(db.Model, BaseMixin, SerializerMixin): 9 | """ 10 | Message shared in the entity chat feeds. 11 | """ 12 | 13 | chat_id = db.Column( 14 | UUIDType(binary=False), 15 | db.ForeignKey("chat.id"), 16 | nullable=False, 17 | index=True, 18 | ) 19 | person_id = db.Column( 20 | UUIDType(binary=False), 21 | db.ForeignKey("person.id"), 22 | nullable=False, 23 | index=True, 24 | ) 25 | text = db.Column(db.Text()) 26 | attachment_files = db.relationship( 27 | "AttachmentFile", backref="chat_message", lazy="joined" 28 | ) 29 | 30 | # TODO 31 | # * mentions 32 | # * reactions 33 | # * concept links ? 34 | # * task links ? 35 | 36 | def __repr__(self): 37 | return "" % self.object_id 38 | -------------------------------------------------------------------------------- /zou/app/models/custom_action.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class CustomAction(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Custom actions are HTTP links that can be activated outside of the API. 9 | They are mainly aimed at being used by the web frontend to allow studio 10 | to make custom HTTP calls. 11 | """ 12 | 13 | name = db.Column(db.String(80), nullable=False) 14 | url = db.Column(db.String(400)) 15 | entity_type = db.Column(db.String(40), default="all") 16 | is_ajax = db.Column(db.Boolean(), default=False) 17 | -------------------------------------------------------------------------------- /zou/app/models/data_import_error.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.dialects.postgresql import JSONB 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | 8 | class DataImportError(db.Model, BaseMixin, SerializerMixin): 9 | """ 10 | Table to allow the storage of import errors. 11 | """ 12 | 13 | event_data = db.Column(JSONB, nullable=False) 14 | source = db.Column(db.Enum("csv", "shotgun", name="import_source_enum")) 15 | -------------------------------------------------------------------------------- /zou/app/models/day_off.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | from zou.app import db 3 | from zou.app.models.serializer import SerializerMixin 4 | from zou.app.models.base import BaseMixin 5 | 6 | 7 | class DayOff(db.Model, BaseMixin, SerializerMixin): 8 | """ 9 | Tells that someone will have a day off this day. 10 | """ 11 | 12 | date = db.Column(db.Date, nullable=False) 13 | end_date = db.Column(db.Date, nullable=False) 14 | description = db.Column(db.Text) 15 | person_id = db.Column( 16 | UUIDType(binary=False), db.ForeignKey("person.id"), index=True 17 | ) 18 | __table_args__ = ( 19 | db.UniqueConstraint("person_id", "date", name="day_off_uc"), 20 | db.CheckConstraint("date <= end_date", name="day_off_date_check"), 21 | ) 22 | -------------------------------------------------------------------------------- /zou/app/models/department.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class Department(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Studio department like modeling, animation, etc. 9 | """ 10 | 11 | name = db.Column(db.String(80), unique=True, nullable=False) 12 | color = db.Column(db.String(7), nullable=False) 13 | archived = db.Column(db.Boolean(), default=False) 14 | -------------------------------------------------------------------------------- /zou/app/models/desktop_login_log.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | 8 | class DesktopLoginLog(db.Model, BaseMixin, SerializerMixin): 9 | """ 10 | Table to log all desktop session logins. The aim is to build report that 11 | helps validating presence form. 12 | """ 13 | 14 | person_id = db.Column( 15 | UUIDType(binary=False), 16 | db.ForeignKey("person.id"), 17 | nullable=False, 18 | index=True, 19 | ) 20 | date = db.Column(db.DateTime, nullable=False) 21 | -------------------------------------------------------------------------------- /zou/app/models/event.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | from sqlalchemy.dialects.postgresql import JSONB 8 | 9 | 10 | class ApiEvent(db.Model, BaseMixin, SerializerMixin): 11 | """ 12 | Represent notable events occuring on database (asset creation, 13 | task assignation, etc.). 14 | """ 15 | 16 | name = db.Column(db.String(80), nullable=False, index=True) 17 | user_id = db.Column( 18 | UUIDType(binary=False), db.ForeignKey("person.id"), index=True 19 | ) 20 | project_id = db.Column( 21 | UUIDType(binary=False), db.ForeignKey("project.id"), index=True 22 | ) 23 | data = db.Column(JSONB) 24 | -------------------------------------------------------------------------------- /zou/app/models/file_status.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class FileStatus(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Describe the state of a given file. 9 | """ 10 | 11 | name = db.Column(db.String(40), unique=True, nullable=False) 12 | color = db.Column(db.String(7), nullable=False) 13 | -------------------------------------------------------------------------------- /zou/app/models/login_log.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType, IPAddressType, ChoiceType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | ORIGINS = [("web", "Web"), ("script", "Script")] 8 | 9 | 10 | class LoginLog(db.Model, BaseMixin, SerializerMixin): 11 | """ 12 | Table to log all web session logins. The aim is to build a table that 13 | helps finding suspicious behaviours. 14 | """ 15 | 16 | person_id = db.Column( 17 | UUIDType(binary=False), 18 | db.ForeignKey("person.id"), 19 | nullable=False, 20 | index=True, 21 | ) 22 | ip_address = db.Column(IPAddressType) 23 | origin = db.Column(ChoiceType(ORIGINS)) 24 | -------------------------------------------------------------------------------- /zou/app/models/milestone.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | from zou.app.utils import fields 7 | 8 | 9 | class Milestone(db.Model, BaseMixin, SerializerMixin): 10 | """ 11 | Allow to set a milestone date to the production schedule. 12 | """ 13 | 14 | date = db.Column(db.Date()) 15 | name = db.Column(db.String(40), nullable=False) 16 | 17 | project_id = db.Column( 18 | UUIDType(binary=False), db.ForeignKey("project.id"), index=True 19 | ) 20 | task_type_id = db.Column( 21 | UUIDType(binary=False), db.ForeignKey("task_type.id"), index=True 22 | ) 23 | 24 | def present(self): 25 | return fields.serialize_dict( 26 | { 27 | "id": self.id, 28 | "date": self.date, 29 | "name": self.name, 30 | "project_id": self.project_id, 31 | "task_type_id": self.task_type_id, 32 | } 33 | ) 34 | -------------------------------------------------------------------------------- /zou/app/models/output_type.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class OutputType(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Type of an output files (geometry, cache, etc.) 9 | """ 10 | 11 | name = db.Column(db.String(40), unique=True, nullable=False, index=True) 12 | short_name = db.Column(db.String(20), nullable=False) 13 | -------------------------------------------------------------------------------- /zou/app/models/plugin.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | from sqlalchemy_utils import EmailType, URLType 5 | 6 | 7 | class Plugin(db.Model, BaseMixin, SerializerMixin): 8 | """ 9 | Describe a plugin. 10 | """ 11 | 12 | plugin_id = db.Column( 13 | db.String(80), unique=True, nullable=False, index=True 14 | ) 15 | name = db.Column(db.String(80), nullable=False, index=True) 16 | description = db.Column(db.Text()) 17 | version = db.Column(db.String(50), nullable=False) 18 | maintainer_name = db.Column(db.String(200), nullable=False) 19 | maintainer_email = db.Column(EmailType) 20 | website = db.Column(URLType) 21 | license = db.Column(db.String(80), nullable=False) 22 | revision = db.Column(db.String(12), nullable=True) 23 | -------------------------------------------------------------------------------- /zou/app/models/preview_background_file.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class PreviewBackgroundFile(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Describe a preview background file. 9 | """ 10 | 11 | name = db.Column(db.String(40), nullable=False) 12 | archived = db.Column(db.Boolean(), default=False) 13 | is_default = db.Column(db.Boolean(), default=False, index=True) 14 | original_name = db.Column(db.String(250)) 15 | extension = db.Column(db.String(6)) 16 | file_size = db.Column(db.BigInteger(), default=0) 17 | -------------------------------------------------------------------------------- /zou/app/models/project_status.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class ProjectStatus(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Describes the state of the project (mainly open or closed). 9 | """ 10 | 11 | name = db.Column(db.String(20), unique=True, nullable=False, index=True) 12 | color = db.Column(db.String(7), nullable=False) 13 | -------------------------------------------------------------------------------- /zou/app/models/salary_scale.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | from sqlalchemy_utils import ChoiceType 3 | 4 | from zou.app.models.person import POSITION_TYPES, SENIORITY_TYPES 5 | 6 | from zou.app import db 7 | from zou.app.models.serializer import SerializerMixin 8 | from zou.app.models.base import BaseMixin 9 | 10 | 11 | class SalaryScale(db.Model, BaseMixin, SerializerMixin): 12 | """ 13 | Model to represent a salary scale tied to a department. 14 | """ 15 | 16 | department_id = db.Column( 17 | UUIDType(binary=False), 18 | db.ForeignKey("department.id"), 19 | index=True, 20 | nullable=False, 21 | ) 22 | 23 | position = db.Column(ChoiceType(POSITION_TYPES), default="artist") 24 | seniority = db.Column(ChoiceType(SENIORITY_TYPES), default="mid") 25 | salary = db.Column(db.Integer, nullable=False, default=0) 26 | 27 | def present(self): 28 | return self.serialize() 29 | -------------------------------------------------------------------------------- /zou/app/models/search_filter_group.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | from sqlalchemy.sql import expression 7 | 8 | 9 | class SearchFilterGroup(db.Model, BaseMixin, SerializerMixin): 10 | """ 11 | Groups are used to store search filters into sections. 12 | """ 13 | 14 | list_type = db.Column(db.String(80), nullable=False, index=True) 15 | entity_type = db.Column(db.String(80)) 16 | name = db.Column(db.String(200), nullable=False, default="") 17 | color = db.Column(db.String(8), nullable=False, default="") 18 | is_shared = db.Column( 19 | db.Boolean, 20 | server_default=expression.false(), 21 | default=False, 22 | nullable=False, 23 | ) 24 | 25 | person_id = db.Column(UUIDType(binary=False), db.ForeignKey("person.id")) 26 | project_id = db.Column(UUIDType(binary=False), db.ForeignKey("project.id")) 27 | department_id = db.Column( 28 | UUIDType(binary=False), db.ForeignKey("department.id") 29 | ) 30 | -------------------------------------------------------------------------------- /zou/app/models/software.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | from sqlalchemy.dialects.postgresql import JSONB 6 | 7 | 8 | class Software(db.Model, BaseMixin, SerializerMixin): 9 | """ 10 | Describes software used by working files. 11 | """ 12 | 13 | name = db.Column(db.String(40), unique=True, nullable=False) 14 | short_name = db.Column(db.String(20), nullable=False) 15 | file_extension = db.Column(db.String(20), nullable=False) 16 | secondary_extensions = db.Column(JSONB) 17 | -------------------------------------------------------------------------------- /zou/app/models/studio.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | 5 | 6 | class Studio(db.Model, BaseMixin, SerializerMixin): 7 | """ 8 | Describe a studio. 9 | """ 10 | 11 | name = db.Column(db.String(80), unique=True, nullable=False) 12 | color = db.Column(db.String(7), nullable=False) 13 | archived = db.Column(db.Boolean(), default=False) 14 | -------------------------------------------------------------------------------- /zou/app/models/subscription.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | 8 | class Subscription(db.Model, BaseMixin, SerializerMixin): 9 | """ 10 | Allow to subscribe to an entity 11 | """ 12 | 13 | person_id = db.Column( 14 | UUIDType(binary=False), 15 | db.ForeignKey("person.id"), 16 | nullable=False, 17 | index=True, 18 | ) 19 | task_id = db.Column( 20 | UUIDType(binary=False), db.ForeignKey("task.id"), index=True 21 | ) 22 | 23 | entity_id = db.Column( 24 | UUIDType(binary=False), db.ForeignKey("entity.id"), index=True 25 | ) # Deprecated 26 | task_type_id = db.Column( 27 | UUIDType(binary=False), db.ForeignKey("task_type.id"), index=True 28 | ) # Deprecated 29 | 30 | __table_args__ = ( 31 | db.UniqueConstraint( 32 | "person_id", "task_id", name="subscription_task_uc" 33 | ), 34 | db.UniqueConstraint( 35 | "person_id", 36 | "task_type_id", 37 | "entity_id", 38 | name="subscription_entity_uc", 39 | ), 40 | ) 41 | -------------------------------------------------------------------------------- /zou/app/models/task_status.py: -------------------------------------------------------------------------------- 1 | from zou.app import db 2 | from zou.app.models.serializer import SerializerMixin 3 | from zou.app.models.base import BaseMixin 4 | from sqlalchemy.sql import expression 5 | 6 | 7 | class TaskStatus(db.Model, BaseMixin, SerializerMixin): 8 | """ 9 | Describe the state of a task. A status marked as reviewable expects a 10 | preview file linked to relate comment. 11 | """ 12 | 13 | name = db.Column(db.String(40), nullable=False) 14 | archived = db.Column(db.Boolean(), default=False) 15 | short_name = db.Column( 16 | db.String(10), unique=True, nullable=False, index=True 17 | ) 18 | description = db.Column(db.Text()) 19 | color = db.Column(db.String(7), nullable=False) 20 | priority = db.Column(db.Integer, default=1) 21 | 22 | is_done = db.Column(db.Boolean(), default=False, index=True) 23 | is_artist_allowed = db.Column(db.Boolean(), default=True) 24 | is_client_allowed = db.Column(db.Boolean(), default=True) 25 | is_retake = db.Column(db.Boolean(), default=False) 26 | is_feedback_request = db.Column(db.Boolean(), default=False, index=True) 27 | is_default = db.Column(db.Boolean(), default=False, index=True) 28 | shotgun_id = db.Column(db.Integer) 29 | 30 | for_concept = db.Column( 31 | db.Boolean(), server_default=expression.false(), default=False 32 | ) 33 | -------------------------------------------------------------------------------- /zou/app/models/task_type.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | 3 | from zou.app import db 4 | from zou.app.models.serializer import SerializerMixin 5 | from zou.app.models.base import BaseMixin 6 | 7 | 8 | class TaskType(db.Model, BaseMixin, SerializerMixin): 9 | """ 10 | Categorize tasks in domain areas: modeling, animation, etc. 11 | """ 12 | 13 | name = db.Column(db.String(40), nullable=False) 14 | short_name = db.Column(db.String(20)) 15 | description = db.Column(db.Text()) 16 | color = db.Column(db.String(7), default="#FFFFFF") 17 | priority = db.Column(db.Integer, default=1) 18 | for_entity = db.Column(db.String(30), default="Asset") 19 | allow_timelog = db.Column(db.Boolean, default=True) 20 | archived = db.Column(db.Boolean(), default=False) 21 | shotgun_id = db.Column(db.Integer, index=True) 22 | 23 | department_id = db.Column( 24 | UUIDType(binary=False), db.ForeignKey("department.id"), index=True 25 | ) 26 | 27 | __table_args__ = ( 28 | db.UniqueConstraint( 29 | "name", "for_entity", "department_id", name="task_type_uc" 30 | ), 31 | ) 32 | -------------------------------------------------------------------------------- /zou/app/models/time_spent.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | from zou.app import db 3 | from zou.app.models.serializer import SerializerMixin 4 | from zou.app.models.base import BaseMixin 5 | 6 | 7 | class TimeSpent(db.Model, BaseMixin, SerializerMixin): 8 | """ 9 | Describes the time spent by someone on a task. 10 | """ 11 | 12 | duration = db.Column(db.Float, nullable=False) 13 | date = db.Column(db.Date, nullable=False) 14 | 15 | task_id = db.Column( 16 | UUIDType(binary=False), db.ForeignKey("task.id"), index=True 17 | ) 18 | person_id = db.Column( 19 | UUIDType(binary=False), db.ForeignKey("person.id"), index=True 20 | ) 21 | 22 | __table_args__ = ( 23 | db.UniqueConstraint( 24 | "person_id", "task_id", "date", name="time_spent_uc" 25 | ), 26 | db.CheckConstraint("duration > 0", name="check_duration_positive"), 27 | ) 28 | -------------------------------------------------------------------------------- /zou/app/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/services/__init__.py -------------------------------------------------------------------------------- /zou/app/services/custom_actions_service.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.custom_action import CustomAction 2 | from zou.app.utils import cache, fields 3 | 4 | 5 | def clear_custom_action_cache(): 6 | cache.cache.delete_memoized(get_custom_actions) 7 | 8 | 9 | @cache.memoize_function(120) 10 | def get_custom_actions(): 11 | return fields.serialize_models(CustomAction.get_all()) 12 | -------------------------------------------------------------------------------- /zou/app/services/scenes_service.py: -------------------------------------------------------------------------------- 1 | from zou.app.services import shots_service 2 | from zou.app.utils import events 3 | 4 | 5 | def add_shot_to_scene(scene, shot): 6 | """ 7 | Link a shot to a scene. Once done, the scene is considered as the source 8 | of the scene. 9 | """ 10 | shot = shots_service.update_shot(shot["id"], {"source_id": scene["id"]}) 11 | events.emit( 12 | "shot:add-to-scene", 13 | {"scene_id": scene["id"], "shot_id": shot["id"]}, 14 | project_id=shot["project_id"], 15 | ) 16 | return shot 17 | 18 | 19 | def remove_shot_from_scene(scene, shot): 20 | """ 21 | Remove link from a shot to a scene. 22 | """ 23 | shot = shots_service.update_shot(shot["id"], {"source_id": None}) 24 | events.emit( 25 | "shot:remove-from-scene", 26 | {"scene_id": scene["id"], "shot_id": shot["id"]}, 27 | project_id=shot["project_id"], 28 | ) 29 | return shot 30 | 31 | 32 | def get_shots_by_scene(scene_id): 33 | """ 34 | Get shots linked to a scene (of which the source is the given scene). 35 | """ 36 | return shots_service.get_shots({"source_id": scene_id}) 37 | -------------------------------------------------------------------------------- /zou/app/services/status_automations_service.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.status_automation import StatusAutomation 2 | from zou.app.utils import cache, fields 3 | from zou.app.services import base_service 4 | from zou.app.services.exception import StatusAutomationNotFoundException 5 | 6 | 7 | def clear_status_automation_cache(): 8 | cache.cache.delete_memoized(get_status_automations) 9 | 10 | 11 | @cache.memoize_function(120) 12 | def get_status_automations(): 13 | return fields.serialize_models(StatusAutomation.get_all()) 14 | 15 | 16 | def get_status_automation_raw(status_automation_id): 17 | """ 18 | Get status automation matching given id as an active record. 19 | """ 20 | return base_service.get_instance( 21 | StatusAutomation, 22 | status_automation_id, 23 | StatusAutomationNotFoundException, 24 | ) 25 | -------------------------------------------------------------------------------- /zou/app/services/telemetry_services.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import requests 3 | 4 | from zou import __version__ 5 | 6 | from zou.app.models.person import Person 7 | 8 | from zou.app.services import stats_service, persons_service 9 | 10 | from zou.app import config 11 | 12 | 13 | def send_main_infos(): 14 | """ 15 | Send main usage informations to the CGWire website. 16 | 17 | These infos are used to estimate the size of the Kitsu user community. 18 | """ 19 | 20 | organisation = persons_service.get_organisation() 21 | stats = stats_service.get_main_stats() 22 | nb_active_users = Person.query.filter_by(active=True).count() 23 | 24 | data = { 25 | "organisation_id": organisation["id"], 26 | "nb_active_users": nb_active_users, 27 | "nb_movie_previews": stats["number_of_video_previews"], 28 | "nb_picture_previews": stats["number_of_picture_previews"], 29 | "nb_model_previews": stats["number_of_model_previews"], 30 | "nb_comments": stats["number_of_comments"], 31 | "api_version": __version__, 32 | "python_version": platform.python_version(), 33 | } 34 | 35 | requests.post(config.TELEMETRY_URL, json=data) 36 | -------------------------------------------------------------------------------- /zou/app/stores/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/stores/__init__.py -------------------------------------------------------------------------------- /zou/app/stores/publisher_store.py: -------------------------------------------------------------------------------- 1 | import redis 2 | 3 | from flask_socketio import SocketIO 4 | 5 | from zou.app import config 6 | from zou.app.utils.redis import get_redis_url 7 | 8 | socketio = None 9 | 10 | 11 | def publish(event, data): 12 | if socketio is not None: 13 | socketio.emit(event, data, namespace="/events") 14 | 15 | 16 | def init(): 17 | """ 18 | Initialize key value store that will be used for the event publishing. 19 | That way the main API takes advantage of Redis pub/sub capabilities to push 20 | events to the event stream API. 21 | """ 22 | global socketio 23 | 24 | try: 25 | publisher_store = redis.StrictRedis( 26 | host=config.KEY_VALUE_STORE["host"], 27 | port=config.KEY_VALUE_STORE["port"], 28 | db=config.KV_EVENTS_DB_INDEX, 29 | password=config.KEY_VALUE_STORE["password"], 30 | decode_responses=True, 31 | ) 32 | publisher_store.get("test") 33 | socketio = SocketIO( 34 | message_queue=get_redis_url(config.KV_EVENTS_DB_INDEX), 35 | cors_allowed_origins=[], 36 | cors_credentials=False, 37 | ) 38 | except redis.ConnectionError: 39 | pass 40 | 41 | return socketio 42 | -------------------------------------------------------------------------------- /zou/app/stores/queue_store.py: -------------------------------------------------------------------------------- 1 | import redis 2 | import sys 3 | 4 | from rq import Queue 5 | from zou.app import config 6 | 7 | 8 | try: 9 | if config.ENABLE_JOB_QUEUE: 10 | queue_store = redis.StrictRedis( 11 | host=config.KEY_VALUE_STORE["host"], 12 | port=config.KEY_VALUE_STORE["port"], 13 | db=config.KV_JOB_DB_INDEX, 14 | password=config.KEY_VALUE_STORE["password"], 15 | decode_responses=True, 16 | ) 17 | queue_store.get("test") 18 | except redis.ConnectionError: 19 | try: 20 | import fakeredis 21 | 22 | revoked_tokens_store = fakeredis.FakeStrictRedis() 23 | except BaseException: 24 | sys.exit(1) 25 | 26 | if config.ENABLE_JOB_QUEUE: 27 | job_queue = Queue(connection=queue_store) 28 | -------------------------------------------------------------------------------- /zou/app/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/app/utils/__init__.py -------------------------------------------------------------------------------- /zou/app/utils/api.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Api 2 | from zou.app.utils.flask import output_json 3 | 4 | 5 | def configure_api_from_blueprint(blueprint, route_tuples, decorators=None): 6 | """ 7 | Creates a Flask Restful api object based on information from given 8 | blueprint. API is configured to return JSON objects. 9 | 10 | Each blueprint is describe by a list of tuple. Each tuple is composed of a 11 | route and the related resource (controller). 12 | """ 13 | 14 | api = Api(blueprint, catch_all_404s=True, decorators=decorators) 15 | 16 | api.representations = { 17 | "application/json": output_json, 18 | } 19 | 20 | for route_tuple in route_tuples: 21 | (path, resource) = route_tuple 22 | api.add_resource(resource, path) 23 | 24 | return api 25 | -------------------------------------------------------------------------------- /zou/app/utils/auth.py: -------------------------------------------------------------------------------- 1 | import flask_bcrypt 2 | import email_validator 3 | 4 | from zou.app import config 5 | 6 | 7 | class PasswordTooShortException(BaseException): 8 | pass 9 | 10 | 11 | class PasswordsNoMatchException(BaseException): 12 | pass 13 | 14 | 15 | class EmailNotValidException(BaseException): 16 | pass 17 | 18 | 19 | def encrypt_password(password): 20 | """ 21 | Encrypt given string password using bcrypt algorithm. 22 | """ 23 | return flask_bcrypt.generate_password_hash(password) 24 | 25 | 26 | def validate_email( 27 | email, check_deliverability=config.MAIL_CHECK_DELIVERABILITY 28 | ): 29 | try: 30 | return email_validator.validate_email( 31 | email, check_deliverability=check_deliverability 32 | ).normalized 33 | except email_validator.EmailNotValidError as e: 34 | raise EmailNotValidException(str(e)) 35 | 36 | 37 | def validate_password(password, password_2=None): 38 | if len(password) < config.MIN_PASSWORD_LENGTH: 39 | raise PasswordTooShortException() 40 | if password_2 is not None and password != password_2: 41 | raise PasswordsNoMatchException() 42 | return True 43 | -------------------------------------------------------------------------------- /zou/app/utils/colors.py: -------------------------------------------------------------------------------- 1 | def rgb_to_hex(color): 2 | """ 3 | Return color as #rrggbb for the given color values. 4 | """ 5 | [red, green, blue] = color.split(",") 6 | return "#%02x%02x%02x" % (int(red), int(green), int(blue)) 7 | -------------------------------------------------------------------------------- /zou/app/utils/dbhelpers.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, inspect 2 | from sqlalchemy_utils import database_exists, create_database 3 | from sqlalchemy.engine.url import URL 4 | from sqlalchemy.orm import close_all_sessions 5 | 6 | 7 | def get_db_uri(): 8 | from zou.app.config import DATABASE 9 | 10 | return URL.create(**DATABASE).render_as_string(hide_password=False) 11 | 12 | 13 | def reset_all(): 14 | """ 15 | Check that database exist. 16 | """ 17 | drop_all() 18 | return create_all() 19 | 20 | 21 | def create_all(): 22 | """ 23 | Create all database tables. 24 | """ 25 | from zou.app import db, config 26 | 27 | engine = create_engine(config.SQLALCHEMY_DATABASE_URI) 28 | if not database_exists(engine.url): 29 | create_database(engine.url) 30 | return db.create_all() 31 | 32 | 33 | def drop_all(): 34 | """ 35 | Drop all database tables. 36 | """ 37 | from zou.app import db 38 | 39 | db.session.flush() 40 | close_all_sessions() 41 | return db.drop_all() 42 | 43 | 44 | def is_init(): 45 | """ 46 | Check if database is initialized. 47 | """ 48 | from zou.app import db 49 | from zou.app.models.project_status import ProjectStatus 50 | 51 | return ( 52 | inspect(db.engine).has_table("person") 53 | and db.session.query(ProjectStatus).count() == 2 54 | ) 55 | -------------------------------------------------------------------------------- /zou/app/utils/env.py: -------------------------------------------------------------------------------- 1 | import os 2 | from zou.app.utils.string import strtobool 3 | 4 | 5 | def envtobool(key, default=False): 6 | """ 7 | Convert an environment variable to a boolean value. 8 | If environment variable can't be converted raise ValueError. 9 | """ 10 | try: 11 | return strtobool(os.getenv(key, default)) 12 | except ValueError: 13 | raise ValueError( 14 | f"Environment variable {key} cannot be converted to a boolean value." 15 | ) 16 | 17 | 18 | def env_with_semicolon_to_list(key, default=[]): 19 | """ 20 | Convert an environment variable to a list. 21 | Items are separated by semicolon. 22 | """ 23 | env_value = os.getenv(key, None) 24 | if env_value is None: 25 | return default 26 | else: 27 | return env_value.split(";") 28 | -------------------------------------------------------------------------------- /zou/app/utils/fido.py: -------------------------------------------------------------------------------- 1 | from fido2.webauthn import ( 2 | PublicKeyCredentialRpEntity, 3 | ) 4 | from fido2.server import Fido2Server 5 | from urllib.parse import urlparse 6 | 7 | from zou.app import config 8 | 9 | 10 | def get_fido_server(): 11 | return Fido2Server( 12 | PublicKeyCredentialRpEntity( 13 | name="Kitsu", id=urlparse(f"https://{config.DOMAIN_NAME}").hostname 14 | ), 15 | verify_origin=( 16 | None if config.DOMAIN_NAME != "localhost:8080" else lambda a: True 17 | ), 18 | ) 19 | -------------------------------------------------------------------------------- /zou/app/utils/git.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def get_git_revision_hash(): 5 | return subprocess.check_output(["git", "rev-parse", "HEAD"]) 6 | -------------------------------------------------------------------------------- /zou/app/utils/redis.py: -------------------------------------------------------------------------------- 1 | from zou.app import config 2 | 3 | 4 | def get_redis_url(db_index): 5 | redis_host = config.KEY_VALUE_STORE["host"] 6 | redis_port = config.KEY_VALUE_STORE["port"] 7 | if config.KEY_VALUE_STORE["password"]: 8 | redis_password = f":{config.KEY_VALUE_STORE['password']}@" 9 | else: 10 | redis_password = "" 11 | return f"redis://{redis_password}{redis_host}:{redis_port}/{db_index}" 12 | -------------------------------------------------------------------------------- /zou/app/utils/shell.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | class ShellCommandFailed(Exception): 5 | pass 6 | 7 | 8 | def run_command(args): 9 | try: 10 | sp = subprocess.Popen( 11 | args, stdout=subprocess.PIPE, stderr=subprocess.PIPE 12 | ) 13 | out, err = sp.communicate() 14 | if err: 15 | raise ShellCommandFailed(err) 16 | else: 17 | return out 18 | except Exception as e: 19 | raise ShellCommandFailed(str(e)) 20 | -------------------------------------------------------------------------------- /zou/app/utils/string.py: -------------------------------------------------------------------------------- 1 | def strtobool(val): 2 | """ 3 | Convert a string (val) to a boolean value. 4 | If val is already a boolean return val. 5 | Else raise ValueError. 6 | """ 7 | if isinstance(val, bool): 8 | return val 9 | if isinstance(val, int): 10 | return bool(val) 11 | elif val.lower() in ("y", "yes", "t", "true", "on", "1"): 12 | return True 13 | elif val.lower() in ("n", "no", "f", "false", "off", "0"): 14 | return False 15 | else: 16 | raise ValueError 17 | -------------------------------------------------------------------------------- /zou/debug.py: -------------------------------------------------------------------------------- 1 | from gevent import monkey 2 | 3 | monkey.patch_all() 4 | 5 | from zou.app import app, config 6 | from flask_socketio import SocketIO 7 | import logging 8 | 9 | 10 | FORMAT = "%(message)s" 11 | logging.basicConfig(level=logging.INFO, format=FORMAT) 12 | socketio = SocketIO(app, cors_allowed_origins=[], cors_credentials=False) 13 | 14 | if __name__ == "__main__": 15 | print( 16 | f"The Kitsu API server is listening on http://{config.DEBUG_HOST}:{config.DEBUG_PORT}" 17 | ) 18 | socketio.run( 19 | app, host=config.DEBUG_HOST, port=config.DEBUG_PORT, debug=True 20 | ) 21 | -------------------------------------------------------------------------------- /zou/job_settings.py: -------------------------------------------------------------------------------- 1 | from zou.app import config 2 | 3 | REDIS_HOST = config.KEY_VALUE_STORE["host"] 4 | REDIS_PORT = config.KEY_VALUE_STORE["port"] 5 | REDIS_DB = config.KV_JOB_DB_INDEX 6 | REDIS_PASSWORD = config.KEY_VALUE_STORE["password"] 7 | -------------------------------------------------------------------------------- /zou/migrations/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /zou/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/migrations/__init__.py -------------------------------------------------------------------------------- /zou/migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = WARN 34 | handlers = 35 | qualname = alembic 36 | 37 | [handler_console] 38 | class = StreamHandler 39 | args = (sys.stderr,) 40 | level = NOTSET 41 | formatter = generic 42 | 43 | [formatter_generic] 44 | format = %(levelname)-5.5s [%(name)s] %(message)s 45 | datefmt = %H:%M:%S 46 | -------------------------------------------------------------------------------- /zou/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | import sqlalchemy_utils 11 | ${imports if imports else ""} 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = ${repr(up_revision)} 15 | down_revision = ${repr(down_revision)} 16 | branch_labels = ${repr(branch_labels)} 17 | depends_on = ${repr(depends_on)} 18 | 19 | 20 | def upgrade(): 21 | ${upgrades if upgrades else "pass"} 22 | 23 | 24 | def downgrade(): 25 | ${downgrades if downgrades else "pass"} 26 | -------------------------------------------------------------------------------- /zou/migrations/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/migrations/utils/__init__.py -------------------------------------------------------------------------------- /zou/migrations/utils/base.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy_utils import UUIDType 2 | from zou.app.utils import fields, date_helpers 3 | import sqlalchemy as sa 4 | 5 | 6 | class BaseMixin(object): 7 | id = sa.Column( 8 | UUIDType(binary=False), primary_key=True, default=fields.gen_uuid 9 | ) 10 | 11 | # Audit fields 12 | created_at = sa.Column( 13 | sa.DateTime, default=date_helpers.get_utc_now_datetime 14 | ) 15 | updated_at = sa.Column( 16 | sa.DateTime, 17 | default=date_helpers.get_utc_now_datetime, 18 | onupdate=date_helpers.get_utc_now_datetime, 19 | ) 20 | -------------------------------------------------------------------------------- /zou/migrations/versions/003be8a91001_add_start_and_end_dates_to_projects.py: -------------------------------------------------------------------------------- 1 | """Add start and end dates to projects 2 | 3 | Revision ID: 003be8a91001 4 | Revises: 590aa1ffe731 5 | Create Date: 2019-07-05 09:22:47.857496 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "003be8a91001" 15 | down_revision = "590aa1ffe731" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column("project", sa.Column("end_date", sa.Date(), nullable=True)) 23 | op.add_column("project", sa.Column("start_date", sa.Date(), nullable=True)) 24 | # ### end Alembic commands ### 25 | 26 | 27 | def downgrade(): 28 | # ### commands auto generated by Alembic - please adjust! ### 29 | op.drop_column("project", "start_date") 30 | op.drop_column("project", "end_date") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/1cb44194db49_add_file_size_field_to_preview_file.py: -------------------------------------------------------------------------------- 1 | """add file size field to preview file 2 | 3 | Revision ID: 1cb44194db49 4 | Revises: cf6cec6d6bf5 5 | Create Date: 2021-01-20 10:58:07.202278 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "1cb44194db49" 15 | down_revision = "cf6cec6d6bf5" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "preview_file", sa.Column("file_size", sa.Integer(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("preview_file", "file_size") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/1e150c2cea4d_add_nb_entities_out.py: -------------------------------------------------------------------------------- 1 | """Add nb entities out field to entity table 2 | 3 | Revision ID: 1e150c2cea4d 4 | Revises: 9060dd4f6116 5 | Create Date: 2021-11-22 16:39:09.420069 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "1e150c2cea4d" 15 | down_revision = "9060dd4f6116" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity", sa.Column("nb_entities_out", sa.Integer(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("entity", "nb_entities_out") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/1e2d77a2f0c4_add_hd_by_default_column.py: -------------------------------------------------------------------------------- 1 | """Add is hd_by_default column to organisation table 2 | 3 | Revision ID: 1e2d77a2f0c4 4 | Revises: 4e3738cdc34c 5 | Create Date: 2021-10-12 18:56:43.683008 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "1e2d77a2f0c4" 15 | down_revision = "4e3738cdc34c" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "organisation", sa.Column("hd_by_default", sa.Boolean(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("organisation", "hd_by_default") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/2baede80b111_add_entity_status_field.py: -------------------------------------------------------------------------------- 1 | """add entity status field 2 | 3 | Revision ID: 2baede80b111 4 | Revises: 956659992419 5 | Create Date: 2023-01-24 19:09:55.648115 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | 13 | # revision identifiers, used by Alembic. 14 | 15 | revision = "2baede80b111" 16 | down_revision = "956659992419" 17 | branch_labels = None 18 | depends_on = None 19 | 20 | ENTITY_STATUSES = [ 21 | ("standby", "Stand By"), 22 | ("running", "Running"), 23 | ("complete", "Complete"), 24 | ("canceled", "Canceled"), 25 | ] 26 | 27 | 28 | def upgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.add_column( 31 | "entity", 32 | sa.Column( 33 | "status", 34 | sqlalchemy_utils.types.choice.ChoiceType(ENTITY_STATUSES), 35 | nullable=True, 36 | default="running", 37 | ), 38 | ) 39 | # ### end Alembic commands ### 40 | 41 | 42 | def downgrade(): 43 | # ### commands auto generated by Alembic - please adjust! ### 44 | op.drop_column("entity", "status") 45 | # ### end Alembic commands ### 46 | -------------------------------------------------------------------------------- /zou/migrations/versions/328fd44c6347_add_entity_created_by.py: -------------------------------------------------------------------------------- 1 | """Add entity.created_by 2 | 3 | Revision ID: 328fd44c6347 4 | Revises: 269d41bfb73f 5 | Create Date: 2023-12-17 23:07:37.629894 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | import sqlalchemy_utils 13 | import uuid 14 | 15 | # revision identifiers, used by Alembic. 16 | revision = "328fd44c6347" 17 | down_revision = "269d41bfb73f" 18 | branch_labels = None 19 | depends_on = None 20 | 21 | 22 | def upgrade(): 23 | # ### commands auto generated by Alembic - please adjust! ### 24 | with op.batch_alter_table("entity", schema=None) as batch_op: 25 | batch_op.add_column( 26 | sa.Column( 27 | "created_by", 28 | sqlalchemy_utils.types.uuid.UUIDType(binary=False), 29 | default=uuid.uuid4, 30 | nullable=True, 31 | ) 32 | ) 33 | batch_op.create_foreign_key(None, "person", ["created_by"], ["id"]) 34 | 35 | # ### end Alembic commands ### 36 | 37 | 38 | def downgrade(): 39 | # ### commands auto generated by Alembic - please adjust! ### 40 | with op.batch_alter_table("entity", schema=None) as batch_op: 41 | batch_op.drop_constraint("entity_created_by_fkey", type_="foreignkey") 42 | batch_op.drop_column("created_by") 43 | 44 | # ### end Alembic commands ### 45 | -------------------------------------------------------------------------------- /zou/migrations/versions/32f134ff1201_add_is_shared_flag_to_filters.py: -------------------------------------------------------------------------------- 1 | """add is_shared flag to filters 2 | 3 | Revision ID: 32f134ff1201 4 | Revises: 57222395f2be 5 | Create Date: 2024-05-17 00:50:00.493167 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "32f134ff1201" 15 | down_revision = "57222395f2be" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("search_filter", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column("is_shared", sa.Boolean(), nullable=True) 25 | ) 26 | 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | with op.batch_alter_table("search_filter", schema=None) as batch_op: 33 | batch_op.drop_column("is_shared") 34 | -------------------------------------------------------------------------------- /zou/migrations/versions/346250b5304c_add_position_to_preview_files.py: -------------------------------------------------------------------------------- 1 | """add position to preview files 2 | 3 | Revision ID: 346250b5304c 4 | Revises: 82e7f7a95e84 5 | Create Date: 2020-10-26 18:34:47.936827 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "346250b5304c" 15 | down_revision = "82e7f7a95e84" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "preview_file", sa.Column("position", sa.Integer(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("preview_file", "position") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/389cfb9de776_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 389cfb9de776 4 | Revises: a65bdadbae2f 5 | Create Date: 2019-02-05 18:43:19.270283 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "389cfb9de776" 15 | down_revision = "a65bdadbae2f" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task", sa.Column("last_comment_date", sa.DateTime(), nullable=True) 24 | ) 25 | op.add_column( 26 | "task", sa.Column("retake_count", sa.Integer(), nullable=True) 27 | ) 28 | op.add_column( 29 | "task_status", sa.Column("is_retake", sa.Boolean(), nullable=True) 30 | ) 31 | # ### end Alembic commands ### 32 | 33 | 34 | def downgrade(): 35 | # ### commands auto generated by Alembic - please adjust! ### 36 | op.drop_column("task_status", "is_retake") 37 | op.drop_column("task", "retake_count") 38 | op.drop_column("task", "last_comment_date") 39 | # ### end Alembic commands ### 40 | -------------------------------------------------------------------------------- /zou/migrations/versions/38baa9a23b3d_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 38baa9a23b3d 4 | Revises: 443d1e78a932 5 | Create Date: 2018-08-14 15:48:04.225525 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "38baa9a23b3d" 15 | down_revision = "443d1e78a932" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column("task", sa.Column("priority", sa.Integer(), nullable=True)) 23 | # ### end Alembic commands ### 24 | 25 | 26 | def downgrade(): 27 | # ### commands auto generated by Alembic - please adjust! ### 28 | op.drop_column("task", "priority") 29 | # ### end Alembic commands ### 30 | -------------------------------------------------------------------------------- /zou/migrations/versions/398150912a3f_validation_status_preview.py: -------------------------------------------------------------------------------- 1 | """Add validation status column to preview files 2 | 3 | Revision ID: 398150912a3f 4 | Revises: 3b0d1321079e 5 | Create Date: 2021-11-09 00:07:04.543076 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | 13 | 14 | # revision identifiers, used by Alembic. 15 | revision = "398150912a3f" 16 | down_revision = "3b0d1321079e" 17 | branch_labels = None 18 | depends_on = None 19 | 20 | VALIDATION_STATUSES = [ 21 | ("validated", "Validated"), 22 | ("rejected", "Rejected"), 23 | ("neutral", "Neutral"), 24 | ] 25 | 26 | 27 | def upgrade(): 28 | # ### commands auto generated by Alembic - please adjust! ### 29 | op.add_column( 30 | "preview_file", 31 | sa.Column( 32 | "validation_status", 33 | sqlalchemy_utils.types.choice.ChoiceType(VALIDATION_STATUSES), 34 | nullable=True, 35 | ), 36 | ) 37 | # ### end Alembic commands ### 38 | 39 | 40 | def downgrade(): 41 | # ### commands auto generated by Alembic - please adjust! ### 42 | op.drop_column("preview_file", "validation_status") 43 | # ### end Alembic commands ### 44 | -------------------------------------------------------------------------------- /zou/migrations/versions/3b0d1321079e_.py: -------------------------------------------------------------------------------- 1 | """Add replies column 2 | 3 | Revision ID: 3b0d1321079e 4 | Revises: 1e2d77a2f0c4 5 | Create Date: 2021-11-03 23:32:15.720557 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.dialects import postgresql 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "3b0d1321079e" 15 | down_revision = "1e2d77a2f0c4" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "comment", 24 | sa.Column( 25 | "replies", postgresql.JSONB(astext_type=sa.Text()), nullable=True 26 | ), 27 | ) 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | op.drop_column("comment", "replies") 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /zou/migrations/versions/3d5c93bafb9d_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 3d5c93bafb9d 4 | Revises: 7dc79d4ed7cd 5 | Create Date: 2018-05-30 20:04:38.461178 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "3d5c93bafb9d" 15 | down_revision = "7dc79d4ed7cd" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "project", sa.Column("has_avatar", sa.Boolean(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("project", "has_avatar") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/3e0538ddf80f_.py: -------------------------------------------------------------------------------- 1 | """add columns: notifications_mattermost_{enabled,userid} 2 | 3 | Revision ID: 3e0538ddf80f 4 | Revises: a66508788c53 5 | Create Date: 2021-11-12 15:31:19.621953 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "3e0538ddf80f" 15 | down_revision = "a66508788c53" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", 24 | sa.Column( 25 | "notifications_mattermost_enabled", sa.Boolean(), nullable=True 26 | ), 27 | ) 28 | op.add_column( 29 | "person", 30 | sa.Column( 31 | "notifications_mattermost_userid", 32 | sa.String(length=60), 33 | nullable=True, 34 | ), 35 | ) 36 | # ### end Alembic commands ### 37 | 38 | 39 | def downgrade(): 40 | # ### commands auto generated by Alembic - please adjust! ### 41 | op.drop_column("person", "notifications_mattermost_userid") 42 | op.drop_column("person", "notifications_mattermost_enabled") 43 | # ### end Alembic commands ### 44 | -------------------------------------------------------------------------------- /zou/migrations/versions/3fee3bd10f9d_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 3fee3bd10f9d 4 | Revises: 45c2de366e66 5 | Create Date: 2018-10-23 16:46:02.990772 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.dialects import postgresql 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "3fee3bd10f9d" 15 | down_revision = "45c2de366e66" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "output_file", 24 | sa.Column( 25 | "data", postgresql.JSONB(astext_type=sa.Text()), nullable=True 26 | ), 27 | ) 28 | op.add_column( 29 | "working_file", 30 | sa.Column( 31 | "data", postgresql.JSONB(astext_type=sa.Text()), nullable=True 32 | ), 33 | ) 34 | # ### end Alembic commands ### 35 | 36 | 37 | def downgrade(): 38 | # ### commands auto generated by Alembic - please adjust! ### 39 | op.drop_column("working_file", "data") 40 | op.drop_column("output_file", "data") 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/4095103c7d01_add_is_clients_isolated_flag_to_projects.py: -------------------------------------------------------------------------------- 1 | """add is clients isolated flag to projects 2 | 3 | Revision ID: 4095103c7d01 4 | Revises: 05ac7e8caa21 5 | Create Date: 2022-06-22 15:09:56.585117 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "4095103c7d01" 15 | down_revision = "05ac7e8caa21" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "project", 24 | sa.Column("is_clients_isolated", sa.Boolean(), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("project", "is_clients_isolated") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/40dea9555940_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 40dea9555940 4 | Revises: 2adc020885fa 5 | Create Date: 2019-05-09 18:19:12.766386 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "40dea9555940" 14 | down_revision = "2adc020885fa" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.drop_constraint("news_preview_file_id_fkey", "news", type_="foreignkey") 22 | op.create_foreign_key( 23 | None, "news", "preview_file", ["preview_file_id"], ["id"] 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_constraint(None, "news", type_="foreignkey") 31 | op.create_foreign_key( 32 | "news_preview_file_id_fkey", 33 | "news", 34 | "task", 35 | ["preview_file_id"], 36 | ["id"], 37 | ) 38 | # ### end Alembic commands ### 39 | -------------------------------------------------------------------------------- /zou/migrations/versions/43d0cf0ed5e7_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 43d0cf0ed5e7 4 | Revises: 40dea9555940 5 | Create Date: 2019-05-15 22:36:39.136039 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "43d0cf0ed5e7" 15 | down_revision = "40dea9555940" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "preview_file", 24 | sa.Column("original_name", sa.String(length=250), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("preview_file", "original_name") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/443d1e78a932_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 443d1e78a932 4 | Revises: 59e149a772cf 5 | Create Date: 2018-08-07 21:36:03.384838 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "443d1e78a932" 15 | down_revision = "c726b98be194" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task_type", sa.Column("allow_timelog", sa.Boolean(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("task_type", "allow_timelog") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/45c2de366e66_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 45c2de366e66 4 | Revises: 925771029620 5 | Create Date: 2018-10-12 14:53:26.099406 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.dialects import postgresql 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "45c2de366e66" 15 | down_revision = "925771029620" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "preview_file", 24 | sa.Column( 25 | "annotations", 26 | postgresql.JSONB(astext_type=sa.Text()), 27 | nullable=True, 28 | ), 29 | ) 30 | # ### end Alembic commands ### 31 | 32 | 33 | def downgrade(): 34 | # ### commands auto generated by Alembic - please adjust! ### 35 | op.drop_column("preview_file", "annotations") 36 | # ### end Alembic commands ### 37 | -------------------------------------------------------------------------------- /zou/migrations/versions/4f2398ebcd49_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 4f2398ebcd49 4 | Revises: 389cfb9de776 5 | Create Date: 2019-02-15 18:22:51.684630 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "4f2398ebcd49" 14 | down_revision = "389cfb9de776" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.add_column( 22 | "search_filter", sa.Column("entity_type", sa.String(length=80)) 23 | ) 24 | # ### end Alembic commands ### 25 | 26 | 27 | def downgrade(): 28 | # ### commands auto generated by Alembic - please adjust! ### 29 | op.drop_column("search_filter", "entity_type") 30 | # ### end Alembic commands ### 31 | -------------------------------------------------------------------------------- /zou/migrations/versions/57222395f2be_add_statusautomation_import_last_revision.py: -------------------------------------------------------------------------------- 1 | """Add StatusAutomation.import_last_revision 2 | 3 | Revision ID: 57222395f2be 4 | Revises: 5b980f0dc365 5 | Create Date: 2024-05-07 01:23:21.597644 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "57222395f2be" 15 | down_revision = "5b980f0dc365" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("status_automation", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column( 25 | "import_last_revision", 26 | sa.Boolean(), 27 | nullable=True, 28 | default=False, 29 | server_default=sa.sql.expression.false(), 30 | ) 31 | ) 32 | 33 | # ### end Alembic commands ### 34 | 35 | 36 | def downgrade(): 37 | # ### commands auto generated by Alembic - please adjust! ### 38 | with op.batch_alter_table("status_automation", schema=None) as batch_op: 39 | batch_op.drop_column("import_last_revision") 40 | 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/590aa1ffe731_add_notifications_enabled_flag.py: -------------------------------------------------------------------------------- 1 | """Add notifications enabled flag 2 | 3 | Revision ID: 590aa1ffe731 4 | Revises: 54ee0d1d60ba 5 | Create Date: 2019-06-30 13:19:43.298001 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "590aa1ffe731" 15 | down_revision = "54ee0d1d60ba" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", 24 | sa.Column("notifications_enabled", sa.Boolean(), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("person", "notifications_enabled") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/59a7445a966c_add_entity_is_shared.py: -------------------------------------------------------------------------------- 1 | """Add Entity.is_shared 2 | 3 | Revision ID: 59a7445a966c 4 | Revises: ca28796a2a62 5 | Create Date: 2024-08-16 17:31:59.331365 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.sql import expression 12 | 13 | 14 | # revision identifiers, used by Alembic. 15 | revision = "59a7445a966c" 16 | down_revision = "ca28796a2a62" 17 | branch_labels = None 18 | depends_on = None 19 | 20 | 21 | def upgrade(): 22 | # ### commands auto generated by Alembic - please adjust! ### 23 | with op.batch_alter_table("entity", schema=None) as batch_op: 24 | batch_op.add_column( 25 | sa.Column( 26 | "is_shared", 27 | sa.Boolean(), 28 | nullable=False, 29 | server_default=expression.false(), 30 | ) 31 | ) 32 | 33 | # ### end Alembic commands ### 34 | 35 | 36 | def downgrade(): 37 | # ### commands auto generated by Alembic - please adjust! ### 38 | with op.batch_alter_table("entity", schema=None) as batch_op: 39 | batch_op.drop_column("is_shared") 40 | 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/5a291251823c_add_max_retake_parameter.py: -------------------------------------------------------------------------------- 1 | """add max retake parameter 2 | 3 | Revision ID: 5a291251823c 4 | Revises: 4095103c7d01 5 | Create Date: 2022-06-29 10:56:13.556495 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "5a291251823c" 15 | down_revision = "4095103c7d01" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "project", sa.Column("max_retakes", sa.Integer(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("project", "max_retakes") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/5ab9d7a75887_.py: -------------------------------------------------------------------------------- 1 | """Empty revision due to wrong revision creation 2 | 3 | Revision ID: 5ab9d7a75887 4 | Revises: d80267806131 5 | Create Date: 2022-06-06 22:33:26.331874 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = "5ab9d7a75887" 11 | down_revision = "d80267806131" 12 | branch_labels = None 13 | depends_on = None 14 | 15 | 16 | def upgrade(): 17 | pass 18 | 19 | 20 | def downgrade(): 21 | pass 22 | -------------------------------------------------------------------------------- /zou/migrations/versions/5b0fcbb94f24_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 5b0fcbb94f24 4 | Revises: 772a5e43f05b 5 | Create Date: 2019-03-14 00:53:39.326495 6 | 7 | """ 8 | 9 | from alembic import op 10 | from sqlalchemy.dialects import postgresql 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "5b0fcbb94f24" 14 | down_revision = "772a5e43f05b" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.alter_column( 22 | "notification", 23 | "comment_id", 24 | existing_type=postgresql.UUID(), 25 | nullable=True, 26 | ) 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | op.alter_column( 33 | "notification", 34 | "comment_id", 35 | existing_type=postgresql.UUID(), 36 | nullable=False, 37 | ) 38 | # ### end Alembic commands ### 39 | -------------------------------------------------------------------------------- /zou/migrations/versions/5b7fa3e51701_add_is_client_allowed_flag_to_task_.py: -------------------------------------------------------------------------------- 1 | """Add is client allowed flag to task status 2 | 3 | Revision ID: 5b7fa3e51701 4 | Revises: 6d1b2c60f58b 5 | Create Date: 2019-11-04 15:37:12.261423 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "5b7fa3e51701" 15 | down_revision = "6d1b2c60f58b" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task_status", 24 | sa.Column( 25 | "is_client_allowed", sa.Boolean(), nullable=True, default=False 26 | ), 27 | ) 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | op.drop_column("task_status", "is_client_allowed") 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /zou/migrations/versions/5b980f0dc365_add_comment_links.py: -------------------------------------------------------------------------------- 1 | """Add Comment.links 2 | 3 | Revision ID: 5b980f0dc365 4 | Revises: 1fab8c420678 5 | Create Date: 2024-05-06 16:42:35.512933 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "5b980f0dc365" 15 | down_revision = "1fab8c420678" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("comment", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column("links", sa.ARRAY(sa.String()), nullable=True) 25 | ) 26 | 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | with op.batch_alter_table("comment", schema=None) as batch_op: 33 | batch_op.drop_column("links") 34 | 35 | # ### end Alembic commands ### 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/5c0498e264bc_add_slack_fields.py: -------------------------------------------------------------------------------- 1 | """add_slack_fields 2 | 3 | Revision ID: 5c0498e264bc 4 | Revises: 9f8445f9b42c 5 | Create Date: 2019-08-04 19:37:10.284171 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "5c0498e264bc" 15 | down_revision = "9f8445f9b42c" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | op.add_column( 22 | "person", 23 | sa.Column("notifications_slack_enabled", sa.Boolean(), nullable=True), 24 | ) 25 | op.add_column( 26 | "person", 27 | sa.Column( 28 | "notifications_slack_userid", sa.String(length=60), nullable=True 29 | ), 30 | ) 31 | 32 | 33 | def downgrade(): 34 | op.drop_column("person", "notifications_slack_userid") 35 | op.drop_column("person", "notifications_slack_enabled") 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py: -------------------------------------------------------------------------------- 1 | """For SearchFilterGroup.is_shared 2 | 3 | Revision ID: 680c64565f9d 4 | Revises: f344b867a911 5 | Create Date: 2024-05-29 02:13:20.764614 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "680c64565f9d" 15 | down_revision = "f344b867a911" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("search_filter_group", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column("is_shared", sa.Boolean(), nullable=True) 25 | ) 26 | 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | with op.batch_alter_table("search_filter_group", schema=None) as batch_op: 33 | batch_op.drop_column("is_shared") 34 | 35 | # ### end Alembic commands ### 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/6aa446ee4072_add_is_for_all_flage_to_playlists.py: -------------------------------------------------------------------------------- 1 | """add is_for_all flage to playlists 2 | 3 | Revision ID: 6aa446ee4072 4 | Revises: 7417c8eb70d8 5 | Create Date: 2020-04-02 16:48:05.015267 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "6aa446ee4072" 15 | down_revision = "7417c8eb70d8" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "playlist", sa.Column("is_for_all", sa.Boolean(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("playlist", "is_for_all") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/6d7fa5a8e9a5_add_timesheets_locked_field_to_org.py: -------------------------------------------------------------------------------- 1 | """add timesheets locked field to org 2 | 3 | Revision ID: 6d7fa5a8e9a5 4 | Revises: 8e4f39e321f4 5 | Create Date: 2021-05-07 13:19:06.751710 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "6d7fa5a8e9a5" 15 | down_revision = "8e4f39e321f4" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "organisation", 24 | sa.Column("timesheets_locked", sa.Boolean(), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("organisation", "timesheets_locked") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/6eeaff945706_add_data_field_on_entity_links.py: -------------------------------------------------------------------------------- 1 | """Add data field on entity links 2 | 3 | Revision ID: 6eeaff945706 4 | Revises: addbbefa7028 5 | Create Date: 2022-04-08 16:17:32.473332 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.dialects import postgresql 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "6eeaff945706" 15 | down_revision = "addbbefa7028" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity_link", 24 | sa.Column( 25 | "data", postgresql.JSONB(astext_type=sa.Text()), nullable=True 26 | ), 27 | ) 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | op.drop_column("entity_link", "data") 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /zou/migrations/versions/6f6049877105_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 6f6049877105 4 | Revises: 5b0fcbb94f24 5 | Create Date: 2019-03-15 12:24:15.277023 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | # revision identifiers, used by Alembic. 12 | revision = "6f6049877105" 13 | down_revision = "5b0fcbb94f24" 14 | branch_labels = None 15 | depends_on = None 16 | 17 | 18 | def upgrade(): 19 | # ### commands auto generated by Alembic - please adjust! ### 20 | op.drop_constraint("notification_uc", "notification", type_="unique") 21 | op.create_unique_constraint( 22 | "notification_uc", 23 | "notification", 24 | ["person_id", "author_id", "comment_id", "type"], 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_constraint("notification_uc", "notification", type_="unique") 32 | op.create_unique_constraint( 33 | "notification_uc", 34 | "notification", 35 | ["person_id", "author_id", "comment_id"], 36 | ) 37 | # ### end Alembic commands ### 38 | -------------------------------------------------------------------------------- /zou/migrations/versions/7417c8eb70d8_add_for_entity_field_to_playlists.py: -------------------------------------------------------------------------------- 1 | """add for entity field to playlists 2 | 3 | Revision ID: 7417c8eb70d8 4 | Revises: cf3d365de164 5 | Create Date: 2020-03-15 17:49:32.365732 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "7417c8eb70d8" 15 | down_revision = "8739ae9fa28b" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "playlist", 24 | sa.Column("for_entity", sa.String(length=10), nullable=True), 25 | ) 26 | op.create_index( 27 | op.f("ix_playlist_for_entity"), 28 | "playlist", 29 | ["for_entity"], 30 | unique=False, 31 | ) 32 | # ### end Alembic commands ### 33 | 34 | 35 | def downgrade(): 36 | # ### commands auto generated by Alembic - please adjust! ### 37 | op.drop_index(op.f("ix_playlist_for_entity"), table_name="playlist") 38 | op.drop_column("playlist", "for_entity") 39 | # ### end Alembic commands ### 40 | -------------------------------------------------------------------------------- /zou/migrations/versions/77d6820f494f_add_reply_to_notif.py: -------------------------------------------------------------------------------- 1 | """Add reply_id to notifications. 2 | 3 | Revision ID: 77d6820f494f 4 | Revises: 87efceb6745b 5 | Create Date: 2021-11-14 16:33:29.498193 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | import sqlalchemy_utils 13 | import uuid 14 | 15 | # revision identifiers, used by Alembic. 16 | revision = "77d6820f494f" 17 | down_revision = "87efceb6745b" 18 | branch_labels = None 19 | depends_on = None 20 | 21 | 22 | def upgrade(): 23 | # ### commands auto generated by Alembic - please adjust! ### 24 | op.add_column( 25 | "notification", 26 | sa.Column( 27 | "reply_id", 28 | sqlalchemy_utils.types.uuid.UUIDType(binary=False), 29 | default=uuid.uuid4, 30 | nullable=True, 31 | ), 32 | ) 33 | op.create_index( 34 | op.f("ix_notification_reply_id"), 35 | "notification", 36 | ["reply_id"], 37 | unique=False, 38 | ) 39 | # ### end Alembic commands ### 40 | 41 | 42 | def downgrade(): 43 | op.drop_index(op.f("ix_notification_reply_id"), table_name="notification") 44 | op.drop_column("notification", "reply_id") 45 | # ### end Alembic commands ### 46 | -------------------------------------------------------------------------------- /zou/migrations/versions/7a16258f2fab_add_currency_field_to_budgets.py: -------------------------------------------------------------------------------- 1 | """add currency field to budgets 2 | 3 | Revision ID: 7a16258f2fab 4 | Revises: 8ab98c178903 5 | Create Date: 2025-04-21 22:27:22.703525 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "7a16258f2fab" 15 | down_revision = "8ab98c178903" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("budget", schema=None) as batch_op: 23 | batch_op.add_column(sa.Column("currency", sa.String(length=3))) 24 | # ### end Alembic commands ### 25 | 26 | 27 | def downgrade(): 28 | # ### commands auto generated by Alembic - please adjust! ### 29 | with op.batch_alter_table("task", schema=None) as batch_op: 30 | batch_op.alter_column( 31 | "difficulty", existing_type=sa.INTEGER(), nullable=True 32 | ) 33 | # ### end Alembic commands ### 34 | -------------------------------------------------------------------------------- /zou/migrations/versions/7b1f765677d8_.py: -------------------------------------------------------------------------------- 1 | """add column chat_webhook_mattermost 2 | 3 | Revision ID: 7b1f765677d8 4 | Revises: 3e0538ddf80f 5 | Create Date: 2021-11-12 15:45:40.750324 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "7b1f765677d8" 15 | down_revision = "3e0538ddf80f" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "organisation", 24 | sa.Column( 25 | "chat_webhook_mattermost", sa.String(length=80), nullable=True 26 | ), 27 | ) 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | op.drop_column("organisation", "chat_webhook_mattermost") 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /zou/migrations/versions/7bc746997e8d_add_slack_token_field_to_organisation.py: -------------------------------------------------------------------------------- 1 | """Add slack token field to organisation 2 | 3 | Revision ID: 7bc746997e8d 4 | Revises: 5c0498e264bc 5 | Create Date: 2019-08-05 17:47:01.203644 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "7bc746997e8d" 15 | down_revision = "5c0498e264bc" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "organisation", 24 | sa.Column("chat_token_slack", sa.String(length=80), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("organisation", "chat_token_slack") 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/818f7bda2528_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 818f7bda2528 4 | Revises: bf1347acdee2 5 | Create Date: 2018-04-10 22:45:19.462757 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "818f7bda2528" 15 | down_revision = "bf1347acdee2" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "preview_file", sa.Column("path", sa.String(length=400), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("preview_file", "path") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/8588f254d6b8_add_archived_fields.py: -------------------------------------------------------------------------------- 1 | """add archived fields 2 | 3 | Revision ID: 8588f254d6b8 4 | Revises: f0c6cbb61869 5 | Create Date: 2022-09-06 14:03:39.729550 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "8588f254d6b8" 15 | down_revision = "f0c6cbb61869" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column("person", sa.Column("archived", sa.Boolean(), default=False)) 23 | op.add_column( 24 | "task_status", sa.Column("archived", sa.Boolean(), default=False) 25 | ) 26 | op.add_column( 27 | "task_type", sa.Column("archived", sa.Boolean(), default=False) 28 | ) 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | op.drop_column("task_type", "archived") 35 | op.drop_column("task_status", "archived") 36 | op.drop_column("person", "archived") 37 | # ### end Alembic commands ### 38 | -------------------------------------------------------------------------------- /zou/migrations/versions/8739ae9fa28b_add_for_client_field.py: -------------------------------------------------------------------------------- 1 | """Add for client field 2 | 3 | Revision ID: 8739ae9fa28b 4 | Revises: cf3d365de164 5 | Create Date: 2020-03-11 21:28:29.145467 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "8739ae9fa28b" 15 | down_revision = "cf3d365de164" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "metadata_descriptor", 24 | sa.Column("for_client", sa.Boolean(), nullable=True), 25 | ) 26 | op.create_index( 27 | op.f("ix_metadata_descriptor_for_client"), 28 | "metadata_descriptor", 29 | ["for_client"], 30 | unique=False, 31 | ) 32 | # ### end Alembic commands ### 33 | 34 | 35 | def downgrade(): 36 | # ### commands auto generated by Alembic - please adjust! ### 37 | op.drop_index( 38 | op.f("ix_metadata_descriptor_for_client"), 39 | table_name="metadata_descriptor", 40 | ) 41 | op.drop_column("metadata_descriptor", "for_client") 42 | # ### end Alembic commands ### 43 | -------------------------------------------------------------------------------- /zou/migrations/versions/87efceb6745b_add_ready_for_to_entity.py: -------------------------------------------------------------------------------- 1 | """Add ready for column to entities 2 | 3 | Revision ID: 87efceb6745b 4 | Revises: 398150912a3f 5 | Create Date: 2021-11-09 00:13:49.912704 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | import sqlalchemy_utils 13 | import uuid 14 | 15 | # revision identifiers, used by Alembic. 16 | revision = "87efceb6745b" 17 | down_revision = "398150912a3f" 18 | branch_labels = None 19 | depends_on = None 20 | 21 | 22 | def upgrade(): 23 | # ### commands auto generated by Alembic - please adjust! ### 24 | op.add_column( 25 | "entity", 26 | sa.Column( 27 | "ready_for", 28 | sqlalchemy_utils.types.uuid.UUIDType(binary=False), 29 | default=uuid.uuid4, 30 | nullable=True, 31 | ), 32 | ) 33 | op.create_foreign_key( 34 | "fk_ready_for", "entity", "task_type", ["ready_for"], ["id"] 35 | ) 36 | # ### end Alembic commands ### 37 | 38 | 39 | def downgrade(): 40 | # ### commands auto generated by Alembic - please adjust! ### 41 | op.drop_constraint("fk_ready_for", "entity", type_="foreignkey") 42 | op.drop_column("entity", "ready_for") 43 | # ### end Alembic commands ### 44 | -------------------------------------------------------------------------------- /zou/migrations/versions/8a1b4a1b7f4a_add_totp_columns_for_person.py: -------------------------------------------------------------------------------- 1 | """Add totp columns for Person 2 | 3 | Revision ID: 8a1b4a1b7f4a 4 | Revises: a6c25eed3ea1 5 | Create Date: 2022-11-15 23:15:42.600126 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "8a1b4a1b7f4a" 15 | down_revision = "a6c25eed3ea1" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", sa.Column("totp_enabled", sa.Boolean(), nullable=True) 24 | ) 25 | op.add_column( 26 | "person", sa.Column("totp_secret", sa.String(length=32), nullable=True) 27 | ) 28 | op.add_column( 29 | "person", 30 | sa.Column( 31 | "otp_recovery_codes", 32 | sa.ARRAY(sa.LargeBinary(length=60)), 33 | nullable=True, 34 | ), 35 | ) 36 | # ### end Alembic commands ### 37 | 38 | 39 | def downgrade(): 40 | # ### commands auto generated by Alembic - please adjust! ### 41 | op.drop_column("person", "otp_recovery_codes") 42 | op.drop_column("person", "totp_secret") 43 | op.drop_column("person", "totp_enabled") 44 | # ### end Alembic commands ### 45 | -------------------------------------------------------------------------------- /zou/migrations/versions/9060dd4f6116_notification_uc.py: -------------------------------------------------------------------------------- 1 | """change notification unique constraint 2 | 3 | Revision ID: 9060dd4f6116 4 | Revises: 77d6820f494f 5 | Create Date: 2021-11-14 21:20:23.848173 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "9060dd4f6116" 14 | down_revision = "77d6820f494f" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.drop_constraint("notification_uc", "notification", type_="unique") 22 | op.create_unique_constraint( 23 | "notification_uc", 24 | "notification", 25 | ["person_id", "author_id", "comment_id", "reply_id", "type"], 26 | ) 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | op.drop_constraint("notification_uc", "notification", type_="unique") 33 | op.create_unique_constraint( 34 | "notification_uc", 35 | "notification", 36 | ["person_id", "author_id", "comment_id", "type"], 37 | ) 38 | # ### end Alembic commands ### 39 | -------------------------------------------------------------------------------- /zou/migrations/versions/925771029620_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: 925771029620 4 | Revises: fb6b6f188497 5 | Create Date: 2018-09-26 21:16:04.776712 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "925771029620" 14 | down_revision = "fb6b6f188497" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.drop_constraint("playlist_uc", "playlist", type_="unique") 22 | op.create_unique_constraint( 23 | "playlist_uc", "playlist", ["name", "project_id", "episode_id"] 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_constraint("playlist_uc", "playlist", type_="unique") 31 | op.create_unique_constraint( 32 | "playlist_uc", "playlist", ["name", "project_id"] 33 | ) 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /zou/migrations/versions/92b40d79ad3f_allow_message_attachments.py: -------------------------------------------------------------------------------- 1 | """allow attachment on messages 2 | 3 | Revision ID: 92b40d79ad3f 4 | Revises: 23122f290ca2 5 | Create Date: 2024-03-08 01:35:16.127135 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "92b40d79ad3f" 14 | down_revision = "23122f290ca2" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | with op.batch_alter_table("chat", schema=None) as batch_op: 22 | batch_op.drop_index("ix_chat_person_id") 23 | batch_op.drop_constraint("chat_person_id_fkey", type_="foreignkey") 24 | batch_op.drop_column("person_id") 25 | 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | """ 32 | with op.batch_alter_table('chat', schema=None) as batch_op: 33 | batch_op.add_column(sa.Column('person_id', sa.UUID(), autoincrement=False, nullable=False)) 34 | batch_op.create_foreign_key('chat_person_id_fkey', 'person', ['person_id'], ['id']) 35 | batch_op.create_index('ix_chat_person_id', ['person_id'], unique=False) 36 | """ 37 | 38 | # ### end Alembic commands ### 39 | -------------------------------------------------------------------------------- /zou/migrations/versions/92bdfe07e5f5_discord_integration.py: -------------------------------------------------------------------------------- 1 | """discord_integration 2 | 3 | Revision ID: 92bdfe07e5f5 4 | Revises: aa0a60033106 5 | Create Date: 2022-03-03 17:04:48.006864 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "92bdfe07e5f5" 15 | down_revision = "aa0a60033106" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "organisation", 24 | sa.Column("chat_token_discord", sa.String(length=80), nullable=True), 25 | ) 26 | op.add_column( 27 | "person", 28 | sa.Column( 29 | "notifications_discord_enabled", sa.Boolean(), nullable=True 30 | ), 31 | ) 32 | op.add_column( 33 | "person", 34 | sa.Column( 35 | "notifications_discord_userid", sa.String(length=60), nullable=True 36 | ), 37 | ) 38 | # ### end Alembic commands ### 39 | 40 | 41 | def downgrade(): 42 | # ### commands auto generated by Alembic - please adjust! ### 43 | op.drop_column("person", "notifications_discord_userid") 44 | op.drop_column("person", "notifications_discord_enabled") 45 | op.drop_column("organisation", "chat_token_discord") 46 | # ### end Alembic commands ### 47 | -------------------------------------------------------------------------------- /zou/migrations/versions/956659992419_add_columns_for_person_to_store_fido_.py: -------------------------------------------------------------------------------- 1 | """Add columns for Person to store fido devices informations 2 | 3 | Revision ID: 956659992419 4 | Revises: 42ec83db6a01 5 | Create Date: 2023-01-04 15:20:26.590297 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.dialects import postgresql 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "956659992419" 15 | down_revision = "42ec83db6a01" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", sa.Column("fido_enabled", sa.Boolean(), nullable=True) 24 | ) 25 | op.add_column( 26 | "person", 27 | sa.Column( 28 | "fido_credentials", 29 | sa.ARRAY(postgresql.JSONB(astext_type=sa.Text())), 30 | nullable=True, 31 | ), 32 | ) 33 | # ### end Alembic commands ### 34 | 35 | 36 | def downgrade(): 37 | # ### commands auto generated by Alembic - please adjust! ### 38 | op.drop_column("person", "fido_credentials") 39 | op.drop_column("person", "fido_enabled") 40 | # ### end Alembic commands ### 41 | -------------------------------------------------------------------------------- /zou/migrations/versions/96c79d31e648_add_mail_otp_columns_for_person.py: -------------------------------------------------------------------------------- 1 | """Add mail otp columns for Person 2 | 3 | Revision ID: 96c79d31e648 4 | Revises: 8a1b4a1b7f4a 5 | Create Date: 2022-11-30 05:19:44.449553 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "96c79d31e648" 15 | down_revision = "8a1b4a1b7f4a" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", sa.Column("email_otp_enabled", sa.Boolean(), nullable=True) 24 | ) 25 | op.add_column( 26 | "person", 27 | sa.Column("email_otp_secret", sa.String(length=32), nullable=True), 28 | ) 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | op.drop_column("person", "email_otp_secret") 35 | op.drop_column("person", "email_otp_enabled") 36 | # ### end Alembic commands ### 37 | -------------------------------------------------------------------------------- /zou/migrations/versions/971dbf5a0faf_add_short_name_for_asset_type_entity_.py: -------------------------------------------------------------------------------- 1 | """Add short_name for Asset Type entity_type 2 | 3 | Revision ID: 971dbf5a0faf 4 | Revises: a252a094e977 5 | Create Date: 2024-06-20 19:51:15.758780 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "971dbf5a0faf" 15 | down_revision = "a252a094e977" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity_type", 24 | sa.Column("short_name", sa.String(length=20), nullable=True), 25 | ) 26 | 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | op.drop_column("entity_type", "short_name") 33 | # ### end Alembic commands ### 34 | -------------------------------------------------------------------------------- /zou/migrations/versions/98c90621cf58_add_for_client_flag_to_playlist.py: -------------------------------------------------------------------------------- 1 | """Add for client flag to playlist 2 | 3 | Revision ID: 98c90621cf58 4 | Revises: 5b7fa3e51701 5 | Create Date: 2019-11-04 20:10:43.256743 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "98c90621cf58" 15 | down_revision = "5b7fa3e51701" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "playlist", 24 | sa.Column("for_client", sa.Boolean(), nullable=True, default=False), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("playlist", "for_client") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/9f8445f9b42c_add_man_days_fields.py: -------------------------------------------------------------------------------- 1 | """Add man days fields 2 | 3 | Revision ID: 9f8445f9b42c 4 | Revises: e29638428dfd 5 | Create Date: 2019-07-10 19:54:05.160074 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "9f8445f9b42c" 15 | down_revision = "e29638428dfd" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "project", sa.Column("man_days", sa.Integer(), nullable=True) 24 | ) 25 | op.add_column( 26 | "schedule_item", sa.Column("man_days", sa.Integer(), nullable=True) 27 | ) 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | op.drop_column("schedule_item", "man_days") 34 | op.drop_column("project", "man_days") 35 | # ### end Alembic commands ### 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/a252a094e977_add_descriptions_for_entities_tasks_and_.py: -------------------------------------------------------------------------------- 1 | """Add descriptions for entities tasks and statuses 2 | 3 | Revision ID: a252a094e977 4 | Revises: 1bb55759146f 5 | Create Date: 2024-06-20 20:37:21.885953 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "a252a094e977" 15 | down_revision = "1bb55759146f" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task_type", sa.Column("description", sa.Text(), nullable=True) 24 | ) 25 | op.add_column( 26 | "task_status", sa.Column("description", sa.Text(), nullable=True) 27 | ) 28 | op.add_column( 29 | "entity_type", sa.Column("description", sa.Text(), nullable=True) 30 | ) 31 | # ### end Alembic commands ### 32 | 33 | 34 | def downgrade(): 35 | # ### commands auto generated by Alembic - please adjust! ### 36 | op.drop_column("task_type", "description") 37 | op.drop_column("task_status", "description") 38 | op.drop_column("entity_type", "description") 39 | 40 | # ### end Alembic commands ### 41 | -------------------------------------------------------------------------------- /zou/migrations/versions/a519c710877c_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: a519c710877c 4 | Revises: 4f2398ebcd49 5 | Create Date: 2019-02-25 13:39:29.662284 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "a519c710877c" 15 | down_revision = "4f2398ebcd49" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity", sa.Column("code", sa.String(length=160), nullable=True) 24 | ) 25 | op.add_column( 26 | "project", sa.Column("code", sa.String(length=80), nullable=True) 27 | ) 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | op.drop_column("project", "code") 34 | op.drop_column("entity", "code") 35 | # ### end Alembic commands ### 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/a66508788c53_add_nb_assets_ready.py: -------------------------------------------------------------------------------- 1 | """Add nb assets ready column 2 | 3 | Revision ID: a66508788c53 4 | Revises: 1e150c2cea4d 5 | Create Date: 2021-11-23 00:07:43.717653 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "a66508788c53" 15 | down_revision = "1e150c2cea4d" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task", sa.Column("nb_assets_ready", sa.Integer(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("task", "nb_assets_ready") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/a6c25eed3ea1_add_login_failed_attemps_and_last_login_.py: -------------------------------------------------------------------------------- 1 | """add login_failed_attemps and last_login_failed column to person 2 | 3 | Revision ID: a6c25eed3ea1 4 | Revises: 82ee682204ab 5 | Create Date: 2022-10-15 02:10:18.559767 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "a6c25eed3ea1" 15 | down_revision = "82ee682204ab" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", 24 | sa.Column("login_failed_attemps", sa.Integer(), nullable=True), 25 | ) 26 | op.add_column( 27 | "person", sa.Column("last_login_failed", sa.DateTime(), nullable=True) 28 | ) 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | op.drop_column("person", "last_login_failed") 35 | op.drop_column("person", "login_failed_attemps") 36 | # ### end Alembic commands ### 37 | -------------------------------------------------------------------------------- /zou/migrations/versions/a7c43f3fbc76_add_duration_column_to_the_preview_file.py: -------------------------------------------------------------------------------- 1 | """Add duration column to the preview file 2 | 3 | Revision ID: a7c43f3fbc76 4 | Revises: 05b7dc79a416 5 | Create Date: 2023-11-14 14:44:34.569736 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "a7c43f3fbc76" 15 | down_revision = "05b7dc79a416" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("preview_file", schema=None) as batch_op: 23 | batch_op.add_column(sa.Column("duration", sa.Float(), nullable=True)) 24 | # ### end Alembic commands ### 25 | 26 | 27 | def downgrade(): 28 | # ### commands auto generated by Alembic - please adjust! ### 29 | with op.batch_alter_table("preview_file", schema=None) as batch_op: 30 | batch_op.drop_column("duration") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/aa0a60033106_feedback_request.py: -------------------------------------------------------------------------------- 1 | """Add is feedback request column to task types 2 | 3 | Revision ID: aa0a60033106 4 | Revises: 7b1f765677d8 5 | Create Date: 2022-01-31 12:39:25.332258 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "aa0a60033106" 15 | down_revision = "7b1f765677d8" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task_status", 24 | sa.Column("is_feedback_request", sa.Boolean(), nullable=True), 25 | ) 26 | op.create_index( 27 | op.f("ix_task_status_is_feedback_request"), 28 | "task_status", 29 | ["is_feedback_request"], 30 | unique=False, 31 | ) 32 | # ### end Alembic commands ### 33 | 34 | 35 | def downgrade(): 36 | # ### commands auto generated by Alembic - please adjust! ### 37 | op.drop_index( 38 | op.f("ix_task_status_is_feedback_request"), table_name="task_status" 39 | ) 40 | op.drop_column("task_status", "is_feedback_request") 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/addbad59c706_allow_to_manage_drawings_instead_of_.py: -------------------------------------------------------------------------------- 1 | """allow to manage drawings instead of frames 2 | 3 | Revision ID: addbad59c706 4 | Revises: 20a8ad264659 5 | Create Date: 2025-02-24 11:06:34.925091 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "addbad59c706" 15 | down_revision = "06552e22f9e7" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("task", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column("nb_drawings", sa.Integer(), nullable=True) 25 | ) 26 | 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | with op.batch_alter_table("task", schema=None) as batch_op: 33 | batch_op.drop_column("nb_drawings") 34 | 35 | # ### end Alembic commands ### 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/ae0127f2fc56_add_previewfile_width_and_previewfile_.py: -------------------------------------------------------------------------------- 1 | """Add PreviewFile.width and PreviewFile.height 2 | 3 | Revision ID: ae0127f2fc56 4 | Revises: f5bdca075cdc 5 | Create Date: 2023-09-13 17:22:11.318041 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "ae0127f2fc56" 15 | down_revision = "f5bdca075cdc" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("preview_file", schema=None) as batch_op: 23 | batch_op.add_column(sa.Column("width", sa.Integer(), nullable=True)) 24 | batch_op.add_column(sa.Column("height", sa.Integer(), nullable=True)) 25 | 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | with op.batch_alter_table("preview_file", schema=None) as batch_op: 32 | batch_op.drop_column("height") 33 | batch_op.drop_column("width") 34 | 35 | # ### end Alembic commands ### 36 | -------------------------------------------------------------------------------- /zou/migrations/versions/af1790868e2c_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: af1790868e2c 4 | Revises: a519c710877c 5 | Create Date: 2019-02-26 20:36:21.400109 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "af1790868e2c" 15 | down_revision = "a519c710877c" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity", sa.Column("nb_frames", sa.Integer(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("entity", "nb_frames") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/b45cb782bb9c_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: b45cb782bb9c 4 | Revises: d8dcd5196d57 5 | Create Date: 2019-08-21 16:49:56.479274 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "b45cb782bb9c" 14 | down_revision = "d8dcd5196d57" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.drop_constraint( 22 | "schedule_item_entity_id_fkey", "schedule_item", type_="foreignkey" 23 | ) 24 | op.create_foreign_key( 25 | None, "schedule_item", "entity", ["entity_id"], ["id"] 26 | ) 27 | # ### end Alembic commands ### 28 | 29 | 30 | def downgrade(): 31 | # ### commands auto generated by Alembic - please adjust! ### 32 | op.drop_constraint(None, "schedule_item", type_="foreignkey") 33 | op.create_foreign_key( 34 | "schedule_item_entity_id_fkey", 35 | "schedule_item", 36 | "task_type", 37 | ["entity_id"], 38 | ["id"], 39 | ) 40 | # ### end Alembic commands ### 41 | -------------------------------------------------------------------------------- /zou/migrations/versions/b8c0a0f9d054_drop_task_status_is_reviewable.py: -------------------------------------------------------------------------------- 1 | """drop task_status.is_reviewable 2 | 3 | Revision ID: b8c0a0f9d054 4 | Revises: 6eeaff945706 5 | Create Date: 2022-04-29 15:04:36.498586 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "b8c0a0f9d054" 15 | down_revision = "6eeaff945706" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.drop_column("task_status", "is_reviewable") 23 | # ### end Alembic commands ### 24 | 25 | 26 | def downgrade(): 27 | # ### commands auto generated by Alembic - please adjust! ### 28 | op.add_column( 29 | "task_status", 30 | sa.Column( 31 | "is_reviewable", sa.BOOLEAN(), autoincrement=False, nullable=True 32 | ), 33 | ) 34 | # ### end Alembic commands ### 35 | -------------------------------------------------------------------------------- /zou/migrations/versions/b97a71306fc8_add_is_casting_standby_column_to_entity.py: -------------------------------------------------------------------------------- 1 | """add is_casting_standby column to Entity 2 | 3 | Revision ID: b97a71306fc8 4 | Revises: 5a291251823c 5 | Create Date: 2022-07-12 14:21:29.530095 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "b97a71306fc8" 15 | down_revision = "5a291251823c" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity", sa.Column("is_casting_standby", sa.Boolean(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("entity", "is_casting_standby") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/bf1347acdee2_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: bf1347acdee2 4 | Revises: b4dd0add5f79 5 | Create Date: 2018-03-23 17:08:11.289953 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "bf1347acdee2" 15 | down_revision = "b4dd0add5f79" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task_type", 24 | sa.Column("for_entity", sa.String(length=10), nullable=True), 25 | ) 26 | op.drop_constraint("task_type_uc", "task_type", type_="unique") 27 | op.create_unique_constraint( 28 | "task_type_uc", "task_type", ["name", "for_entity", "department_id"] 29 | ) 30 | # ### end Alembic commands ### 31 | 32 | 33 | def downgrade(): 34 | # ### commands auto generated by Alembic - please adjust! ### 35 | op.drop_constraint("task_type_uc", "task_type", type_="unique") 36 | op.create_unique_constraint( 37 | "task_type_uc", "task_type", ["name", "department_id"] 38 | ) 39 | op.drop_column("task_type", "for_entity") 40 | # ### end Alembic commands ### 41 | -------------------------------------------------------------------------------- /zou/migrations/versions/c68c2a62cfac_add_mimetype_column_to_attachment.py: -------------------------------------------------------------------------------- 1 | """add mimetype column to attachment 2 | 3 | Revision ID: c68c2a62cfac 4 | Revises: 0ec3762a745d 5 | Create Date: 2020-04-12 22:34:50.597925 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "c68c2a62cfac" 15 | down_revision = "0ec3762a745d" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "attachment_file", 24 | sa.Column("mimetype", sa.String(length=255), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("attachment_file", "mimetype") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/c726b98be194_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: c726b98be194 4 | Revises: fee7c696166e 5 | Create Date: 2018-08-02 23:20:49.472287 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "c726b98be194" 15 | down_revision = "fee7c696166e" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "preview_file", 24 | sa.Column("extension", sa.String(length=6), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("preview_file", "extension") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/c81f3e83bdb5_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: c81f3e83bdb5 4 | Revises: 43d0cf0ed5e7 5 | Create Date: 2019-05-15 23:15:01.050692 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "c81f3e83bdb5" 15 | down_revision = "43d0cf0ed5e7" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "organisation", 24 | sa.Column("use_original_file_name", sa.Boolean(), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("organisation", "use_original_file_name") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/cf6cec6d6bf5_add_status_field_to_preview_file.py: -------------------------------------------------------------------------------- 1 | """add status field to preview file 2 | 3 | Revision ID: cf6cec6d6bf5 4 | Revises: ffeed4956ab1 5 | Create Date: 2021-01-18 23:16:58.046608 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | 13 | 14 | # revision identifiers, used by Alembic. 15 | revision = "cf6cec6d6bf5" 16 | down_revision = "ffeed4956ab1" 17 | branch_labels = None 18 | depends_on = None 19 | 20 | STATUSES = [ 21 | ("processing", "Processing"), 22 | ("ready", "Ready"), 23 | ("broken", "Broken"), 24 | ] 25 | 26 | 27 | def upgrade(): 28 | # ### commands auto generated by Alembic - please adjust! ### 29 | op.add_column( 30 | "preview_file", 31 | sa.Column( 32 | "status", 33 | sqlalchemy_utils.types.choice.ChoiceType(STATUSES), 34 | nullable=True, 35 | default="processing", 36 | ), 37 | ) 38 | # ### end Alembic commands ### 39 | 40 | 41 | def downgrade(): 42 | # ### commands auto generated by Alembic - please adjust! ### 43 | op.drop_column("preview_file", "status") 44 | # ### end Alembic commands ### 45 | -------------------------------------------------------------------------------- /zou/migrations/versions/d8dcd5196d57_add_casting_label.py: -------------------------------------------------------------------------------- 1 | """Add casting label 2 | 3 | Revision ID: d8dcd5196d57 4 | Revises: 7bc746997e8d 5 | Create Date: 2019-08-07 17:14:22.968405 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "d8dcd5196d57" 15 | down_revision = "7bc746997e8d" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "entity_link", sa.Column("label", sa.String(length=80), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("entity_link", "label") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/df1834485f57_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: df1834485f57 4 | Revises: 9a09467f9b2c 5 | Create Date: 2019-04-12 15:54:48.151083 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "df1834485f57" 15 | down_revision = "9a09467f9b2c" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "custom_action", sa.Column("is_ajax", sa.Boolean(), nullable=True) 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_column("custom_action", "is_ajax") 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /zou/migrations/versions/df9f8a147e80_change_file_size_to_big_integer.py: -------------------------------------------------------------------------------- 1 | """Change file_size to big integer 2 | 3 | Revision ID: df9f8a147e80 4 | Revises: 92bdfe07e5f5 5 | Create Date: 2022-03-14 18:01:49.812243 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "df9f8a147e80" 15 | down_revision = "92bdfe07e5f5" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.alter_column( 23 | "preview_file", 24 | "file_size", 25 | existing_type=sa.INTEGER(), 26 | type_=sa.BigInteger(), 27 | existing_nullable=True, 28 | ) 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | op.alter_column( 35 | "preview_file", 36 | "file_size", 37 | existing_type=sa.BigInteger(), 38 | type_=sa.INTEGER(), 39 | existing_nullable=True, 40 | ) 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/e1ef93f40d3d_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: e1ef93f40d3d 4 | Revises: de8a3de227ef 5 | Create Date: 2018-12-03 12:35:01.505576 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "e1ef93f40d3d" 15 | down_revision = "de8a3de227ef" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "task_status", 24 | sa.Column("is_artist_allowed", sa.Boolean(), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("task_status", "is_artist_allowed") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/migrations/versions/e3f6db74cc1e_.py: -------------------------------------------------------------------------------- 1 | """ 2 | 3 | Revision ID: e3f6db74cc1e 4 | Revises: df9f8a147e80 5 | Create Date: 2022-03-21 17:26:12.103387 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "e3f6db74cc1e" 15 | down_revision = "df9f8a147e80" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.alter_column( 23 | "search_filter", 24 | "search_query", 25 | existing_type=sa.VARCHAR(length=200), 26 | type_=sa.String(length=500), 27 | existing_nullable=False, 28 | ) 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | op.alter_column( 35 | "search_filter", 36 | "search_query", 37 | existing_type=sa.String(length=500), 38 | type_=sa.VARCHAR(length=200), 39 | existing_nullable=False, 40 | ) 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/e7e633bd6fa2_add_exceptions_to_budget_entries.py: -------------------------------------------------------------------------------- 1 | """add exceptions to budget entries 2 | 3 | Revision ID: e7e633bd6fa2 4 | Revises: 4aab1f84ad72 5 | Create Date: 2025-05-12 18:47:21.506153 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | from sqlalchemy.dialects import postgresql 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "e7e633bd6fa2" 15 | down_revision = "4aab1f84ad72" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("budget_entry", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column( 25 | "exceptions", 26 | postgresql.JSONB(astext_type=sa.Text()), 27 | nullable=True, 28 | ) 29 | ) 30 | # ### end Alembic commands ### 31 | 32 | 33 | def downgrade(): 34 | # ### commands auto generated by Alembic - please adjust! ### 35 | with op.batch_alter_table("budget_entry", schema=None) as batch_op: 36 | batch_op.drop_column("exceptions") 37 | # ### end Alembic commands ### 38 | -------------------------------------------------------------------------------- /zou/migrations/versions/f0567e8d0c62_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: f0567e8d0c62 4 | Revises: a23682ccc1f1 5 | Create Date: 2018-04-26 17:24:47.093547 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | import uuid 13 | 14 | # revision identifiers, used by Alembic. 15 | revision = "f0567e8d0c62" 16 | down_revision = "a23682ccc1f1" 17 | branch_labels = None 18 | depends_on = None 19 | 20 | 21 | def upgrade(): 22 | # ### commands auto generated by Alembic - please adjust! ### 23 | op.add_column( 24 | "entity", 25 | sa.Column( 26 | "source_id", 27 | sqlalchemy_utils.types.uuid.UUIDType(binary=False), 28 | default=uuid.uuid4, 29 | nullable=True, 30 | ), 31 | ) 32 | op.create_index( 33 | op.f("ix_entity_source_id"), "entity", ["source_id"], unique=False 34 | ) 35 | op.create_foreign_key(None, "entity", "entity", ["source_id"], ["id"]) 36 | # ### end Alembic commands ### 37 | 38 | 39 | def downgrade(): 40 | # ### commands auto generated by Alembic - please adjust! ### 41 | op.drop_constraint(None, "entity", type_="foreignkey") 42 | op.drop_index(op.f("ix_entity_source_id"), table_name="entity") 43 | op.drop_column("entity", "source_id") 44 | # ### end Alembic commands ### 45 | -------------------------------------------------------------------------------- /zou/migrations/versions/f4ff5a73d283_add_person_ldap_uid.py: -------------------------------------------------------------------------------- 1 | """Add Person.ldap_uid 2 | 3 | Revision ID: f4ff5a73d283 4 | Revises: 16df47d76c64 5 | Create Date: 2023-08-07 13:05:48.693043 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "f4ff5a73d283" 15 | down_revision = "16df47d76c64" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("person", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column("ldap_uid", sa.String(length=60), nullable=True) 25 | ) 26 | batch_op.create_unique_constraint(None, ["ldap_uid"]) 27 | 28 | # ### end Alembic commands ### 29 | 30 | 31 | def downgrade(): 32 | # ### commands auto generated by Alembic - please adjust! ### 33 | with op.batch_alter_table("person", schema=None) as batch_op: 34 | batch_op.drop_constraint(None, type_="unique") 35 | batch_op.drop_column("ldap_uid") 36 | 37 | # ### end Alembic commands ### 38 | -------------------------------------------------------------------------------- /zou/migrations/versions/f5b113876a49_add_preferred_two_factor_authentication_.py: -------------------------------------------------------------------------------- 1 | """Add preferred_two_factor_authentication for Person 2 | 3 | Revision ID: f5b113876a49 4 | Revises: 96c79d31e648 5 | Create Date: 2022-12-01 15:58:36.117269 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "f5b113876a49" 15 | down_revision = "96c79d31e648" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "person", 24 | sa.Column( 25 | "preferred_two_factor_authentication", 26 | sa.String(length=30), 27 | nullable=True, 28 | ), 29 | ) 30 | # ### end Alembic commands ### 31 | 32 | 33 | def downgrade(): 34 | # ### commands auto generated by Alembic - please adjust! ### 35 | op.drop_column("person", "preferred_two_factor_authentication") 36 | # ### end Alembic commands ### 37 | -------------------------------------------------------------------------------- /zou/migrations/versions/f5bdca075cdc_add_preview_download_flag_to_projects.py: -------------------------------------------------------------------------------- 1 | """add preview download flag to projects 2 | 3 | Revision ID: f5bdca075cdc 4 | Revises: f4ff5a73d283 5 | Create Date: 2023-08-29 21:18:38.791600 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "f5bdca075cdc" 15 | down_revision = "f4ff5a73d283" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | with op.batch_alter_table("project", schema=None) as batch_op: 23 | batch_op.add_column( 24 | sa.Column( 25 | "is_preview_download_allowed", sa.Boolean(), nullable=True 26 | ) 27 | ) 28 | 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | with op.batch_alter_table("project", schema=None) as batch_op: 35 | batch_op.drop_column("is_preview_download_allowed") 36 | 37 | # ### end Alembic commands ### 38 | -------------------------------------------------------------------------------- /zou/migrations/versions/f995b28fb749_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: f995b28fb749 4 | Revises: 38baa9a23b3d 5 | Create Date: 2018-09-13 13:40:05.558866 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "f995b28fb749" 15 | down_revision = "38baa9a23b3d" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "project", sa.Column("fps", sa.String(length=10), nullable=True) 24 | ) 25 | op.add_column( 26 | "project", sa.Column("ratio", sa.String(length=10), nullable=True) 27 | ) 28 | op.add_column( 29 | "project", sa.Column("resolution", sa.String(length=12), nullable=True) 30 | ) 31 | # ### end Alembic commands ### 32 | 33 | 34 | def downgrade(): 35 | # ### commands auto generated by Alembic - please adjust! ### 36 | op.drop_column("project", "resolution") 37 | op.drop_column("project", "ratio") 38 | op.drop_column("project", "fps") 39 | # ### end Alembic commands ### 40 | -------------------------------------------------------------------------------- /zou/migrations/versions/fb6b6f188497_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: fb6b6f188497 4 | Revises: fc322f908695 5 | Create Date: 2018-09-26 19:38:09.539070 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | import sqlalchemy_utils 12 | import sqlalchemy_utils 13 | import uuid 14 | 15 | # revision identifiers, used by Alembic. 16 | revision = "fb6b6f188497" 17 | down_revision = "fc322f908695" 18 | branch_labels = None 19 | depends_on = None 20 | 21 | 22 | def upgrade(): 23 | # ### commands auto generated by Alembic - please adjust! ### 24 | op.add_column( 25 | "playlist", 26 | sa.Column( 27 | "episode_id", 28 | sqlalchemy_utils.types.uuid.UUIDType(binary=False), 29 | default=uuid.uuid4, 30 | nullable=True, 31 | ), 32 | ) 33 | op.create_foreign_key(None, "playlist", "entity", ["episode_id"], ["id"]) 34 | # ### end Alembic commands ### 35 | 36 | 37 | def downgrade(): 38 | # ### commands auto generated by Alembic - please adjust! ### 39 | op.drop_constraint(None, "playlist", type_="foreignkey") 40 | op.drop_column("playlist", "episode_id") 41 | # ### end Alembic commands ### 42 | -------------------------------------------------------------------------------- /zou/migrations/versions/fb87feaaa094_add_missing_unique_constraints.py: -------------------------------------------------------------------------------- 1 | """add missing unique constraints 2 | 3 | Revision ID: fb87feaaa094 4 | Revises: 1cb44194db49 5 | Create Date: 2021-02-03 19:02:51.347109 6 | 7 | """ 8 | 9 | from alembic import op 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = "fb87feaaa094" 14 | down_revision = "1cb44194db49" 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | ### commands auto generated by Alembic - please adjust! ### 21 | op.create_unique_constraint( 22 | "entity_link_uc", "entity_link", ["entity_in_id", "entity_out_id"] 23 | ) 24 | op.create_unique_constraint( 25 | "schedule_item_uc", 26 | "schedule_item", 27 | ["project_id", "task_type_id", "object_id"], 28 | ) 29 | # ### end Alembic commands ### 30 | 31 | 32 | def downgrade(): 33 | # ### commands auto generated by Alembic - please adjust! ### 34 | op.drop_constraint("schedule_item_uc", "schedule_item", type_="unique") 35 | op.drop_constraint("entity_link_uc", "entity_link", type_="unique") 36 | # ### end Alembic commands ### 37 | -------------------------------------------------------------------------------- /zou/migrations/versions/fc322f908695_.py: -------------------------------------------------------------------------------- 1 | """empty message 2 | 3 | Revision ID: fc322f908695 4 | Revises: f995b28fb749 5 | Create Date: 2018-09-18 20:25:14.447916 6 | 7 | """ 8 | 9 | from alembic import op 10 | import sqlalchemy as sa 11 | 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = "fc322f908695" 15 | down_revision = "f995b28fb749" 16 | branch_labels = None 17 | depends_on = None 18 | 19 | 20 | def upgrade(): 21 | # ### commands auto generated by Alembic - please adjust! ### 22 | op.add_column( 23 | "project", 24 | sa.Column("production_type", sa.String(length=20), nullable=True), 25 | ) 26 | # ### end Alembic commands ### 27 | 28 | 29 | def downgrade(): 30 | # ### commands auto generated by Alembic - please adjust! ### 31 | op.drop_column("project", "production_type") 32 | # ### end Alembic commands ### 33 | -------------------------------------------------------------------------------- /zou/plugin_template/__init__.py: -------------------------------------------------------------------------------- 1 | from . import routes 2 | from flask import Blueprint 3 | from zou.app.utils.api import configure_api_from_blueprint 4 | 5 | 6 | def init_plugin(app, manifest): 7 | """ 8 | Init the plugin. 9 | """ 10 | app.logger.info("Loading plugin...") 11 | routes_tuples = [(f"/hello-world", routes.HelloWorld)] 12 | 13 | blueprint = Blueprint(manifest["id"], manifest["id"]) 14 | configure_api_from_blueprint(blueprint, routes_tuples) 15 | app.register_blueprint(blueprint, url_prefix=f"/{manifest['id']}") 16 | 17 | 18 | def pre_install(manifest): 19 | """ 20 | Pre install the plugin. 21 | """ 22 | 23 | 24 | def post_install(manifest): 25 | """ 26 | Post install the plugin. 27 | """ 28 | 29 | 30 | def pre_uninstall(manifest): 31 | """ 32 | Pre uninstall the plugin. 33 | """ 34 | 35 | 36 | def post_uninstall(manifest): 37 | """ 38 | Post uninstall the plugin. 39 | """ 40 | -------------------------------------------------------------------------------- /zou/plugin_template/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/plugin_template/logo.png -------------------------------------------------------------------------------- /zou/plugin_template/manifest.toml: -------------------------------------------------------------------------------- 1 | id = "myplugin" 2 | name = "MyPlugin" 3 | description = "My plugin description." 4 | version = "0.1.0" 5 | maintainer = "Author " 6 | website = "mywebsite.com" 7 | license = "GPL-3.0-only" 8 | -------------------------------------------------------------------------------- /zou/plugin_template/migrations/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # template used to generate migration files 5 | # file_template = %%(rev)s_%%(slug)s 6 | 7 | # set to 'true' to run the environment during 8 | # the 'revision' command, regardless of autogenerate 9 | # revision_environment = false 10 | 11 | 12 | # Logging configuration 13 | [loggers] 14 | keys = root,sqlalchemy,alembic 15 | 16 | [handlers] 17 | keys = console 18 | 19 | [formatters] 20 | keys = generic 21 | 22 | [logger_root] 23 | level = WARN 24 | handlers = console 25 | qualname = 26 | 27 | [logger_sqlalchemy] 28 | level = WARN 29 | handlers = 30 | qualname = sqlalchemy.engine 31 | 32 | [logger_alembic] 33 | level = WARN 34 | handlers = 35 | qualname = alembic 36 | 37 | [handler_console] 38 | class = StreamHandler 39 | args = (sys.stderr,) 40 | level = NOTSET 41 | formatter = generic 42 | 43 | [formatter_generic] 44 | format = %(levelname)-5.5s [%(name)s] %(message)s 45 | datefmt = %H:%M:%S 46 | -------------------------------------------------------------------------------- /zou/plugin_template/migrations/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | import sqlalchemy_utils 11 | ${imports if imports else ""} 12 | 13 | # revision identifiers, used by Alembic. 14 | revision = ${repr(up_revision)} 15 | down_revision = ${repr(down_revision)} 16 | branch_labels = ${repr(branch_labels)} 17 | depends_on = ${repr(depends_on)} 18 | 19 | 20 | def upgrade(): 21 | ${upgrades if upgrades else "pass"} 22 | 23 | 24 | def downgrade(): 25 | ${downgrades if downgrades else "pass"} 26 | -------------------------------------------------------------------------------- /zou/plugin_template/models.py: -------------------------------------------------------------------------------- 1 | from zou.app.models.serializer import SerializerMixin 2 | from zou.app.models.base import BaseMixin 3 | 4 | from sqlalchemy.orm import declarative_base 5 | from sqlalchemy import MetaData, Column, Integer 6 | 7 | plugin_metadata = MetaData() 8 | PluginBase = declarative_base(metadata=plugin_metadata) 9 | 10 | 11 | class Count(PluginBase, BaseMixin, SerializerMixin): 12 | """ 13 | Describe a plugin. 14 | """ 15 | 16 | __tablename__ = "toto_count" 17 | count = Column(Integer, nullable=False, default=0) 18 | -------------------------------------------------------------------------------- /zou/plugin_template/routes.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource 2 | from .models import Count 3 | 4 | 5 | class HelloWorld(Resource): 6 | def get(self): 7 | if not Count.query.first(): 8 | c = Count.create() 9 | else: 10 | c = Count.query.first() 11 | c.count += 1 12 | Count.commit() 13 | return {"message": "Hello, World!", "count": c.count} 14 | -------------------------------------------------------------------------------- /zou/remote/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/remote/__init__.py -------------------------------------------------------------------------------- /zou/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgwire/zou/726d7ffc0c427924feb721ab4c91b11e5c8585a5/zou/utils/__init__.py --------------------------------------------------------------------------------